mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-02-16 09:14:18 +00:00
Merge remote-tracking branch 'origin/feature/package-autosuggest' into scratch/split-package.el
This commit is contained in:
commit
055ee29eb4
5 changed files with 516 additions and 0 deletions
81
admin/scrape-elpa.el
Normal file
81
admin/scrape-elpa.el
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
11
etc/NEWS
11
etc/NEWS
|
|
@ -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
188
etc/package-autosuggest.eld
Normal 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\\)\\'")
|
||||
)
|
||||
226
lisp/package/package-autosuggest.el
Normal file
226
lisp/package/package-autosuggest.el
Normal 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
|
||||
Loading…
Reference in a new issue