How can I apply a hook to multiple Emacs modes at once?
Asked Answered
E

6

8

I was reading an article about well-formatted Git commits, and I was wondering how I could apply some of the rules to the Magit log mode.

It seems to use 3 major modes simultaneously: Magit, Log, Edit.

So how would I get just those modes, when used together, to hard-wrap at 72 characters automatically?

Entice answered 13/9, 2011 at 7:23 Comment(1)
Title is misleading. The body of the question is different than the title.Schottische
E
4

There can be only one major mode in Emacs buffer (unless you are using something like MMM or MuMaMo). In your case that one major mode is magit-log-edit-mode, whose name consists of three words ("Magit Log Edit"). You can just add to it whatever hook you like:

(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)
Ellenaellender answered 13/9, 2011 at 7:36 Comment(4)
Perfect, thanks! As a tangent, I was confused about the major modes - each of them pops a separate "context menu" on click. Either way, your solution works perfectly.Entice
I would strongly advice against adding lambda-expression to hooks. One reason is that it's really hard to see what a hook contains when studying it's content. Another reason is that when you are modifying your hook, both the new and every old version are present (unless you carefully remove the old version), which could lead to interesting effects.Albertina
@Albertina i totally agree, but this was just for demonstration purpose. Anyway, changed to separate function.Ellenaellender
This does not answer the question as stated in the title of this post.Schottische
A
10

In answer to the original stated question, if you have a single function to add to numerous hook variables, you could do it like this:

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

Not the best example, perhaps, but I have something similar for some common behaviours I want enabled in programming modes, of which there are a great many more to list.

Agential answered 13/9, 2011 at 10:37 Comment(0)
O
6

Emacs modes have "base modes" which is to say bade modes. For example python-mode extends prog-mode which itself extends fundamental-mode. All modes extend fundamental-mode. So to hook python-mode plus c-mode but not text-mode, you could hook prog-mode.

Opt answered 13/9, 2011 at 16:13 Comment(0)
E
4

There can be only one major mode in Emacs buffer (unless you are using something like MMM or MuMaMo). In your case that one major mode is magit-log-edit-mode, whose name consists of three words ("Magit Log Edit"). You can just add to it whatever hook you like:

(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)
Ellenaellender answered 13/9, 2011 at 7:36 Comment(4)
Perfect, thanks! As a tangent, I was confused about the major modes - each of them pops a separate "context menu" on click. Either way, your solution works perfectly.Entice
I would strongly advice against adding lambda-expression to hooks. One reason is that it's really hard to see what a hook contains when studying it's content. Another reason is that when you are modifying your hook, both the new and every old version are present (unless you carefully remove the old version), which could lead to interesting effects.Albertina
@Albertina i totally agree, but this was just for demonstration purpose. Anyway, changed to separate function.Ellenaellender
This does not answer the question as stated in the title of this post.Schottische
A
4

In general, you could define your own function, say my-common-hook and add it to all the major modes, for example:

(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)
Albertina answered 13/9, 2011 at 7:51 Comment(0)
R
1

Just seeing this now, but here's what I've done. The end result is that i want to do the following: (hook-up-modes my-lisps 'standard-lisp-environment).

To do this, i define the following defvars.

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

I want to have lisp append -mode-hook to the lisps i use so i have the following:

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

so that ("clojure" "lisp") => ("clojure-mode-hook" "lisp-mode-hook").

Now that we could easily have these, we need their reader symbols, which we easily get from

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

And then finally we have the similar form posted above:

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

These all operate on the type that makes sense for them, list of strings, list of symbols 'blah-mode-hook, etc. So now we need a nice user facing function that we can work with.

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

Now this should be pretty legible: We create our modes from a space delimited list of strings and apply the hook to it. Also, since I've defined a standard-lisp-environment, all my lisps behave similarly, and I can easily remove the hook later if I like. Then the code that actually does work is the super simple phrase (hook-up-modes my-lisps 'standard-lisp-environment).

Reactant answered 9/4, 2016 at 4:24 Comment(1)
Starting out with a string instead of a list is pretty weird. A list of symbols would be a more usual starting point. You can use symbol-name to get the string equivalent to concatenate with, when processing.Agential
W
-2
(dolist (mode-hook '(org-mode-hook
               term-mode-hook))
  (add-hook mode-hook (lambda () (display-line-numbers-mode 0))))
Wolfenbarger answered 7/2, 2023 at 11:6 Comment(1)
A code-only answer is not high quality. While this code may be useful, you can improve it by saying why it works, how it works, when it should be used, and what its limitations are. Please edit your answer to include explanation and link to relevant documentation.Cree

© 2022 - 2024 — McMap. All rights reserved.