Merge remote-tracking branch 'origin/feature/package-autosuggest' into scratch/split-package.el

This commit is contained in:
Philip Kaludercic 2025-09-02 14:51:09 +02:00
commit 055ee29eb4
5 changed files with 516 additions and 0 deletions

81
admin/scrape-elpa.el Normal file
View file

@ -0,0 +1,81 @@
;;; scrape-elpa.el --- Collect ELPA package suggestions -*- lexical-binding: t; -*-
;; Copyright (C) 2024 Free Software Foundation, Inc.
;; Author: Philip Kaludercic <philipk@posteo.net>
;; Keywords: tools
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; This file defines an administrative command to update the
;; `package-autosuggest' database.
;;; Code:
(defun scrape-elpa (&rest directories)
"Scrape autoload files in DIRECTORIES for package suggestions.
This file will automatically update \"package-autosuggest.eld\", but not
save it. You should invoke this command with built GNU ELPA and NonGNU
ELPA checkouts (i.e. having run \"make autoloads\" in both directories).
Please review the results before updating the autosuggest database!"
(interactive (completing-read-multiple
"ELPA directories to scrape: "
#'completion-file-name-table
#'file-directory-p))
(with-current-buffer
(find-file (expand-file-name "package-autosuggest.eld" data-directory))
(erase-buffer)
(lisp-data-mode)
(insert ";; The contents of this file are loaded into `package-autosuggest-database'
;; and were automatically generate by scraping ELPA for auto-loaded
;; code using the `scrape-elpa' command. Please avoid updating this
;; file manually!
")
(fill-paragraph)
(insert "(")
(let ((standard-output (current-buffer)))
(dolist-with-progress-reporter
(file (mapcan
(lambda (dir)
(directory-files-recursively
dir "-autoloads\\.el\\'"))
directories))
"Scraping files..."
(and-let* (((string-match "/\\([^/]+?\\)-autoloads\\.el\\'" file))
(pkg (intern (match-string 1 file)))
(inhibit-message t))
(with-temp-buffer
(insert-file-contents file)
(condition-case nil
(while t
(dolist (exp (macroexp-unprogn (read (current-buffer))))
(pcase exp
(`(add-to-list
',(and (or 'interpreter-mode-alist
'magic-mode-alist
'auto-mode-alist)
variable)
'(,(and (pred stringp) regexp) .
,(and (pred symbolp) mode)))
(terpri)
(prin1 (append (list pkg variable regexp)
(and (not (eq pkg mode)) (list mode))))))))
(end-of-file nil))))))
(insert "\n)\n")))
(provide 'scrape-elpa)
;;; scrape-elpa.el ends here

View file

@ -439,6 +439,16 @@ case, Emacs retrieves packages from this archive via ordinary file
access. Such local archives are mainly useful for testing.
@end defopt
@cindex suggestions
@findex package-autosuggest
@findex package-autosuggest-mode
Emacs has a built-in database of suggested packages for certain file
types. If Emacs opens a file with no specific mode, you can use the
@code{package-autosuggest} command to install the recommended packages
from ELPA. After enabling @code{package-autosuggest-mode}, Emacs will
display a clickable hint in the mode-line if it there is a suggested
package.
@anchor{Package Signing}
@cindex package security
@cindex package signing

View file

@ -2258,6 +2258,17 @@ version-list of a given package symbol. These functions provide public
interfaces for external tools to query information about built-in
packages.
+++
*** New command 'package-autosuggest'
Using a built-in database of package suggestions from ELPA, this command
will install viable packages if no specific major mode is available.
+++
*** New minor mode 'package-autosuggest-mode'
When enabled, this displays a hint in the mode line indicating the
availability of a suggested package. You can customise the presentation
of these hints using 'package-autosuggest-style'.
** Rcirc
+++

188
etc/package-autosuggest.eld Normal file
View file

@ -0,0 +1,188 @@
;; The contents of this file are loaded into `package-autosuggest-database'
;; and were automatically generate by scraping ELPA for auto-loaded
;; code using the `scrape-elpa' command. Please avoid updating this
;; file manually!
(
(ada-mode auto-mode-alist "\\.ad[abs]\\'")
(arbitools auto-mode-alist "\\.trf?\\'" arbitools-mode)
(auctex auto-mode-alist "\\.hva\\'" LaTeX-mode)
(bnf-mode auto-mode-alist "\\.bnf\\'")
(chess auto-mode-alist "\\.pgn\\'" chess-pgn-mode)
(cobol-mode auto-mode-alist "\\.c\\(ob\\|bl\\|py\\)\\'")
(code-cells auto-mode-alist "\\.ipynb\\'" code-cells-convert-ipynb)
(csharp-mode auto-mode-alist "\\.cs\\'")
(csv-mode auto-mode-alist "\\.[Cc][Ss][Vv]\\'")
(csv-mode auto-mode-alist "\\.tsv\\'" tsv-mode)
(dismal auto-mode-alist "\\.dis\\'" dismal-mode)
(djvu auto-mode-alist "\\.djvu\\'" djvu-init-mode)
(dts-mode auto-mode-alist "\\.dtsi?\\'")
(ess auto-mode-alist "\\.[Bb][Uu][Gg]\\'" ess-bugs-mode)
(ess auto-mode-alist "\\.[Bb][Oo][Gg]\\'" ess-bugs-mode)
(ess auto-mode-alist "\\.[Bb][Mm][Dd]\\'" ess-bugs-mode)
(ess auto-mode-alist "\\.[Jj][Aa][Gg]\\'" ess-jags-mode)
(ess auto-mode-alist "/R/.*\\.q\\'" ess-r-mode)
(ess auto-mode-alist "\\.[rR]\\'" ess-r-mode)
(ess auto-mode-alist "\\.[rR]profile\\'" ess-r-mode)
(ess auto-mode-alist "NAMESPACE\\'" ess-r-mode)
(ess auto-mode-alist "CITATION\\'" ess-r-mode)
(ess auto-mode-alist "\\.[Rr]out\\'" ess-r-transcript-mode)
(ess interpreter-mode-alist "Rscript" ess-r-mode)
(ess interpreter-mode-alist "r" ess-r-mode)
(ess auto-mode-alist "/Makevars\\(\\.win\\)?\\'" makefile-mode)
(ess auto-mode-alist "DESCRIPTION\\'" conf-colon-mode)
(ess auto-mode-alist "\\.Rd\\'" Rd-mode)
(ess auto-mode-alist "\\.[Ss]t\\'" S-transcript-mode)
(ess auto-mode-alist "\\.Sout\\'" S-transcript-mode)
(ess auto-mode-alist "\\.[Ss][Aa][Ss]\\'" SAS-mode)
(gle-mode auto-mode-alist "\\.gle\\'")
(gpr-mode auto-mode-alist "\\.gpr\\'")
(html5-schema auto-mode-alist "\\.html?\\'" nxml-mode)
(jgraph-mode auto-mode-alist "\\.jgr\\'")
(json-mode auto-mode-alist "\\.json\\'")
(lmc auto-mode-alist "\\.elmc\\'" lmc-asm-mode)
(matlab-mode auto-mode-alist "\\.tlc\\'" tlc-mode)
(matlab auto-mode-alist "\\.tlc\\'" tlc-mode)
(muse auto-mode-alist "\\.muse\\'" muse-mode-choose-mode)
(auctex auto-mode-alist "\\.drv\\'" latex-mode)
(auctex auto-mode-alist "\\.dtx\\'" doctex-mode)
(nftables-mode auto-mode-alist "\\.nft\\(?:ables\\)?\\'")
(nftables-mode auto-mode-alist "/etc/nftables.conf")
(nftables-mode interpreter-mode-alist "nft\\(?:ables\\)?")
(omn-mode auto-mode-alist "\\.pomn\\'")
(omn-mode auto-mode-alist "\\.omn\\'")
(poke-mode auto-mode-alist "\\.pk\\'")
(pspp-mode auto-mode-alist "\\.sps\\'")
(python auto-mode-alist "/\\(?:Pipfile\\|\\.?flake8\\)\\'" conf-mode)
(rec-mode auto-mode-alist "\\.rec\\'")
(rnc-mode auto-mode-alist "\\.rnc\\'")
(sed-mode auto-mode-alist "\\.sed\\'")
(sed-mode interpreter-mode-alist "sed")
(shen-mode auto-mode-alist "\\.shen\\'")
(sisu-mode auto-mode-alist "\\.ss[imt]\\'")
(smalltalk-mode auto-mode-alist "\\.st\\'")
(sml-mode auto-mode-alist "\\.s\\(ml\\|ig\\)\\'")
(sml-mode auto-mode-alist "\\.cm\\'" sml-cm-mode)
(sml-mode auto-mode-alist "\\.grm\\'" sml-yacc-mode)
(sql-cassandra auto-mode-alist "\\.cql\\'" sql-mode)
(sxhkdrc-mode auto-mode-alist "sxhkdrc\\'")
(systemd auto-mode-alist "\\.automount\\'" systemd-automount-mode)
(systemd auto-mode-alist "\\.mount\\'" systemd-mount-mode)
(systemd auto-mode-alist "\\.path\\'" systemd-path-mode)
(systemd auto-mode-alist "\\.service\\'" systemd-service-mode)
(systemd auto-mode-alist "\\.socket\\'" systemd-socket-mode)
(systemd auto-mode-alist "\\.swap\\'" systemd-swap-mode)
(systemd auto-mode-alist "\\.timer\\'" systemd-timer-mode)
(vcard auto-mode-alist "\\.[Vv][Cc][Ff]\\'" vcard-mode)
(wisi auto-mode-alist "\\.parse_table.*\\'" wisitoken-parse_table-mode)
(wisitoken-grammar-mode auto-mode-alist "\\.wy\\'" simple-indent-mode)
(wisitoken-grammar-mode auto-mode-alist "\\.wy\\'")
(adoc-mode auto-mode-alist "\\.a\\(?:scii\\)?doc\\'")
(apache-mode auto-mode-alist "/\\.htaccess\\'")
(apache-mode auto-mode-alist "/\\(?:access\\|httpd\\|srm\\)\\.conf\\'")
(apache-mode auto-mode-alist "/apache2/.+\\.conf\\'")
(apache-mode auto-mode-alist "/httpd/conf/.+\\.conf\\'")
(apache-mode auto-mode-alist "/apache2/sites-\\(?:available\\|enabled\\)/")
(arduino-mode auto-mode-alist "\\.pde\\'")
(arduino-mode auto-mode-alist "\\.ino\\'")
(beancount auto-mode-alist "\\.beancount\\'" beancount-mode)
(bison-mode auto-mode-alist "\\.y\\'")
(bison-mode auto-mode-alist "\\.l\\'" flex-mode)
(bison-mode auto-mode-alist "\\.jison\\'" jison-mode)
(bqn-mode auto-mode-alist "\\.bqn\\'")
(bqn-mode interpreter-mode-alist "bqn")
(clojure-mode auto-mode-alist "\\.\\(clj\\|cljd\\|dtm\\|edn\\|lpy\\)\\'")
(clojure-mode auto-mode-alist "\\.cljc\\'" clojurec-mode)
(clojure-mode auto-mode-alist "\\.cljs\\'" clojurescript-mode)
(clojure-mode auto-mode-alist "\\(?:build\\|profile\\)\\.boot\\'")
(clojure-mode interpreter-mode-alist "bb")
(clojure-mode interpreter-mode-alist "nbb" clojurescript-mode)
(coffee-mode auto-mode-alist "\\.coffee\\'")
(coffee-mode auto-mode-alist "\\.iced\\'")
(coffee-mode auto-mode-alist "Cakefile\\'")
(coffee-mode auto-mode-alist "\\.cson\\'")
(coffee-mode interpreter-mode-alist "coffee")
(d-mode auto-mode-alist "\\.d[i]?\\'")
(dart-mode auto-mode-alist "\\.dart\\'")
(dockerfile-mode auto-mode-alist "\\.dockerfile\\'")
(drupal-mode auto-mode-alist "[^/]\\.\\(module\\|test\\|install\\|profile\\|tpl\\.php\\|theme\\|inc\\)\\'" php-mode)
(drupal-mode auto-mode-alist "[^/]\\.info\\'" conf-windows-mode)
(drupal-mode auto-mode-alist "[^/]\\.make\\'" drush-make-mode)
(editorconfig auto-mode-alist "\\.editorconfig\\'" editorconfig-conf-mode)
(elixir-mode auto-mode-alist "\\.elixir\\'")
(elixir-mode auto-mode-alist "\\.ex\\'")
(elixir-mode auto-mode-alist "\\.exs\\'")
(elixir-mode auto-mode-alist "mix\\.lock")
(ett auto-mode-alist "\\.ett\\'" ett-mode)
(forth-mode auto-mode-alist "\\.\\(f\\|fs\\|fth\\|4th\\)\\'")
(geiser-racket auto-mode-alist "\\.rkt\\'" scheme-mode)
(gnu-apl-mode auto-mode-alist "\\.apl\\'")
(gnu-apl-mode interpreter-mode-alist "apl")
(go-mode auto-mode-alist "go\\.mod\\'" go-dot-mod-mode)
(go-mode auto-mode-alist "go\\.work\\'" go-dot-work-mode)
(graphql-mode auto-mode-alist "\\.graphql\\'")
(graphql-mode auto-mode-alist "\\.gql\\'")
(haml-mode auto-mode-alist "\\.haml\\'")
(haskell-mode auto-mode-alist "\\.hcr\\'" ghc-core-mode)
(haskell-mode auto-mode-alist "\\.dump-simpl\\'" ghc-core-mode)
(haskell-mode auto-mode-alist "\\.ghci\\'" ghci-script-mode)
(haskell-mode auto-mode-alist "\\.chs\\'" haskell-c2hs-mode)
(haskell-mode auto-mode-alist "\\.cabal\\'\\|/cabal\\.project\\|/\\.cabal/config\\'" haskell-cabal-mode)
(haskell-mode auto-mode-alist "\\.[gh]s\\'")
(haskell-mode auto-mode-alist "\\.hsig\\'")
(haskell-mode auto-mode-alist "\\.l[gh]s\\'" haskell-literate-mode)
(haskell-mode auto-mode-alist "\\.hsc\\'")
(haskell-mode interpreter-mode-alist "runghc")
(haskell-mode interpreter-mode-alist "runhaskell")
(j-mode auto-mode-alist "\\.ij[rsp]$")
(j-mode auto-mode-alist "\\.ijt$" j-lab-mode)
(jade-mode auto-mode-alist "\\.jade\\'")
(jade-mode auto-mode-alist "\\.pug\\'")
(jade-mode auto-mode-alist "\\.styl\\'" stylus-mode)
(jinja2-mode auto-mode-alist "\\.jinja2\\'")
(jinja2-mode auto-mode-alist "\\.j2\\'")
(julia-mode auto-mode-alist "\\.jl\\'")
(lua-mode auto-mode-alist "\\.lua\\'")
(lua-mode interpreter-mode-alist "lua")
(markdown-mode auto-mode-alist "\\.\\(?:md\\|markdown\\|mkd\\|mdown\\|mkdn\\|mdwn\\)\\'")
(nginx-mode auto-mode-alist "nginx\\.conf\\'")
(nginx-mode auto-mode-alist "/nginx/.+\\.conf\\'")
(nix-mode auto-mode-alist "^/nix/store/.+\\.drv\\'" nix-drv-mode)
(nix-mode auto-mode-alist "\\flake.lock\\'" js-mode)
(nix-mode auto-mode-alist "\\.nix\\'")
(php-mode auto-mode-alist "/\\.php_cs\\(?:\\.dist\\)?\\'")
(php-mode auto-mode-alist "\\.\\(?:php\\.inc\\|stub\\)\\'")
(php-mode auto-mode-alist "\\.\\(?:php[s345]?\\|phtml\\)\\'" php-mode-maybe)
(proof auto-mode-alist "\\.v\\'" coq-mode)
(racket-mode auto-mode-alist "\\.rkt\\'")
(racket-mode auto-mode-alist "\\.rktd\\'")
(racket-mode auto-mode-alist "\\.rktl\\'")
(racket-mode interpreter-mode-alist "racket")
(raku-mode interpreter-mode-alist "perl6\\|raku")
(raku-mode auto-mode-alist "\\.p[lm]?6\\'")
(raku-mode auto-mode-alist "\\.nqp\\'")
(raku-mode auto-mode-alist "\\.raku\\(?:mod\\|test\\)?\\'")
(rfc-mode auto-mode-alist "/rfc[0-9]+\\.txt\\'")
(rust-mode auto-mode-alist "\\.rs\\'")
(sass-mode auto-mode-alist "\\.sass\\'")
(scad-mode auto-mode-alist "\\.scad\\'")
(scala-mode auto-mode-alist "\\.\\(scala\\|sbt\\|worksheet\\.sc\\)\\'")
(stylus-mode auto-mode-alist "\\.jade\\'" jade-mode)
(stylus-mode auto-mode-alist "\\.pug\\'" jade-mode)
(stylus-mode auto-mode-alist "\\.styl\\'")
(subed auto-mode-alist "\\.ass\\'" subed-ass-mode)
(subed auto-mode-alist "\\.srt\\'" subed-srt-mode)
(subed auto-mode-alist "\\.vtt\\'" subed-vtt-mode)
(swift-mode auto-mode-alist "\\.swift\\(interface\\)?\\'")
(systemd auto-mode-alist "\\.nspawn\\'" systemd-mode)
(tuareg auto-mode-alist "\\.ml[ip]?\\'" tuareg-mode)
(tuareg auto-mode-alist "\\.eliomi?\\'" tuareg-mode)
(tuareg interpreter-mode-alist "ocamlrun" tuareg-mode)
(tuareg interpreter-mode-alist "ocaml" tuareg-mode)
(tuareg auto-mode-alist "\\.mly\\'" tuareg-menhir-mode)
(tuareg auto-mode-alist "[./]opam_?\\'" tuareg-opam-mode)
(typescript-mode auto-mode-alist "\\.ts\\'")
(yaml-mode auto-mode-alist "\\.\\(e?ya?\\|ra\\)ml\\'")
(yaml-mode magic-mode-alist "^%YAML\\s-+[0-9]+\\.[0-9]+\\(\\s-+#\\|\\s-*$\\)")
(zig-mode auto-mode-alist "\\.\\(zig\\|zon\\)\\'")
)

View file

@ -0,0 +1,226 @@
;;; package-autosuggest.el --- Automatic suggestion of relevant packages -*- lexical-binding: t; -*-
;; Copyright (C) 2023-2025 Free Software Foundation, Inc.
;; Author: Philip Kaludercic <philipk@posteo.net>
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; Packages are suggested to users depending on the entries in the
;; "auto-suggestion" database that is bundled with Emacs. By enabling
;; the `package-autosuggest-mode' minor mode, Emacs will hint that files
;; without any special support, but the user can manually query
;; suggestions using the `package-autosuggest' command as well.
;;; Code:
(require 'package-core)
(require 'package-install)
(defgroup package-autosuggest nil
"Automatic suggestion of relevant packages."
:group 'package
:version "31.1")
(defconst package-autosuggest-database
(eval-when-compile
(with-temp-buffer
(insert-file-contents
(expand-file-name "package-autosuggest.eld" data-directory))
(read (current-buffer))))
"List of hints for packages to suggest installing.
Each hint has the form (PACKAGE TYPE DATA), where PACKAGE is a symbol
denoting the package and major-mode the hint applies to, TYPE is one of
`auto-mode-alist', `magic-mode-alist' or `interpreter-mode-alist'
indicating the type of check to be made and DATA is the value to check
against TYPE in the intuitive way (e.g. for `auto-mode-alist' DATA is a
regular expression matching a file name that PACKAGE should be suggested
for). If the package name and the major mode name differ, then an
optional forth element MAJOR-MODE can indicate what command to invoke to
enable the package.")
(defcustom package-autosuggest-style 'mode-line
"How to draw attention to `package-autosuggest-mode' suggestions.
You can set this value to `mode-line' (default) to indicate the
availability of a package suggestion in the minor mode, `always' to
prompt the user in the minibuffer every time a suggestion is available
in a `fundamenta-mode' buffer, `once' to do only prompt the user once
for each suggestion or `message' to just display a message hinting at
the existence of a suggestion."
:type '(choice (const :tag "Indicate in mode line" mode-line)
(const :tag "Always prompt" always)
(const :tag "Prompt only once" once)
(const :tag "Indicate with message" message)))
;;;###autoload
(define-minor-mode package-autosuggest-mode
"Enable the automatic suggestion and installation of packages."
:global t
(funcall (if package-autosuggest-mode #'add-hook #'remove-hook)
'after-change-major-mode-hook
#'package--autosuggest-after-change-mode))
(defvar package--autosuggest-suggested '()
"List of packages that have already been suggested.
The elements of this list should be a subset of elements from
`package-autosuggest-database'. Suggestions found in this list will not
count as suggestions (e.g. if `package-autosuggest-style' is set to
`mode-line', a suggestion found in here will inhibit
`package-autosuggest-mode' from displaying a hint in the mode line).")
(defun package--suggestion-applies-p (sug)
"Check if a suggestion SUG is applicable to the current buffer.
SUG should be an element of `package-autosuggest-database'."
(pcase sug
(`(,(or (pred (lambda (e) (assq e package--autosuggest-suggested)))
(pred package-installed-p))
. ,_)
nil)
((or `(,_ auto-mode-alist ,ext ,_)
`(,_ auto-mode-alist ,ext))
(and (string-match-p ext (buffer-name)) t))
((or `(,_ magic-mode-alist ,mag ,_)
`(,_ magic-mode-alist ,mag))
(save-restriction
(widen)
(save-excursion
(goto-char (point-min))
(looking-at-p mag))))
((or `(,_ interpreter-mode-alist ,magic ,_)
`(,_ interpreter-mode-alist ,magic))
(save-restriction
(widen)
(save-excursion
(goto-char (point-min))
(and (looking-at auto-mode-interpreter-regexp)
(string-match-p
(concat "\\`" (file-name-nondirectory (match-string 2)) "\\'")
magic)))))))
(defun package--autosuggest-find-candidates ()
"Return a list of suggestions that might be interesting the current buffer.
The elements of the returned list will be a subset of the elements of
`package--autosuggest-suggested'."
(and package-autosuggest-mode (eq major-mode 'fundamental-mode)
(let (suggetions)
(dolist (sug package-autosuggest-database)
(when (package--suggestion-applies-p sug)
(push sug suggetions)))
suggetions)))
(defun package--autosuggest-install-and-enable (sug)
"Install and enable a package suggestion PKG-ENT.
SUG should be an element of `package-autosuggest-database'."
(let ((buffers-to-update '()))
(dolist (buf (buffer-list))
(with-current-buffer buf
(when (and (eq major-mode 'fundamental-mode) (buffer-file-name)
(package--suggestion-applies-p sug))
(push buf buffers-to-update))))
(with-demoted-errors "Failed to install package: %S"
(package-install (car sug))
(dolist (buf buffers-to-update)
(with-demoted-errors "Failed to enable major mode: %S"
(with-current-buffer buf
(funcall-interactively (or (cadddr sug) (car sug)))))))))
(defvar package--autosugest-line-format
'(:eval (package--autosugest-line-format)))
(put 'package--autosugest-line-format 'risky-local-variable t)
(defface package-autosuggest-face
'((t :inherit (success)))
"Face to use in the mode line to highlight suggested packages."
:version "30.1")
(defun package--autosugest-line-format ()
"Generate a mode-line string to indicate a suggested package."
`(,@(and-let* (((not (null package-autosuggest-mode)))
((eq package-autosuggest-style 'mode-line))
(avail (package--autosuggest-find-candidates)))
(propertize
(format "Install %s?"
(mapconcat
#'symbol-name
(delete-dups (mapcar #'car avail))
", "))
'face 'package-autosuggest-face
'mouse-face 'mode-line-highlight
'help-echo "Click to install suggested package."
'keymap (let ((map (make-sparse-keymap)))
(define-key map [mode-line down-mouse-1] #'package-autosuggest)
map)))))
;;;###autoload
(progn
(add-to-list
'mode-line-misc-info
'(package-autosuggest-mode ("" package--autosugest-line-format))))
(defun package--autosuggest-after-change-mode ()
"Display package suggestions for the current buffer.
This function should be added to `after-change-major-mode-hook'."
(when-let* ((avail (package--autosuggest-find-candidates))
(pkgs (mapconcat #'symbol-name
(delete-dups (mapcar #'car avail))
", ")))
(pcase-exhaustive package-autosuggest-style
('mode-line
(force-mode-line-update t))
('always
(when (yes-or-no-p (format "Install suggested packages (%s)?" pkgs))
(mapc #'package--autosuggest-install-and-enable avail)))
('once
(when (yes-or-no-p (format "Install suggested packages (%s)?" pkgs))
(mapc #'package--autosuggest-install-and-enable avail))
(setq package--autosuggest-suggested (append avail package--autosuggest-suggested)))
('message
(message
(substitute-command-keys
(format "Found suggested packages: %s. Install using \\[package-autosuggest]"
pkgs)))))))
;;;###autoload
(defun package-autosuggest ()
"Prompt the user to install the suggested packages."
(interactive)
(let* ((avail (or (package--autosuggest-find-candidates)
(user-error "No suggestions found")))
(use-dialog-box t)
(prompt (concat
"Install "
(mapconcat
#'symbol-name
(delete-dups (mapcar #'car avail))
", ")
"?")))
(if (yes-or-no-p prompt)
(mapc #'package--autosuggest-install-and-enable avail)
(setq package--autosuggest-suggested (append avail package--autosuggest-suggested))
(when (eq package-autosuggest-style 'mode-line)
(force-mode-line-update t)))))
(defun package-reset-suggestions ()
"Forget previous package suggestions.
Emacs will remember if you have previously rejected a suggestion during
a session and won't mention it afterwards. If you have made a mistake
or would like to reconsider this, use this command to want to reset the
suggestions."
(interactive)
(setq package--autosuggest-suggested nil))
(provide 'package-autosuggest)
;;; package-autosuggest.el ends here