如何同时将钩子应用于多个Emacs模式?

8
我正在阅读一篇关于良好格式化的Git提交记录的文章,并且想知道如何将其中一些规则应用于Magit日志模式。
看起来它同时使用了3种主要模式: Magit Log Edit
那么,当它们同时使用时,我该如何自动使它们在72个字符处自动硬换行?

标题有误导性。问题的主体与标题不同。 - mihai
6个回答

10

回答原来的问题,如果你有一个单一的函数需要添加到多个钩子变量中,你可以这样做:

(defun my-add-to-multiple-hooks (function hooks)
  (mapc (lambda (hook)
          (add-hook hook function))
        hooks))

(defun my-turn-on-auto-fill ()
  (setq fill-column 72)
  (turn-on-auto-fill))

(my-add-to-multiple-hooks
 'my-turn-on-auto-fill
 '(text-mode-hook
   magit-log-edit-mode-hook
   change-log-mode-hook))

可能不是最好的例子,但我有一些类似的代码片段用于启用编程模式中的一些常见行为,还有很多要列出来。


6

Emacs模式有“基础模式”,也就是“bade modes”。例如python-mode扩展了prog-mode,而prog-mode又扩展了fundamental-mode。所有模式都扩展fundamental-mode。因此,如果您想挂接python-modec-mode但不挂接text-mode,则可以挂接prog-mode


4

在Emacs缓冲区中只能有一个主要模式(除非您使用类似于MMM或MuMaMo之类的工具)。在您的情况下,那个主要模式是magit-log-edit-mode,它由三个单词组成("Magit Log Edit")。您可以随意添加任何钩子:

(defun my-turn-on-auto-fill ()
  (setq fill-column 72)
  (turn-on-auto-fill))

(add-hook 'magit-log-edit-mode-hook 'my-turn-on-auto-fill)

非常好,谢谢!顺便提一下,我对于主要模式感到有些困惑 - 每个模式在点击时都会弹出一个单独的“上下文菜单”。无论如何,你的解决方案完美地解决了这个问题。 - Brad Wright
1
我强烈建议不要在钩子中添加lambda表达式。一个原因是当你研究它的内容时,很难看出钩子包含了什么。另一个原因是当你修改钩子时,新版本和每个旧版本都存在(除非你仔细删除旧版本),这可能会导致有趣的效果。 - Lindydancer
@Lindydancer 我完全同意,但这只是为了演示目的。无论如何,已更改为单独的函数。 - Victor Deryagin
这并没有回答这篇帖子标题中所述的问题。 - mihai

4

一般而言,你可以定义一个自己的函数,比如my-common-hook,并将其添加到所有主要的模式中,例如:

(defun my-common-hook ()
   ... do stuff ...
   )
(add-hook 'one-mode-hook 'my-common-hook)
(add-hook 'another-mode-hook 'my-common-hook)
(add-hook 'a-third-mode-hook 'my-common-hook)

1

我现在才看到这个,但这是我所做的。最终结果是我想要做以下事情:(hook-up-modes my-lisps 'standard-lisp-environment)

为了实现这一点,我定义了以下defvar

(defvar my-lisps  "clojure lisp emacs-lisp cider-repl")
(defun standard-lisp-environment ()
  (paredit-mode 1)
  (rainbow-delimiters-mode 1)
  (eldoc-mode 1))

我希望将lisp append-mode-hook添加到我使用的lisp中,使其如下所示:

(defun append-suffix (suffix phrases)
  "take SUFFIX and append it to each of the PHRASES."
  (mapcar #'(lambda (phrase) (concat phrase suffix)) phrases))

所以 ("clojure" "lisp") => ("clojure-mode-hook" "lisp-mode-hook")。

现在我们可以轻松获取它们,我们需要它们的读取器符号,这可以从中轻松获得

(defun symbols-from-strings (strings)
  "Given a list of strings, get their symbol values"
  (mapcar #'intern strings))

最后,我们有与上面发布的类似表单:

(defun multiple-mode-add-hook (modes hook)
  "Given a list of x-mode-hook symbols in MODE, add the HOOK to them."
  (mapc (lambda (mode) (add-hook mode hook)) modes))

所有这些操作都是在适合它们的类型上进行的,比如字符串列表、符号列表 'blah-mode-hook 等。因此,现在我们需要一个好用的用户界面函数来处理。

(defun hook-up-modes (strings hook)
  (let ((modes (symbols-from-strings
                (append-suffix "-mode-hook" (split-string strings)))))
    (multiple-mode-add-hook modes hook)))

现在这应该很易读了:我们从一个以空格分隔的字符串列表创建我们的模式,并将钩子应用于它。另外,由于我定义了一个标准的Lisp环境,所有我的Lisps行为类似,如果需要,我可以很容易地稍后删除钩子。然后实际工作的代码是超级简单的短语(hook-up-modes my-lisps 'standard-lisp-environment)

开始时使用字符串而不是列表可能有点奇怪。一个符号列表可能是一个更常见的起点。在处理时,您可以使用symbol-name来获取与之连接的字符串等效项。 - phils

-2
(dolist (mode-hook '(org-mode-hook
               term-mode-hook))
  (add-hook mode-hook (lambda () (display-line-numbers-mode 0))))

1
“纯代码答案并不是高质量的回答”。虽然这段代码可能很有用,但是您可以通过解释它是如何工作的,为什么要使用它以及其局限性是什么来改进它。请修改您的答案,包括说明和链接到相关文档。 - Stephen Ostermiller

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接