13.12.4 适配使用旧版 defadvice 的代码

大量代码使用旧版的 defadvice 机制,而该机制已被新的 advice-add 废弃。新接口的实现与语义都要简单得多。

一段旧版 advice 示例如下:

(defadvice previous-line (before next-line-at-end
                                 (&optional arg try-vscroll))
  "Insert an empty line when moving up from the top line."
  (if (and next-line-add-newlines (= arg 1)
           (save-excursion (beginning-of-line) (bobp)))
      (progn
        (beginning-of-line)
        (newline))))

可以将其在新版 advice 机制中转换为一个普通函数:

(defun previous-line--next-line-at-end (&optional arg try-vscroll)
  "Insert an empty line when moving up from the top line."
  (if (and next-line-add-newlines (= arg 1)
           (save-excursion (beginning-of-line) (bobp)))
      (progn
        (beginning-of-line)
        (newline))))

显然,这并不会实际修改 previous-line 函数。要实现修改,旧版 advice 还需要执行以下操作:

(ad-activate 'previous-line)

whereas the new advice mechanism needs:

(advice-add 'previous-line :before #'previous-line--next-line-at-end)

需要注意的是,ad-activate 具有全局作用:它会激活为指定函数启用的所有 advice 片段。如果你只想激活或停用某一个特定的片段,就需要通过 ad-enable-advicead-disable-advice启用(enable)停用(disable) 该片段。而新版机制则取消了这种区分。

如下所示的环绕型(Around)advice:

(defadvice foo (around foo-around)
  "Ignore case in `foo'."
  (let ((case-fold-search t))
    ad-do-it))
(ad-activate 'foo)

可转换为:

(defun foo--foo-around (orig-fun &rest args)
  "Ignore case in `foo'."
  (let ((case-fold-search t))
    (apply orig-fun args)))
(advice-add 'foo :around #'foo--foo-around)

关于 advice 的 类型(class),需要注意:新版的 :before 与旧版的 before 并不完全等价。因为在旧版 advice 中,你可以修改函数的参数(例如通过 ad-set-arg),这会影响原函数看到的参数值;而在新版 :before 中,在 advice 里通过 setq 修改参数,不会对原函数看到的参数产生任何影响。 在移植依赖这种行为的 before advice 时,你需要将其改为新版的 :around:filter-args advice。

同样,旧版的 after advice 可以通过修改 ad-return-value 来改变返回值,而新版的 :after advice 无法做到这一点。因此在移植这类旧版 after advice 时,你需要将其改为新版的 :around:filter-return advice。


emacs

Emacs

org-mode

Orgmode

Donations

打赏

Copyright

© 2025 Jasper Hsu

Creative Commons

Creative Commons

Attribute

Attribute

Noncommercial

Noncommercial

Share Alike

Share Alike