Add :vc keyword to use-package for VC package support
* lisp/use-package/use-package-core.el (use-package-keywords): Add :vc. (use-package-handler/:load-path): Insert 'load-path' into 'state'. (use-package-vc-install): Install the package with package-vc.el. (use-package-handler/:vc): Handler for the :vc keyword. (use-package-normalize--vc-arg): Normalization for more complex arguments to 'use-package-normalize/:vc', in order to make them compatible with the specification of 'package-vc-selected-packages'. (use-package-normalize/:vc): Normalizer for the :vc keyword. (use-package): Document :vc. * lisp/use-package/use-package-ensure.el (use-package-handler/:ensure): Do not ensure a package when :vc is used in the declaration. * test/lisp/use-package/use-package-tests.el (use-package-test/:vc-1): (use-package-test/:vc-2): (use-package-test/:vc-3): (use-package-test/:vc-4): (use-package-test/:vc-5): (use-package-test-normalize/:vc): Add tests for :vc. * etc/NEWS: Mention change. (Bug#60418)
This commit is contained in:
parent
5ac08768aa
commit
2ce279680b
5 changed files with 219 additions and 5 deletions
|
@ -1554,8 +1554,11 @@ The standard Emacs package manager is documented in the Emacs manual
|
|||
(@pxref{Package Installation,,, emacs, GNU Emacs Manual}). The
|
||||
@code{use-package} macro provides the @code{:ensure} and @code{:pin}
|
||||
keywords that interface with that package manager to automatically
|
||||
install packages. This is particularly useful if you use your init
|
||||
file on more than one system.
|
||||
install packages. The @code{:vc} keyword may be used to control how
|
||||
package sources are downloaded; e.g., from remote hosts
|
||||
(@pxref{Fetching Package Sources,,, emacs, GNU Emacs Manual}). This
|
||||
is particularly useful if you use your init file on more than one
|
||||
system.
|
||||
|
||||
@menu
|
||||
* Install package::
|
||||
|
@ -1607,6 +1610,49 @@ packages:
|
|||
You can override the above setting for a single package by adding
|
||||
@w{@code{:ensure nil}} to its declaration.
|
||||
|
||||
@findex :vc
|
||||
The @code{:vc} keyword can be used to control how packages are
|
||||
downloaded and/or installed. More specifically, it allows one to fetch
|
||||
and update packages directly from a version control system. This is
|
||||
especially convenient when wanting to install a package that is not on
|
||||
any package archive.
|
||||
|
||||
The keyword accepts the same arguments as specified in
|
||||
@pxref{Fetching Package Sources,,, emacs, GNU Emacs Manual}, except
|
||||
that a name need not explicitly be given: it is inferred from the
|
||||
declaration. The accepted property list is augmented by a @code{:rev}
|
||||
keyword, which has the same shape as the @code{REV} argument to
|
||||
@code{package-vc-install}. Notably -- even when not specified --
|
||||
@code{:rev} defaults to checking out the last release of the package.
|
||||
You can use @code{:rev :newest} to check out the latest commit.
|
||||
|
||||
For example,
|
||||
|
||||
@example
|
||||
@group
|
||||
(use-package bbdb
|
||||
:vc (:url "https://git.savannah.nongnu.org/git/bbdb.git"
|
||||
:rev :newest))
|
||||
@end group
|
||||
@end example
|
||||
|
||||
would try -- by invoking @code{package-vc-install} -- to install the
|
||||
latest commit of the package @code{foo} from the specified remote.
|
||||
|
||||
This can also be used for local packages, by combining it with the
|
||||
@code{:load-path} (@pxref{Load path}) keyword:
|
||||
|
||||
@example
|
||||
@group
|
||||
;; Use a local copy of BBDB instead of the one from GNU ELPA.
|
||||
(use-package bbdb
|
||||
:vc t
|
||||
:load-path "/path/to/bbdb/dir/")
|
||||
@end group
|
||||
@end example
|
||||
|
||||
The above dispatches to @code{package-vc-install-from-checkout}.
|
||||
|
||||
@node Pinning packages
|
||||
@section Pinning packages using @code{:pin}
|
||||
@cindex installing package from specific archive
|
||||
|
|
6
etc/NEWS
6
etc/NEWS
|
@ -327,6 +327,12 @@ instead of:
|
|||
and another_expression):
|
||||
do_something()
|
||||
|
||||
** use-package
|
||||
|
||||
+++
|
||||
*** New ':vc' keyword.
|
||||
This keyword enables the user to install packages using 'package-vc'.
|
||||
|
||||
|
||||
* New Modes and Packages in Emacs 30.1
|
||||
|
||||
|
|
|
@ -76,6 +76,7 @@
|
|||
:functions
|
||||
:preface
|
||||
:if :when :unless
|
||||
:vc
|
||||
:no-require
|
||||
:catch
|
||||
:after
|
||||
|
@ -1151,7 +1152,8 @@ meaning:
|
|||
#'use-package-normalize-paths))
|
||||
|
||||
(defun use-package-handler/:load-path (name _keyword arg rest state)
|
||||
(let ((body (use-package-process-keywords name rest state)))
|
||||
(let ((body (use-package-process-keywords name rest
|
||||
(plist-put state :load-path arg))))
|
||||
(use-package-concat
|
||||
(mapcar #'(lambda (path)
|
||||
`(eval-and-compile (add-to-list 'load-path ,path)))
|
||||
|
@ -1577,6 +1579,109 @@ no keyword implies `:all'."
|
|||
(when use-package-compute-statistics
|
||||
`((use-package-statistics-gather :config ',name t))))))
|
||||
|
||||
;;;; :vc
|
||||
|
||||
(defun use-package-vc-install (arg &optional local-path)
|
||||
"Install a package with `package-vc.el'.
|
||||
ARG is a list of the form (NAME OPTIONS REVISION), as returned by
|
||||
`use-package-normalize--vc-arg'. If LOCAL-PATH is non-nil, call
|
||||
`package-vc-install-from-checkout'; otherwise, indicating a
|
||||
remote host, call `package-vc-install' instead."
|
||||
(pcase-let* ((`(,name ,opts ,rev) arg)
|
||||
(spec (if opts (cons name opts) name)))
|
||||
(unless (package-installed-p name)
|
||||
(if local-path
|
||||
(package-vc-install-from-checkout local-path (symbol-name name))
|
||||
(package-vc-install spec rev)))))
|
||||
|
||||
(defun use-package-handler/:vc (name _keyword arg rest state)
|
||||
"Generate code to install package NAME, or do so directly.
|
||||
When the use-package declaration is part of a byte-compiled file,
|
||||
install the package during compilation; otherwise, add it to the
|
||||
macro expansion and wait until runtime. The remaining arguments
|
||||
are as follows:
|
||||
|
||||
_KEYWORD is ignored.
|
||||
|
||||
ARG is the normalized input to the `:vc' keyword, as returned by
|
||||
the `use-package-normalize/:vc' function.
|
||||
|
||||
REST is a plist of other (following) keywords and their
|
||||
arguments, each having already been normalised by the respective
|
||||
function.
|
||||
|
||||
STATE is a plist of any state that keywords processed before
|
||||
`:vc' (see `use-package-keywords') may have accumulated.
|
||||
|
||||
Also see the Info node `(use-package) Creating an extension'."
|
||||
(let ((body (use-package-process-keywords name rest state))
|
||||
(local-path (car (plist-get state :load-path))))
|
||||
;; See `use-package-handler/:ensure' for an explanation.
|
||||
(if (bound-and-true-p byte-compile-current-file)
|
||||
(funcall #'use-package-vc-install arg local-path) ; compile time
|
||||
(push `(use-package-vc-install ',arg ,local-path) body)))) ; runtime
|
||||
|
||||
(defun use-package-normalize--vc-arg (arg)
|
||||
"Normalize possible arguments to the `:vc' keyword.
|
||||
ARG is a cons-cell of approximately the form that
|
||||
`package-vc-selected-packages' accepts, plus an additional `:rev'
|
||||
keyword. If `:rev' is not given, it defaults to `:last-release'.
|
||||
|
||||
Returns a list (NAME SPEC REV), where (NAME . SPEC) is compliant
|
||||
with `package-vc-selected-packages' and REV is a (possibly nil,
|
||||
indicating the latest commit) revision."
|
||||
(cl-flet* ((ensure-string (s)
|
||||
(if (and s (stringp s)) s (symbol-name s)))
|
||||
(ensure-symbol (s)
|
||||
(if (and s (stringp s)) (intern s) s))
|
||||
(normalize (k v)
|
||||
(pcase k
|
||||
(:rev (cond ((or (eq v :last-release) (not v)) :last-release)
|
||||
((eq v :newest) nil)
|
||||
(t (ensure-string v))))
|
||||
(:vc-backend (ensure-symbol v))
|
||||
(_ (ensure-string v)))))
|
||||
(pcase-let ((valid-kws '(:url :branch :lisp-dir :main-file :vc-backend :rev))
|
||||
(`(,name . ,opts) arg))
|
||||
(if (stringp opts) ; (NAME . VERSION-STRING) ?
|
||||
(list name opts)
|
||||
;; Error handling
|
||||
(cl-loop for (k _) on opts by #'cddr
|
||||
if (not (member k valid-kws))
|
||||
do (use-package-error
|
||||
(format "Keyword :vc received unknown argument: %s. Supported keywords are: %s"
|
||||
k valid-kws)))
|
||||
;; Actual normalization
|
||||
(list name
|
||||
(cl-loop for (k v) on opts by #'cddr
|
||||
if (not (eq k :rev))
|
||||
nconc (list k (normalize k v)))
|
||||
(normalize :rev (plist-get opts :rev)))))))
|
||||
|
||||
(defun use-package-normalize/:vc (name _keyword args)
|
||||
"Normalize possible arguments to the `:vc' keyword.
|
||||
NAME is the name of the `use-package' declaration, _KEYWORD is
|
||||
ignored, and ARGS it a list of arguments given to the `:vc'
|
||||
keyword, the cdr of which is ignored.
|
||||
|
||||
See `use-package-normalize--vc-arg' for most of the actual
|
||||
normalization work. Also see the Info
|
||||
node `(use-package) Creating an extension'."
|
||||
(let ((arg (car args)))
|
||||
(pcase arg
|
||||
((or 'nil 't) (list name)) ; guess name
|
||||
((pred symbolp) (list arg)) ; use this name
|
||||
((pred stringp) (list name arg)) ; version string + guess name
|
||||
((pred plistp) ; plist + guess name
|
||||
(use-package-normalize--vc-arg (cons name arg)))
|
||||
(`(,(pred symbolp) . ,(or (pred plistp) ; plist/version string + name
|
||||
(pred stringp)))
|
||||
(use-package-normalize--vc-arg arg))
|
||||
(_ (use-package-error "Unrecognised argument to :vc.\
|
||||
The keyword wants an argument of nil, t, a name of a package,\
|
||||
or a cons-cell as accepted by `package-vc-selected-packages', where \
|
||||
the accepted plist is augmented by a `:rev' keyword.")))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;
|
||||
;;; The main macro
|
||||
|
@ -1666,7 +1771,9 @@ Usage:
|
|||
(compare with `custom-set-variables').
|
||||
:custom-face Call `custom-set-faces' with each face definition.
|
||||
:ensure Loads the package using package.el if necessary.
|
||||
:pin Pin the package to an archive."
|
||||
:pin Pin the package to an archive.
|
||||
:vc Install the package directly from a version control system
|
||||
(using `package-vc.el')."
|
||||
(declare (indent defun))
|
||||
(unless (memq :disabled args)
|
||||
(macroexp-progn
|
||||
|
|
|
@ -182,7 +182,8 @@ manually updated package."
|
|||
|
||||
;;;###autoload
|
||||
(defun use-package-handler/:ensure (name _keyword ensure rest state)
|
||||
(let* ((body (use-package-process-keywords name rest state)))
|
||||
(let* ((body (use-package-process-keywords name rest state))
|
||||
(ensure (and (not (plist-member rest :vc)) ensure)))
|
||||
;; We want to avoid installing packages when the `use-package' macro is
|
||||
;; being macro-expanded by elisp completion (see `lisp--local-variables'),
|
||||
;; but still install packages when byte-compiling, to avoid requiring
|
||||
|
|
|
@ -1951,6 +1951,60 @@
|
|||
(should (eq (nth 1 binding) 'ignore))
|
||||
(should (eq (nth 2 binding) nil))))
|
||||
|
||||
(ert-deftest use-package-test/:vc-1 ()
|
||||
(match-expansion
|
||||
(use-package foo :vc (:url "bar"))
|
||||
'(progn (use-package-vc-install '(foo (:url "bar") :last-release) nil)
|
||||
(require 'foo nil nil))))
|
||||
|
||||
(ert-deftest use-package-test/:vc-2 ()
|
||||
(match-expansion
|
||||
(use-package foo
|
||||
:vc (baz . (:url "baz" :vc-backend "Git"
|
||||
:main-file qux.el :rev "rev-string")))
|
||||
'(progn (use-package-vc-install '(baz
|
||||
(:url "baz" :vc-backend Git :main-file "qux.el")
|
||||
"rev-string")
|
||||
nil)
|
||||
(require 'foo nil nil))))
|
||||
|
||||
(ert-deftest use-package-test/:vc-3 ()
|
||||
(match-expansion
|
||||
(use-package foo :vc (bar . "baz"))
|
||||
'(progn (use-package-vc-install '(bar "baz") nil)
|
||||
(require 'foo nil nil))))
|
||||
|
||||
(ert-deftest use-package-test/:vc-4 ()
|
||||
(match-expansion
|
||||
(use-package foo :vc (bar . (:url "baz" :rev :newest)))
|
||||
'(progn (use-package-vc-install '(bar (:url "baz") nil) nil)
|
||||
(require 'foo nil nil))))
|
||||
|
||||
(ert-deftest use-package-test/:vc-5 ()
|
||||
(let ((load-path? '(pred (apply-partially
|
||||
#'string=
|
||||
(expand-file-name "bar" user-emacs-directory)))))
|
||||
(match-expansion
|
||||
(use-package foo :vc other-name :load-path "bar")
|
||||
`(progn (eval-and-compile
|
||||
(add-to-list 'load-path ,load-path?))
|
||||
(use-package-vc-install '(other-name) ,load-path?)
|
||||
(require 'foo nil nil)))))
|
||||
|
||||
(ert-deftest use-package-test-normalize/:vc ()
|
||||
(should (equal '(foo "version-string")
|
||||
(use-package-normalize/:vc 'foo :vc '("version-string"))))
|
||||
(should (equal '(bar "version-string")
|
||||
(use-package-normalize/:vc 'foo :vc '((bar . "version-string")))))
|
||||
(should (equal '(foo (:url "bar") "baz")
|
||||
(use-package-normalize/:vc 'foo :vc '((:url "bar" :rev "baz")))))
|
||||
(should (equal '(foo)
|
||||
(use-package-normalize/:vc 'foo :vc '(t))))
|
||||
(should (equal '(foo)
|
||||
(use-package-normalize/:vc 'foo :vc nil)))
|
||||
(should (equal '(bar)
|
||||
(use-package-normalize/:vc 'foo :vc '(bar)))))
|
||||
|
||||
;; Local Variables:
|
||||
;; no-byte-compile: t
|
||||
;; no-update-autoloads: t
|
||||
|
|
Loading…
Add table
Reference in a new issue