如题,有时想要等多个包加载以后再运行一些代码,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 水平太差,试了几次也没写出一个能用的,请坛友帮帮忙
cireu
2
牢记传统宏的本质是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 个赞