使用 Advice 修改命令的 Interactive Spec

用 Advice 修改函数大家都知道,但它也能用来修改命令的 Interactive Spec,从而改变命令参数的读取方式。规则如下:

(advice-add SYMBOL WHERE FUNCTION &optional PROPS)
(advice-add 'display-buffer :around #'his-tracing-function)
  1. 如果 FUNCTION 没写 Interactive Spec 则继承 SYMBOL 的;
  2. 如果 FUNCTION 写了 Interactive Spec 则覆盖 SYMBOL 的;
  3. 特例:如果 FUNCTION 的 Interactive Spec 是个函数,则调用这个函数,参数是 SYMBOL 的 Interactive Spec。

相关文档见 add-function 的 Info 文档或者 Docstring,分别用 C-h SC-h f

下面举两个例子,分别对应情况 2 和情况 3:

例1 comint-run

比如 comint-run 这个命令这么写不会有补全:

(defun comint-run (program)
  "..."
  (interactive "sRun program: ")
  ...)

同样读取命令,应该跟 M-! (shell-command) 一样提供补全才对吗:

(define-advice comint-run (:before (_program) fix-interactive-form)
  "Fix the original interactive form."
  (interactive (list (read-shell-command "Run program: "))))

例2 goto-line

执行 goto-line 时希望能够预览当前行号:

图:左边默认没行号 V.S. 右边定制有行号

(define-advice goto-line (:before (&rest _) preview-line-number)
  "Preview line number when prompting for line number.
Idea from URL `https://www.reddit.com/r/emacs/comments/as83e2/weekly_tipstricketc_thread/egu2sve'."
  (interactive
   (lambda (spec)
     (if (and (boundp 'display-line-numbers) ; `display-line-numbers' was added in Emacs 26.1
              (not display-line-numbers))
         (unwind-protect
             (progn (display-line-numbers-mode)
                    (advice-eval-interactive-spec spec))
           (display-line-numbers-mode -1))
       (advice-eval-interactive-spec spec)))))
5 个赞

define-adviceadvice-add/add-function的区别是前者可以不用另外写defun就能给advice一个名字?

define-advice 是一个 Macro,展开就等于 defun + advice-add

1 个赞

用法越来越高级了