建议使用emacs交互功能:之前

时间:2013-01-30 14:12:14

标签: emacs defadvice advising-functions

我想在之前建议一些使用交互式参数的函数,例如: find-dired

(defadvice find-dired (before eab-find-dired activate)
  (message "before!")
  (setq find-args '("-iname '**'" . 10)))

但emacs仅在find-dired交互式会话后执行此建议,而我之前无法设置find-args。如何解决矛盾?

UPD。请注意,defadvice宏是deprecated

5 个答案:

答案 0 :(得分:8)

artscan用一个可行的答案回答了他自己的问题,但它有点不完整和误导。这也涉及'interactive,它本身可能令人困惑 - 因为它看起来像是在命令体内定义的,但实际上在输入函数之前使用了 - 并且在执行任何建议之前(除非该建议有'interactive个电话......)

documentation for advice缺少一些有助于这种情况的细节,因此更好的观察点实际上是来源:advice.el。看一下,找到评论部分@ Foo games: An advice tutorial。您还可以使用 M-x查找库建议RET 在Emacs中找到源代码。

具体而言,对于此问题,请查看标有advice.el的{​​{1}}部分 - 因为这正是您要做的事情。

如果你仔细阅读,你会发现的建议必须是@@ Advising interactive behavior:形式,但也可以是around。可以before - 虽然这只是在寻找麻烦。这是因为after是(并且必须)处理特殊。

因此,以下代码有效(请注意interactive):

before

正如其他人建议的那样,可能更简洁的方法就是编写自己的函数,我认为最简单的方法是Lindydanceranswer

建议是一个非常诱人的工具,但很容易过度使用。我不会说它很危险,但应该谨慎使用。当编写自己的函数不起作用时,似乎最好使用它 - 例如,更改由您无法修改的代码调用的函数的行为。我认为这种情况的好例子可以在hereherehere找到(我自己的号角)。

答案 1 :(得分:4)

Emacs在调用函数之前会选择interactive规范。

一般来说,使用defadvice是一个坏主意,所以我建议您定义自己的函数并将其绑定到适当的密钥。例如:

(defun my-find-dired ()
  (interactive)
  (let ((find-args '("-iname '**'" . 10)))
    (call-interactively 'find-dired)))

当然,如果您认为此设置适用于find-dired的所有来电,您也可以执行以下操作:

(setq find-args '("-iname '**'" . 10))

答案 2 :(得分:2)

为什么要建议互动功能?

您可以轻松定义自己的命令

(defun find-dired-my-defaults (dir args)
  "just like `find-dired' but with defaults."
  (interactive
   (list (read-directory-name "Run find in directory: " nil "" t)
         (read-string "Run find (with args): " '("-iname '**'" . 1)
                      '(find-args-history . 1))))
  (find-dired dir args))

如果它已绑定在键盘映射中,您可以轻松地重新映射它:

(define-key foo-mode-map [remap find-dired] 'find-dired-my-defaults)

尽管wiki告诉你,最终用户几乎不必使用defadvice

编辑:@ Lindydancer的答案在这个例子中更好,但我会在这里留下这个答案,以阻止未来的读者以这种人为的方式使用defadvice

答案 3 :(得分:1)

有效:

(defadvice find-dired (around eab-find-dired (dir args) activate)
  (interactive
   (list (read-directory-name "Run find in directory: " nil "" t)
         (read-string "Run find (with args): " '("-iname '**'" . 10)
                      '(find-args-history . 1))))
  ad-do-it)

我使用函数interactive form的{​​{1}}替换:在表单中直接放置必要的表达式find-dired而不是'("-iname '**'" . 10)。使用带有参数find-args的{​​{1}}代替around-advice

答案 4 :(得分:0)

我遇到了这个问题,寻找一种简单的方法来扩展eval-last-sexpdescribe-function和类似函数的交互行为,而无需为每个函数编写专门的通知函数。为此,我使用toggle-debug-on-error和虚函数(defun x () (interactive (list :interactiveform (error "Int"))))分析了交互式使用的调用堆栈。

  • 交互式使用始终涉及对call-interactively的调用。
  • 按热键执行命令后,命令将传递给command-execute,调用call-interactively
  • 当执行M-x COMMAND时,函数名首先作为字符串传递给execute-extended-command(这是M-x调用的命令),然后调用command-execute, ...

取决于interactive表单的所有调用是否应受建议影响,或仅受直接热键或M-x调用的影响,可以建议其中一个函数,例如:使用现代nadvice.el界面:

(defun setup-var-advice (oldfun command &rest r)
  (if (eq command 'save-buffer)
      (let ((myvar t)) (apply oldfun command r)) ;; Advice sets up variable
    (apply oldfun command r))) ;; Advice has no effect

(advice-add #'call-interactively :around #'setup-var-advice)

在这种简单的情况下,与建议功能相关的开销很小,但对于更现实的情况,尽可能高效地保持检查,建议是否应该产生效果可能很重要。