summaryrefslogtreecommitdiff
path: root/lisp/emacs-lisp/package.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/emacs-lisp/package.el')
-rw-r--r--lisp/emacs-lisp/package.el153
1 files changed, 109 insertions, 44 deletions
diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el
index cd127e1a8e8..ab1fb8b90fc 100644
--- a/lisp/emacs-lisp/package.el
+++ b/lisp/emacs-lisp/package.el
@@ -120,9 +120,9 @@
;; - "installed" instead of a blank in the status column
;; - tramp needs its files to be compiled in a certain order.
;; how to handle this? fix tramp?
-;; - maybe we need separate .elc directories for various emacs versions
-;; and also emacs-vs-xemacs. That way conditional compilation can
-;; work. But would this break anything?
+;; - maybe we need separate .elc directories for various emacs
+;; versions. That way conditional compilation can work. But would
+;; this break anything?
;; - William Xu suggests being able to open a package file without
;; installing it
;; - Interface with desktop.el so that restarting after an install
@@ -214,7 +214,10 @@ Each element has the form (ID . LOCATION).
(Other types of URL are currently not supported.)
Only add locations that you trust, since fetching and installing
-a package can run arbitrary code."
+a package can run arbitrary code.
+
+HTTPS URLs should be used where possible, as they offer superior
+security."
:type '(alist :key-type (string :tag "Archive name")
:value-type (string :tag "URL or directory name"))
:risky t
@@ -354,9 +357,9 @@ contents of the archive."
(defun package-check-signature ()
"Check whether we have a usable OpenPGP configuration.
-If true, and `package-check-signature' is `allow-unsigned',
-return `allow-unsigned', otherwise return the value of
-`package-check-signature'."
+If so, and variable `package-check-signature' is
+`allow-unsigned', return `allow-unsigned', otherwise return the
+value of variable `package-check-signature'."
(if (eq package-check-signature 'allow-unsigned)
(progn
(require 'epg-config)
@@ -445,11 +448,11 @@ Slots:
`summary' Short description of the package, typically taken from
the first line of the file.
-`reqs' Requirements of the package. A list of (PACKAGE
+`reqs' Requirements of the package. A list of (PACKAGE
VERSION-LIST) naming the dependent package and the minimum
required version.
-`kind' The distribution format of the package. Currently, it is
+`kind' The distribution format of the package. Currently, it is
either `single' or `tar'.
`archive' The name of the archive (as a string) whence this
@@ -472,6 +475,8 @@ Slots:
signed)
(defun package--from-builtin (bi-desc)
+ "Create a `package-desc' object from BI-DESC.
+BI-DESC should be a `package--bi-desc' object."
(package-desc-create :name (pop bi-desc)
:version (package--bi-desc-version bi-desc)
:summary (package--bi-desc-summary bi-desc)
@@ -509,11 +514,21 @@ This is, approximately, the inverse of `version-to-list'.
(apply #'concat (nreverse str-list)))))
(defun package-desc-full-name (pkg-desc)
+ "Return full name of package-desc object PKG-DESC.
+This is the name of the package with its version appended."
(format "%s-%s"
(package-desc-name pkg-desc)
(package-version-join (package-desc-version pkg-desc))))
(defun package-desc-suffix (pkg-desc)
+ "Return file-name extension of package-desc object PKG-DESC.
+Depending on the `package-desc-kind' of PKG-DESC, this is one of:
+
+ 'single - \".el\"
+ 'tar - \".tar\"
+ 'dir - \"\"
+
+Signal an error if the kind is none of the above."
(pcase (package-desc-kind pkg-desc)
('single ".el")
('tar ".tar")
@@ -521,6 +536,10 @@ This is, approximately, the inverse of `version-to-list'.
(kind (error "Unknown package kind: %s" kind))))
(defun package-desc--keywords (pkg-desc)
+ "Return keywords of package-desc object PKG-DESC.
+These keywords come from the foo-pkg.el file, and in general
+corresponds to the keywords in the \"Keywords\" header of the
+package."
(let ((keywords (cdr (assoc :keywords (package-desc-extras pkg-desc)))))
(if (eq (car-safe keywords) 'quote)
(nth 1 keywords)
@@ -530,10 +549,10 @@ This is, approximately, the inverse of `version-to-list'.
"Return the priority of the archive of package-desc object P."
(package-archive-priority (package-desc-archive p)))
-;; Package descriptor format used in finder-inf.el and package--builtins.
(cl-defstruct (package--bi-desc
(:constructor package-make-builtin (version summary))
(:type vector))
+ "Package descriptor format used in finder-inf.el and package--builtins."
version
reqs
summary)
@@ -575,7 +594,15 @@ loaded and/or activated, customize `package-load-list'.")
;; The following functions are called on each installed package by
;; `package-load-all-descriptors', which ultimately populates the
;; `package-alist' variable.
+
(defun package-process-define-package (exp)
+ "Process define-package expression EXP and push it to `package-alist'.
+EXP should be a form read from a foo-pkg.el file.
+Convert EXP into a `package-desc' object using the
+`package-desc-from-define' constructor before pushing it to
+`package-alist'.
+If there already exists a package by that name in
+`package-alist', replace that definition with the new one."
(when (eq (car-safe exp) 'define-package)
(let* ((new-pkg-desc (apply #'package-desc-from-define (cdr exp)))
(name (package-desc-name new-pkg-desc))
@@ -866,6 +893,7 @@ untar into a directory named DIR; otherwise, signal an error."
(mapcar #'macroexp-quote
(apply #'nconc
(mapcar (lambda (pair) (list (car pair) (cdr pair))) alist))))
+
(defun package-unpack (pkg-desc)
"Install the contents of the current buffer as a package."
(let* ((name (package-desc-name pkg-desc))
@@ -977,7 +1005,7 @@ untar into a directory named DIR; otherwise, signal an error."
auto-name))
(defun package--make-autoloads-and-stuff (pkg-desc pkg-dir)
- "Generate autoloads, description file, etc.. for PKG-DESC installed at PKG-DIR."
+ "Generate autoloads, description file, etc., for PKG-DESC installed at PKG-DIR."
(package-generate-autoloads (package-desc-name pkg-desc) pkg-dir)
(let ((desc-file (expand-file-name (package--description-file pkg-dir)
pkg-dir)))
@@ -1028,6 +1056,7 @@ is wrapped around any parts requiring it."
deps))))
(declare-function lm-header "lisp-mnt" (header))
+(declare-function lm-header-multiline "lisp-mnt" (header))
(declare-function lm-homepage "lisp-mnt" (&optional file))
(declare-function lm-keywords-list "lisp-mnt" (&optional file))
(declare-function lm-maintainer "lisp-mnt" (&optional file))
@@ -1054,8 +1083,7 @@ boundaries."
(narrow-to-region start (point))
(require 'lisp-mnt)
;; Use some headers we've invented to drive the process.
- (let* ((requires-str (lm-header "package-requires"))
- ;; Prefer Package-Version; if defined, the package author
+ (let* (;; Prefer Package-Version; if defined, the package author
;; probably wants us to use it. Otherwise try Version.
(pkg-version
(or (package-strip-rcs-id (lm-header "package-version"))
@@ -1067,9 +1095,9 @@ boundaries."
"Package lacks a \"Version\" or \"Package-Version\" header"))
(package-desc-from-define
file-name pkg-version desc
- (if requires-str
- (package--prepare-dependencies
- (package-read-from-string requires-str)))
+ (and-let* ((require-lines (lm-header-multiline "package-requires")))
+ (package--prepare-dependencies
+ (package-read-from-string (mapconcat #'identity require-lines " "))))
:kind 'single
:url homepage
:keywords keywords
@@ -1899,12 +1927,13 @@ if all the in-between dependencies are also in PACKAGE-LIST."
;; installed in a variety of ways (archives, buffer, file), but
;; requirements (dependencies) are always satisfied by looking in
;; `package-archive-contents'.
+
(defun package-archive-base (desc)
- "Return the archive containing the package NAME."
+ "Return the package described by DESC."
(cdr (assoc (package-desc-archive desc) package-archives)))
(defun package-install-from-archive (pkg-desc)
- "Download and install a tar package."
+ "Download and install a tar package defined by PKG-DESC."
;; This won't happen, unless the archive is doing something wrong.
(when (eq (package-desc-kind pkg-desc) 'dir)
(error "Can't install directory package from archive"))
@@ -2081,7 +2110,7 @@ Downloads and installs required packages as needed."
;;;###autoload
(defun package-install-file (file)
- "Install a package from a file.
+ "Install a package from FILE.
The file can either be a tar file, an Emacs Lisp file, or a
directory."
(interactive "fPackage file name: ")
@@ -2217,7 +2246,7 @@ object."
;;;###autoload
(defun package-autoremove ()
- "Remove packages that are no more needed.
+ "Remove packages that are no longer needed.
Packages that are no more needed by other packages in
`package-selected-packages' and their dependencies
@@ -2334,6 +2363,8 @@ The description is read from the installed package files."
)))
(defun describe-package-1 (pkg)
+ "Insert the package description for PKG.
+Helper function for `describe-package'."
(require 'lisp-mnt)
(let* ((desc (or
(if (package-desc-p pkg) pkg)
@@ -2563,6 +2594,9 @@ The description is read from the installed package files."
(browse-url-add-buttons))))
(defun package-install-button-action (button)
+ "Run `package-install' on the package BUTTON points to.
+Used for the 'action property of buttons in the buffer created by
+`describe-package'."
(let ((pkg-desc (button-get button 'package-desc)))
(when (y-or-n-p (format-message "Install package `%s'? "
(package-desc-full-name pkg-desc)))
@@ -2571,6 +2605,9 @@ The description is read from the installed package files."
(goto-char (point-min)))))
(defun package-delete-button-action (button)
+ "Run `package-delete' on the package BUTTON points to.
+Used for the 'action property of buttons in the buffer created by
+`describe-package'."
(let ((pkg-desc (button-get button 'package-desc)))
(when (y-or-n-p (format-message "Delete package `%s'? "
(package-desc-full-name pkg-desc)))
@@ -2579,10 +2616,17 @@ The description is read from the installed package files."
(goto-char (point-min)))))
(defun package-keyword-button-action (button)
+ "Show filtered \"*Packages*\" buffer for BUTTON.
+The buffer is filtered by the `package-keyword' property of BUTTON.
+Used for the 'action property of buttons in the buffer created by
+`describe-package'."
(let ((pkg-keyword (button-get button 'package-keyword)))
(package-show-package-list t (list pkg-keyword))))
-(defun package-make-button (text &rest props)
+(defun package-make-button (text &rest properties)
+ "Insert button labeled TEXT with button PROPERTIES at point.
+PROPERTIES are passed to `insert-text-button', for which this
+function is a convenience wrapper used by `describe-package-1'."
(let ((button-text (if (display-graphic-p) text (concat "[" text "]")))
(button-face (if (display-graphic-p)
'(:box (:line-width 2 :color "dark grey")
@@ -2590,20 +2634,23 @@ The description is read from the installed package files."
:foreground "black")
'link)))
(apply #'insert-text-button button-text 'face button-face 'follow-link t
- props)))
-
-(defun package--print-email-button (name)
- (when (car name)
- (insert (car name)))
- (when (and (car name) (cdr name))
+ properties)))
+
+(defun package--print-email-button (recipient)
+ "Insert a button whose action will send an email to RECIPIENT.
+NAME should have the form (FULLNAME . EMAIL) where FULLNAME is
+either a full name or nil, and EMAIL is a valid email address."
+ (when (car recipient)
+ (insert (car recipient)))
+ (when (and (car recipient) (cdr recipient))
(insert " "))
- (when (cdr name)
+ (when (cdr recipient)
(insert "<")
- (insert-text-button (cdr name)
+ (insert-text-button (cdr recipient)
'follow-link t
'action (lambda (_)
(compose-mail
- (format "%s <%s>" (car name) (cdr name)))))
+ (format "%s <%s>" (car recipient) (cdr recipient)))))
(insert ">"))
(insert "\n"))
@@ -2678,11 +2725,11 @@ Letters do not insert themselves; instead, they are commands.
package-menu--transaction-status)))
(setq tabulated-list-format
`[("Package" 18 package-menu--name-predicate)
- ("Version" 13 nil)
+ ("Version" 13 package-menu--version-predicate)
("Status" 10 package-menu--status-predicate)
,@(if (cdr package-archives)
'(("Archive" 10 package-menu--archive-predicate)))
- ("Description" 0 nil)])
+ ("Description" 0 package-menu--description-predicate)])
(setq tabulated-list-padding 2)
(setq tabulated-list-sort-key (cons "Status" nil))
(add-hook 'tabulated-list-revert-hook #'package-menu--refresh nil t)
@@ -2701,13 +2748,13 @@ package PKG-DESC, add one. The alist is keyed with PKG-DESC."
(push (cons ,pkg-desc ,status) ,listname)))
(defvar package-list-unversioned nil
- "If non-nil include packages that don't have a version in `list-package'.")
+ "If non-nil, include packages that don't have a version in `list-packages'.")
(defvar package-list-unsigned nil
"If non-nil, mention in the list which packages were installed w/o signature.")
(defvar package--emacs-version-list (version-to-list emacs-version)
- "`emacs-version', as a list.")
+ "The value of variable `emacs-version' as a list.")
(defun package--incompatible-p (pkg &optional shallow)
"Return non-nil if PKG has no chance of being installable.
@@ -2782,7 +2829,7 @@ Can be toggled with \\<package-menu-mode-map> \\[package-menu-toggle-hiding].
Installed obsolete packages are always displayed.")
(defun package-menu-toggle-hiding ()
- "Toggle visibility of obsolete available packages."
+ "In Package Menu, toggle visibility of obsolete available packages."
(interactive)
(unless (derived-mode-p 'package-menu-mode)
(user-error "The current buffer is not a Package Menu"))
@@ -2840,7 +2887,7 @@ If the name of a package matches any of these regexps it is
omitted from the package menu. To toggle this, type \\[package-menu-toggle-hiding].
Values can be interactively added to this list by typing
-\\[package-menu-hide-package] on a package"
+\\[package-menu-hide-package] on a package."
:version "25.1"
:type '(repeat (regexp :tag "Hide packages with name matching")))
@@ -3100,7 +3147,7 @@ user-error if there is already a refresh running asynchronously."
(package-refresh-contents package-menu-async))
(defun package-menu-hide-package ()
- "Hide a package under point.
+ "Hide a package under point in Package Menu.
If optional arg BUTTON is non-nil, describe its associated package."
(interactive)
(declare (interactive-only "change `package-hidden-regexps' instead."))
@@ -3199,6 +3246,7 @@ The full list of keys can be viewed with \\[describe-mode]."
'package-menu-view-commentary 'package-menu-describe-package "24.1")
(defun package-menu-get-status ()
+ "Return status text of package at point in Package Menu."
(let* ((id (tabulated-list-get-id))
(entry (and id (assoc id tabulated-list-entries))))
(if entry
@@ -3224,6 +3272,10 @@ consideration."
(package-desc-version pkg-desc)))
(defun package-menu--find-upgrades ()
+ "In Package Menu, return an alist of packages that can be upgraded.
+The alist has the same form as `package-alist', namely a list
+of (PKG . DESCS), but where DESCS is the `package-desc' object
+corresponding to the newer version."
(let (installed available upgrades)
;; Build list of installed/available packages in this buffer.
(dolist (entry tabulated-list-entries)
@@ -3417,13 +3469,17 @@ Optional argument NOQUERY non-nil means do not ask the user to confirm."
(message "Operation %s finished" message-template))))))))
(defun package-menu--version-predicate (A B)
- (let ((vA (or (aref (cadr A) 1) '(0)))
- (vB (or (aref (cadr B) 1) '(0))))
+ "Predicate to sort \"*Packages*\" buffer by the version column.
+This is used for `tabulated-list-format' in `package-menu-mode'."
+ (let ((vA (or (version-to-list (aref (cadr A) 1)) '(0)))
+ (vB (or (version-to-list (aref (cadr B) 1)) '(0))))
(if (version-list-= vA vB)
(package-menu--name-predicate A B)
(version-list-< vA vB))))
(defun package-menu--status-predicate (A B)
+ "Predicate to sort \"*Packages*\" buffer by the status column.
+This is used for `tabulated-list-format' in `package-menu-mode'."
(let ((sA (aref (cadr A) 2))
(sB (aref (cadr B) 2)))
(cond ((string= sA sB)
@@ -3454,19 +3510,28 @@ Optional argument NOQUERY non-nil means do not ask the user to confirm."
(t (string< sA sB)))))
(defun package-menu--description-predicate (A B)
- (let ((dA (aref (cadr A) 3))
- (dB (aref (cadr B) 3)))
+ "Predicate to sort \"*Packages*\" buffer by the description column.
+This is used for `tabulated-list-format' in `package-menu-mode'."
+ (let ((dA (aref (cadr A) (if (cdr package-archives) 4 3)))
+ (dB (aref (cadr B) (if (cdr package-archives) 4 3))))
(if (string= dA dB)
(package-menu--name-predicate A B)
(string< dA dB))))
(defun package-menu--name-predicate (A B)
+ "Predicate to sort \"*Packages*\" buffer by the name column.
+This is used for `tabulated-list-format' in `package-menu-mode'."
(string< (symbol-name (package-desc-name (car A)))
(symbol-name (package-desc-name (car B)))))
(defun package-menu--archive-predicate (A B)
- (string< (or (package-desc-archive (car A)) "")
- (or (package-desc-archive (car B)) "")))
+ "Predicate to sort \"*Packages*\" buffer by the archive column.
+This is used for `tabulated-list-format' in `package-menu-mode'."
+ (let ((a (or (package-desc-archive (car A)) ""))
+ (b (or (package-desc-archive (car B)) "")))
+ (if (string= a b)
+ (package-menu--name-predicate A B)
+ (string< a b))))
(defun package-menu--populate-new-package-list ()
"Decide which packages are new in `package-archives-contents'.
@@ -3487,7 +3552,7 @@ Store this list in `package-menu--new-package-list'."
(defun package-menu--post-refresh ()
- "If there's a *Packages* buffer, revert it and check for new packages and upgrades.
+ "Revert \"*Packages*\" buffer and check for new packages and upgrades.
Do nothing if there's no *Packages* buffer.
This function is called after `package-refresh-contents' and it