怎样定义一个能接受多个 feature 的 with-eval-after-load?

如题,有时想要等多个包加载以后再运行一些代码,use-package:after 在这种时候显得比较难搞。比方说包 a、b 都与 buffer 的管理有关,想把它们的键位配置放在一块,方便管理:

(my-with-eval-after-load (a b general) ; general 是一个用来管理快捷键的包
  keybinding configurations...)

然后它应该展开成

(eval-after-load 'a
  (progn
    (eval-after-load 'b
      (progn
        (eval-after-load 'general
          (progn
            keybinding configurations...))))))

因为咱的 elisp 水平太差,试了几次也没写出一个能用的,请坛友帮帮忙 :rofl:

牢记传统宏的本质是list的变换,想写出想要的功能并不难

ELISP> (defmacro my/after (features &rest body)
         (cl-callf nreverse features)
         (let ((result `(progn ,@body)))
           (dolist (f features)
             (setq result `(with-eval-after-load ',f ,result)))
           result))
my/after
ELISP> (macroexpand-1 '(my/after (a b c) 1))
(with-eval-after-load 'a
  (with-eval-after-load 'b
    (with-eval-after-load 'c
      (progn 1))))

这样似乎能生成更干净的代码,没有多余的progn


ELISP> (defmacro my/after (features &rest body)
         (cl-callf nreverse features)
         (let ((result body))
           (dolist (f features)
             (setq result (macroexp-unprogn `(with-eval-after-load ',f ,@result))))
           (macroexp-progn result)))
my/after
ELISP> (macroexpand-1 '(my/after (a b c) 1))
(with-eval-after-load 'a
  (with-eval-after-load 'b
    (with-eval-after-load 'c 1)))

ELISP> (macroexpand-1 '(my/after (a b c) 1 2 3 4 5 6 'do-it!))
(with-eval-after-load 'a
  (with-eval-after-load 'b
    (with-eval-after-load 'c 1 2 3 4 5 6 'do-it!)))

3 个赞

(。・ω・。)ノ♡

棒棒!

如果 features 之间存在「或」甚至更复杂的关系,BODY 将会在多个位置展开:

(macroexpand-1 '(my/after (and a (or b c)) 1))
;; => 
;; (with-eval-after-load 'a
;;   (with-eval-after-load 'b 1)
;;   (with-eval-after-load 'c 1))

强迫症患者看了恐怕要抓狂。

最彻底的方案是实现自己的 with-eval-after-load,支持 features 表达式运算。

eval-after-load 中最关键的应该是这句了:

把输入的 feature 名称当作 key 去已加载列表当中查找,注定了 eval-after-load 只能接受一个 feature 作为参数。

如果改成接受 lambda,就可以比较容易地实现复杂的逻辑:

(defun my/with-eval-after-load (func body)
  (when (funcall func)
    ...body...
    ))

(my/with-eval-after-load
 (lambda ()
   (and (feature-loaded-p a)
        (or (feature-loaded-p b)
            (feature-loaded-p c))))
 ...body...)
2 个赞