14

There are certain things like the display of trailing whitespace, display of buffer boundaries, rainbow-colored delimiters and many more I'd like to enable in most modes or rather, all programming- and text-related ones (because displaying trailing spaces in, say ediff-mode, would clash with the major mode) instead of using a globalized minor mode with exception rules. Since most modes are derived from either prog-mode or text-mode, adding the function in question to both prog-mode-hook and text-mode-hook does work, however there are enough modes not following this specification, such as css-mode or LaTeX-mode.

I'd like to define a hook that encompasses all these modes for the time being to only add functions to one hook. Let's call it non-special-mode-hook (to distinguish it from special-mode-hook). How would I be able to create such a hook that is run for all major modes it is made for?

Malabarba
  • 23,148
  • 6
  • 79
  • 164
wasamasa
  • 22,178
  • 1
  • 66
  • 99

7 Answers7

20

Just group those settings together into a function, and add that function to all of the relevant hook functions:

(defun my-non-special-mode-setup ()
  (setq show-trailing-whitespace t)
  ...)
(dolist (hook '(prog-mode-hook text-mode-hook css-mode-hook ...))
  (add-hook hook 'my-non-special-mode-setup))

No other way to do this would be more concise: whatever happens, somewhere you're going to have either a whitelist or blacklist of modes. In practice, you'll only find a few modes which need adding to the list, so I'd suggest just keeping things simple.

In my own config, I use exactly this trick to unify the configuration of several lisp-oriented modes.

Malabarba
  • 23,148
  • 6
  • 79
  • 164
sanityinc
  • 2,921
  • 14
  • 17
  • Thank you, I think I'll adapt this solution for other useful groups (such as the lispy ones), too. – wasamasa Sep 30 '14 at 13:04
  • 1
    That's exactly what I do myself: https://github.com/purcell/emacs.d/blob/2a3792dfdcbd8f38b7a28bfe6cc04d7d086e7b61/lisp/init-lisp.el#L160 – sanityinc Sep 30 '14 at 13:07
  • 1
    Excellent, could you maybe add the URL to your answer? I believe the highlighted snippet not only explains the actual usage very well, but also demonstrates further usage, such as using an Emacs Lisp related "hook" as subset of a bigger "hook" and how one would automatically create hook names from existing major modes with derived-mode-hook-name. – wasamasa Sep 30 '14 at 13:18
  • I like this more than mine, just for the (dolist ...) vs (add-hook ...) . I'd only stick with mine when keeping mode-specific changes separate (file-per-mode, use-package, el-get). Both are all-in-one-place solutions, but from different points-of-view. – Jonathan Leech-Pepin Sep 30 '14 at 14:34
  • Be aware that if you do not provide a non-nil APPEND argument to add-hook here then the hooks will be invoked in the reverse order from the list order you provide. Nothing wrong with that; just be aware of it, in case the order matters. – Drew Sep 30 '14 at 22:41
5

You could add a function to after-change-major-mode-hook, that checks whether the new mode is an interesting one (possibly through (not (derived-mode-p 'special-mode))), and if so runs non-special-mode-hook.

legoscia
  • 6,072
  • 30
  • 54
  • This sounds pretty nifty (once one has figured out all "unspecial" modes since there are a few that just use fundamental-mode, but do more than just displaying text), but a bit too magic for my taste. Hence the upvote. – wasamasa Sep 30 '14 at 14:40
4

I found myself often doing @sanityinc's pattern of wrapping my settings and minor mode activations in a defun and looping through hooks to call it, but I wanted a cleaner approach so I wrote this macro:

(defmacro hook-modes (modes &rest body)
  (declare (indent 1))
  `(--each ,modes
     (add-hook (intern (format "%s-hook" it))
               (lambda () ,@body))))

Note: I'm dash.el for cleanliness but it could easily be adapted to use (dolist).

Then you can define grouped modes as list variables and use it like so:

(defvar progish-modes
  '(prog-mode css-mode sgml-mode))

(hook-modes progish-modes
  (highlight-symbol-mode)
  (highlight-symbol-nav-mode))
waymondo
  • 1,384
  • 11
  • 16
2

Rather than define a new hook that works for all these non-derived modes you can do the following.

(defun run-my-hooks ()
  "Run all the following functions in the hook"
  (smartparens-mode 1)
  (whitespace-mode 1)
  (my-needed-mode 1)
  ...)

(add-hook 'specific-mode-hook 'run-my-hooks)
(add-hook 'another-mode-hook 'run-my-hooks)

You'll still need to add it to all the modes, but by defining your function to include all the modes you will only have to change one definition when adding/removing addtional features.

Jonathan Leech-Pepin
  • 4,357
  • 1
  • 20
  • 32
1

Following up on @legoscia's answer with some code, I solved the same problem as follows. Note the addition of a check for fundamental-mode, since otherwise the hook will trigger on mini-buffers as well.

(defcustom non-special-mode-hook nil
  "Hook run when entering non-special modes (catches 'prog-mode', 'text-mode', and 'fundamental-mode')."
  :type 'hook)

(add-hook 'after-change-major-mode-hook (lambda () "Trigger non-special-mode hooks that are also fundamental, so don't catch minibuffers." (when (and (not (derived-mode-p 'special-mode)) (derived-mode-p 'fundamental-mode)) (run-hooks 'non-special-mode-hook))))

From there you can hook into 'non-special-mode-hook like normal, or change the predicates in the when call to narrow the scope of which modes trigger the hook.

Drew
  • 77,472
  • 10
  • 114
  • 243
0

Here is my overkill:

(defun my/hooks-hooks (hook-or-hooks mode-or-modes)
       (if (listp hook-or-hooks)
           (dolist (hook hook-or-hooks)
             (add-hook hook mode-or-modes))
         (if (listp mode-or-modes)
             (dolist (hook hook-or-hooks)
             (add-hook hook mode-or-modes))
           ;; else
           (throw 'error "my/hooks-hooks")
           )))
(my/hooks-hooks 'python-mode-hook '('ECFPAW/prettify-symbols-pack/asterik-to-multiplication
                                    'ECFPAW/prettify-symbols-pack/in-equalities
                                    'ECFPAW/prettify-symbols-pack/lambda
                                    'ECFPAW/prettify-symbols-pack/pointers))
-2

Maybe you can try this:

(setq lisp-dialects-mode-hook '(lisp-mode-hook
                            lisp-interaction-mode-hook
                            emacs-lisp-mode-hook
                            ;; common-lisp-mode-hook
                            scheme-mode-hook
                            clojure-mode-hook
                            cider-repl-mode-hook
                            ))

(add-hook 'lisp-dialects-mode-hook 'func)
stardiviner
  • 1,928
  • 28
  • 46