Emacs 29.1 新特性 package-vc.el 让你快乐的管理非melpa的包

我现在配置的原则是能用Emacs自带的功能就用自带的功能,在包管理这个功能上,过去Emacs只有package.el,只能下载melpa上的包。 要下载没有上melpa 的包,要不就用qulpa, 要不就用straight。 但是现在一切都变了,Emacs 29.1新上了 package-vc.el 。 具体见这个package-vc

目前需要添加如下代码才能和 use-package 整合到一起使用


(require 'cl-lib)
(require 'use-package-core)

(cl-defun slot/vc-install (&key (fetcher "github") repo name rev backend)
  (let* ((url (format "https://www.%s.com/%s" fetcher repo))
         (iname (when name (intern name)))
         (package-name (or iname (intern (file-name-base repo)))))
    (unless (package-installed-p package-name)
      (package-vc-install url iname rev backend))))

(defvar package-vc-use-package-keyword :vc)

(defun package-vc-use-package-set-keyword ()
  (unless (member package-vc-use-package-keyword use-package-keywords)
    (setq use-package-keywords
          (let* ((pos (cl-position :unless use-package-keywords))
                 (head (cl-subseq use-package-keywords 0 (+ 1 pos)))
                 (tail (nthcdr (+ 1 pos) use-package-keywords)))
            (append head (list package-vc-use-package-keyword) tail)))))

(defun use-package-normalize/:vc (name-symbol keyword args)
  (let ((arg (car args)))
    (pcase arg
      ((or `nil `t) (list name-symbol))
      ((pred symbolp) args)
      ((pred listp) (cond
                     ((listp (car arg)) arg)
                     ((string-match "^:" (symbol-name (car arg))) (cons name-symbol arg))
                     ((symbolp (car arg)) args)))
      (_ nil))))

(defun use-package-handler/:vc (name-symbol keyword args rest state)
  (let ((body (use-package-process-keywords name-symbol rest state)))
    ;; This happens at macro expansion time, not when the expanded code is
    ;; compiled or evaluated.
    (if args
        (use-package-concat
         `((unless (package-installed-p ',(pcase (car args)
                                            ((pred symbolp) (car args))
                                            ((pred listp) (car (car args)))))
             (apply #'slot/vc-install ',(cdr args))))
         body)
      body)))

(defun package-vc-use-package-override-:ensure (func name-symbol keyword ensure rest state)
  (let ((ensure (if (plist-member rest :vc)
                    nil
                  ensure)))
    (funcall func name-symbol keyword ensure rest state)))

(defun package-vc-use-package-activate-advice ()
  (advice-add
   'use-package-handler/:ensure
   :around
   #'package-vc-use-package-override-:ensure))

(defun package-vc-use-package-deactivate-advice ()
  (advice-remove
   'use-package-handler/:ensure
   #'package-vc-use-package-override-:ensure))

;; register keyword on require
(package-vc-use-package-set-keyword)

使用方式如下

(use-package math-delimiters
  :vc (:fetcher "github" :repo "oantolin/math-delimiters"))
14 个赞

我还在用 quelpa 。主要是 archemacs29.1 刚好比合并 package-vc 要早俩月 :cry:archlinuxcnemacs30 听说bug还有点不太敢用。

Emacs30 里也已经添加了对应的 :vc 关键字

30 里内置的用法是

;; 拉取到最新的 release 版本,不加 `:rev :last-release` 跟这种效果一样
(use-package some-package
  :vc (:url "https://xxx" :rev :last-release))

;; 拉取最新的 commit 版本
(use-package some-package
  :vc (:url "https://xxx" :rev :newest))
4 个赞

可不可以指定一个特定的 commit 版本?

看代码应该是可以的,不过可能是我网络问题或者 bug 吧,我会报 Empty checkout 的错误,指定 branch 和 lisp-dir 倒是有效的

(use-package some-package
  :vc (:url "https://xxx" :rev "<commit>" :branch "dev" :lisp-dir "elisp"))
2 个赞

这个是在太方便了,而且对我 package-vc-install + 额外的 use-package 就够了,之前在.emacs.d 里面放了几个submodule,时不时就就出点问题(操作失误 :frowning: )。

看来以后straight也可以从配置中拿掉了

看 signature (package-vc-install PACKAGE &optional REV BACKEND), 理论上应该是在 REV 的地方设置 commit, 但是实际上会报 empty checkout. 不知道有没有其他用 elisp 指定 commit 的方法.

但是实际上 package-vc-install 下载的是整个 git repo, 因此所有的 commit 全都下载了, 你可以用其他 git 工具 checkout 到你想要的 commit 就可以用了. 然后在 list-package 中相应的 commit hash 也改变.

但确实不知道在 elisp 里应该怎么做, 这应该是一个必然存在的 feature, 就是搞不定

2 个赞

这两天的 commit 新增了一个变量

use-package-vc-prefer-newest

默认为 nil,设为 t 后就会默认拉取最新的 commit 而不是 release 了

1 个赞