给 use-package 添加新的(没实际作用的)关键词

本来非代码的部分一般放到注释中的,不过用 use-package 的关键词组织起来感觉似乎不错:

(use-package magit
  :ensure t
  :homepage https://github.com/magit/magit
  :info (info "(magit) Top")
  :tips
  - To launch from shell, use $ emacsclient -e '(magit-status)'
  - Here is another tip
  - ...
  :bind ("C-x C-g" . magit-status)
  :config (setq magit-save-repository-buffers 'dontask))

上面添加了三个新的关键词: homepage, infotips,不像其它的关键词,这三个关键词完全没用,不对原来的 (use-packag ...) 造成任何影响。

(add-to-list 'use-package-keywords :homepage t)
(defun use-package-normalize/:homepage (&rest _))
(defun use-package-handler/:homepage (&rest _))

(add-to-list 'use-package-keywords :info t)
(defun use-package-normalize/:info (&rest _))
(defun use-package-handler/:info (&rest _))

(add-to-list 'use-package-keywords :tips t)
(defun use-package-normalize/:tips (&rest _))
(defun use-package-handler/:tips (&rest _))

Extending use-package with new or modified keywords

1 个赞

非常聪明的想法,赞

URL 还是最好用 String 吧。

可以用 String,只要 Lisp Reader 能处理就行。

说到这种关键词我想起了 plist 的问题, 其实冒号也不是必须的:

  (plist-get '(:foo 1 bar 2)         :foo) ;; => 1
  (plist-get '(:foo 1 bar 2)         'bar) ;; => 2

另一个问题是:如何像 use-package 那样,一个 keyword 对应多个值呢?

  (plist-get '(:foo 1 2 3 :bar 4)    :foo) ;; => 1
  (plist-member '(:foo 1 2 3 :bar 4) :foo) ;; => (:foo 1 2 3 :bar 4)
  (plist-member '(:foo 1 2 3 :bar 4) :bar) ;; => (:bar 4)

plist-memmber 返回的是剩下所有元素,这个特性可以拿来用:

  • 有固定的 keywords
    (setq my-keywords '(:foo t :bar t))

    (defun my-plist-get (plst key)
      (let (vals)
        (catch 'break
          (mapc (lambda (val)
                  (if (plist-get my-keywords val)
                      (throw 'break vals)
                    (setq vals (append vals `(,val)))))
                (cdr (plist-member plst key))))))
    
    (my-plist-get '(:foo 1 2 3 :bar 4) :foo)
    ;; => (1 2 3)
  • 无 keywords
    (defun my-plist-get (plst key)
      (let (vals)
        (catch 'break
          (mapc (lambda (val)
                  (if (keywordp val)
                      (throw 'break vals)
                    (setq vals (append vals `(,val)))))
                (cdr (plist-member plst key))))))
    
    (my-plist-get '(:foo 1 2 3 :bar 4) :foo)
    ;; => (1 2 3)

应该可以参照 cl-defun

一个属性对应多个值就不适合继续叫做 plist 了。

use-package 把这种叫做 pseudo-plist,使用之前转换成 plist:

(use-package-normalize-plist ":init" '(:init (foo) (bar)))
;; => (:init ((foo) (bar)))

:point_right: https://github.com/jwiegley/use-package/blob/1928d80/use-package.el#L513