如何使用nadvice?


29

我的配置充满了建议,而且我不断听到有关新的闪亮简约nadvice.el包的信息。

我已经搜索了手册,并且阅读了源代码,但我会公开承认:我仍然不知道如何实际使用它。

在座的任何人都可以向我指出指南,或告诉我如何开始移植旧式建议吗?


7
+1问题。如果您已经搜索了手册,但找不到所需的内容,请考虑提交(doc)错误报告:M-x report-emacs-bug。有些开发人员有时更喜欢开发而不是文档。;-) Emacs文档本身很重要。
2015年

2
手册实际上对此有一部分,请参见(信息“(elisp)移植旧建议”)。无论出于何种原因,它都没有在详细索引中列出。
wasamasa


3
nadvice从我的配置中使用的几个示例::after:filter-return:around:before-until
Kaushal Modi 2015年

1
@wasamasa恐怕这一节还远远没有完成。我有一些更复杂的建议(也许只有一个,我们将会看到)。我应该在这里对每个人提问吗?
PythonNut

Answers:


22

C-h f add-function其中包含您需要的所有信息,其中描述了的基本机制advice-add

新的建议系统基本上就像用下表中描述的函数替换函数的当前定义一样 C-h f add-function,这取决于您对WHERE 参数的选择,只是为了跟踪已在哪个源文件中定义了什么行为而更干净。

带有:around选项的示例

最一般的情况是该:around选项,因此我举一个例子。(在可能的情况下,最好使用专用WHERE参数,但您可以用等效的:around函数相互替换 )。

举个例子,假设您想调试每次调用它的参数列表find-file 并使用print它。你可以写

(defun my-find-file-advice-print-arguments (old-function &rest arguments)
  "Print the argument list every time the advised function is called."
  (print arguments)
  (apply old-function arguments))

(advice-add #'find-file :around #'my-find-file-advice-print-arguments)

通过此新实现,建议所需的所有内容均作为参数传递。ad-get-args因为参数作为普通函数参数(对于WHERE有意义的参数)传递给通知函数,因此变得不必要 。ad-do-it由于:around建议将函数和参数作为参数而变得不必要,因此(ad-do-it)它被替换为形式

(apply old-function arguments)

或在您命名参数时

(funcall old-function first-arg second-arg)

因为不涉及任何魔术形式,所以它更清洁。修改参数只需将修改后的值传递给即可OLD-FUNCTION

其他WHERE

的docstring add-function包含所有建议场所(或“组合器”)及其等效表,并lambda以与建议功能等效的方式解释功能:

`:before'       (lambda (&rest r) (apply FUNCTION r) (apply OLDFUN r))
`:after'        (lambda (&rest r) (prog1 (apply OLDFUN r) (apply FUNCTION r)))
`:around'       (lambda (&rest r) (apply FUNCTION OLDFUN r))
`:override'     (lambda (&rest r) (apply FUNCTION r))
`:before-while' (lambda (&rest r) (and (apply FUNCTION r) (apply OLDFUN r)))
`:before-until' (lambda (&rest r) (or  (apply FUNCTION r) (apply OLDFUN r)))
`:after-while'  (lambda (&rest r) (and (apply OLDFUN r) (apply FUNCTION r)))
`:after-until'  (lambda (&rest r) (or  (apply OLDFUN r) (apply FUNCTION r)))
`:filter-args'  (lambda (&rest r) (apply OLDFUN (funcall FUNCTION r)))
`:filter-return'(lambda (&rest r) (funcall FUNCTION (apply OLDFUN r)))

(cited from `C-h f add-function')

其中FUNCTION是建议功能,而OLDFUN是添加建议的功能。不要试图一次理解所有这些,只需选择一个WHERE听起来合适的符号并尝试理解该符号即可。

或只是使用:around。据我所知,WHERE:around所有内容上使用Specialized s 的唯一优势是,C-h f ADVISED-FUNCTION 在阅读建议的文档字符串之前,通过查找可以获得更多信息。除非您计划发布包含建议的代码,否则可能没有关系。

命名建议功能

我建议使用命名函数作为建议,因为它具有许多优点(其中一些优点也适用于对挂钩使用命名函数):

  • 它显示C-h f find-file

    :around advice: `my-find-file-advice-print-arguments'
    

    链接到建议功能的定义,该功能通常包含指向定义它的文件的链接。如果建议已lambda直接以advice-add 表单的形式定义为表单,则该文档字符串将内联显示(长文档字符串是否乱七八糟?),并且没有任何内容指示该文档的定义位置。

  • 您可以通过以下方式删除建议

    (advice-remove #'find-file #'my-find-file-advice-print-arguments)
    
  • 您可以更新建议的定义,而无需重新运行 advice-add或冒使旧版本保持活动状态的风险(因为在运行 advice-add更改lambda后将被识别为新建议,而不是对旧建议的更新)。

旁注#'function符号基本上与等效 'function,除了它有助于字节编译器将符号标识为函数名称,从而标识缺失的函数(例如,由于拼写错误)。


按照该讨论我与Monnier的斯蒂芬,哈希引号不应该在这里所有的参数使用。应该(advice-add 'find-file :around #'my-find-file-advice-print-arguments)和类似(advice-remove 'find-file #'my-find-file-advice-print-arguments)
Kaushal Modi

我想这advice-add是一个边境案件。我个人认为该' ↔ #'区别主要是帮助识别函数名称中的拼写错误,因此在这里它可能取决于是否希望在添加建议时定义该函数。
kdb

@kdb我最终为我自己找到了这个(在我遇到的文档后add-function)。我希望文档能更清楚地说明这一点。我可能希望为此做一个补丁。
PythonNut

@kdb您的意思是“它显示在C-h f find-file,而不是C-x
Peeja

@Peeja是的,已更正它。
kdb
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.