【问题标题】:Advising an emacs interactive function: before建议一个emacs交互功能:之前
【发布时间】:2013-01-30 14:12:13
【问题描述】:

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

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

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

更新。注意defadvice 宏是deprecated

【问题讨论】:

标签: emacs defadvice advising-functions


【解决方案1】:

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

documentation for advice 缺少许多有助于解决这种情况的细节,因此更好的查看位置实际上是源:advice.el。看看那个并找到评论部分@ Foo games: An advice tutorial。您还可以使用 M-x find-library advice RET 在 Emacs 中找到源代码。

具体来说,对于这个问题,请查看 advice.el 中标记为 @@ Advising interactive behavior: 的部分 - 因为这正是您想要做的。

如果您仔细阅读,您会发现建议不需要必须采用around 的形式,但也可以是before,也可以是@987654339 @ - 虽然那只是自找麻烦。这是因为 interactive 被(并且必须)被特殊对待。

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

(defadvice find-dired (before eab-find-dired (dir args) activate)
  "ignore find-args, hard code \"-iname '**'\""
  (interactive
   (list (read-directory-name "Run find in directory: " nil "" t)
         (read-string "Run find (with args): " '("-iname '**'" . 10)
                      '(find-args-history . 1)))))

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

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

【讨论】:

  • 感谢您的指导和准确的回答!
  • defadvice 很危险,因为它在 wiki 上被过度使用。新手很容易认为这是自定义 Emacs 并最终从根本上更改 Emacs API 的唯一方法,这会导致难以调试的错误报告给包维护人员。浪费大家的时间。
  • 对于新读者,请注意defadvice 已弃用,您现在应该使用add-functionadd-advice (and their companion functions)
【解决方案2】:

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

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

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

当然,如果您认为此设置适合所有对find-dired 的调用,您也可以简单地执行以下操作:

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

【讨论】:

  • 谢谢你的回答,我同意这样的例子。但我想讨论一些类似交互形式的修改:请参阅我的答案。
  • @Lindydancer 让绑定阻止find-dired 将上次使用的值保存到find-args。您可能想改用setq
  • 啊,对。好吧,在这种情况下,人们可能会争论是否应该对其进行修改。也许最好只使用setq 设置它或定义类似my-new-find-dired 的东西,在调用find-dired 之前恢复它。无论如何,没有简单的解决方案,因为这一切都开始取决于用户的偏好——你想要最后输入的表达式还是确保表达式总是被恢复。
【解决方案3】:

您为什么要建议交互功能?

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

(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

【讨论】:

  • 感谢您的回答。如果我有很多键映射,我必须在每个键映射中重新映射。我同意:在 99% 的情况下,这样的解决方案就足够了。
【解决方案4】:

有效:

(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)

我使用函数find-diredinteractive form 进行替换:直接在表单中放入必要的表达式'("-iname '**'" . 10) 而不是find-args。使用around-advice(dir args) 参数代替before-advice

【讨论】:

  • 我使用 Emacs 已经有一段时间了。这让我学会了不要复制粘贴不必要的东西,因为当 Emacs 发生变化时,这往往会破坏。
  • defadvice 是危险的,即使在这种情况下不是;这仍然是一个危险的习惯。除非绝对必要,否则不要使用它。
  • 我在必要的情况下使用了一些带有建议的函数。例如。内部函数,如 ac-inline-show,在另一个函数内部调用:它们的名称已经绑定。
【解决方案5】:

我遇到了这个问题,寻找一种简单的方法来扩展 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)

在这种简单的情况下,与通知功能相关的开销很小,但对于更实际的情况,尽可能高效地检查通知是否应该有效可能很重要。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多