1

I am new to elisp. That's why I'd be glad if some of you could help me out.

What I want to achieve

I want to enable hl-line-mode and display-line-number-mode in all current and future buffers derived from prog-mode, text-mode, conf-mode and dired-mode. I also want to be able to toggle this functionality. By default I want this to be turned off. I just want to enable this from time to time.

I'll outline the high level way I thought about achieving this.

Adding functionality

  • Add mode hooks to enable functionality in future buffers. (1)
  • Apply buffer-local minor mode functions to all buffers derived from specific modes. (2)

Removing functionality

  • Remove mode hooks such that future buffers don't have it. (3)
  • Apply buffer-local minor mode functions to all buffers derived from specfic modes to toggle/disable functionality activated in step 2 above. This step might the same as step 2 above because hl-line-mode and display-line-number-mode provide toggling. (4)

I know how to add and remove hooks but I don't know how implement steps 2 and 4. Especially checking whether buffers are in a mode derived from a specific major mode or not.

I'd be really happy to receive some help on how to go about it.

Thanks in advance!

EDIT: Just to clarify what I actually want, if I wasn't precise enough:

  1. Line numbers and line highlighting should be disabled when I launch emacs.
  2. When the need arises, I want to call a function interactively that immediately turns on display-line-numbers-mode and hl-line-mode in all current buffers that match the requirements specified in my original post. With all current buffers I mean all buffers selectable via switch-to-buffer. Furthermore this function should also modify prog-mode-hook, text-mode-hook and so on, such that all buffers matching the requirements that I open in the future have display-line-numbers-mode and hl-line-mode enabled.
  3. When I don't need line numbers and line highlighting anymore I want to call a different interactive function such that display-line-numbers-mode and hl-line-mode get turned off immediately in all current buffers that match the requirements. Furthermore this function should also remove previously added functions from prog-mode-hook, text-mode-hook and so on, such that all buffers matching the requirements that I open in the future do not have display-line-numbers-mode and hl-line-mode enabled.

This is just my naive way of thinking about the problem. More knowledgeable people might be able to implement equivalent functionality in a better, more idiomatic way.

ANOTHER EDIT: I got it working. It's ugly though:

I got it working exactly how I want it to work. It's very ugly and the code can probably be shortened. Especially the parts adding/removing hooks. Any suggestions on how to do so?

(defun my/activate ()
  (interactive)
  (add-hook 'dired-mode-hook 'hl-line-mode)
  (add-hook 'dired-mode-hook 'display-line-numbers-mode)
  (add-hook 'prog-mode-hook 'hl-line-mode)
  (add-hook 'prog-mode-hook 'display-line-numbers-mode)
  (add-hook 'conf-mode-hook 'hl-line-mode)
  (add-hook 'conf-mode-hook 'display-line-numbers-mode)
  (add-hook 'text-mode-hook 'hl-line-mode)
  (add-hook 'text-mode-hook 'display-line-numbers-mode)
  (dolist ($buf (buffer-list))
    (with-current-buffer $buf
      (when (derived-mode-p 'dired-mode 'prog-mode 'conf-mode 'text-mode)
        (lambda)
        (hl-line-mode)
        (display-line-numbers-mode)))))
(defun my/deactivate ()
  (interactive)
  (remove-hook 'dired-mode-hook 'hl-line-mode)
  (remove-hook 'dired-mode-hook 'display-line-numbers-mode)
  (remove-hook 'prog-mode-hook 'hl-line-mode)
  (remove-hook 'prog-mode-hook 'display-line-numbers-mode)
  (remove-hook 'conf-mode-hook 'hl-line-mode)
  (remove-hook 'conf-mode-hook 'display-line-numbers-mode)
  (remove-hook 'text-mode-hook 'hl-line-mode)
  (remove-hook 'text-mode-hook 'display-line-numbers-mode)
  (dolist ($buf (buffer-list))
    (with-current-buffer $buf
      (when (derived-mode-p 'dired-mode 'prog-mode 'conf-mode 'text-mode)
        (lambda)
        (hl-line-mode -1)
        (display-line-numbers-mode -1)))))
2
  • You can define a variable with the list of all the modes as value and then loop over that to add/remove hooks, as well as use it when checking for derived modes. Other than that, I think your solution is fine.
    – NickD
    Mar 19 at 21:05
  • What's with the (lambda) in both functions? That doesn't do anything: just delete it.
    – NickD
    Mar 19 at 21:07

2 Answers 2

0

You can use a "globalized" minor mode, which is the pairing of a buffer local minor mode with a global mode which controls the state of that buffer-local minor mode en-masse in selected buffers (including any future buffers matching the criteria). Globalized modes act during after-change-major-mode-hook, which is later than the major mode's own hook.

(define-globalized-minor-mode global-my-mode my-mode my-mode
  :init-value nil
  :predicate '(prog-mode text-mode conf-mode dired-mode))

(define-minor-mode my-mode
  "Enable line numbering and highlighting."
  :init-value nil
  :global nil
  (let ((state (if my-mode 1 0)))
    (hl-line-mode state)
    (display-line-numbers-mode state)))

Then just use M-x global-my-mode to toggle it.

If you're using Emacs 27 or earlier you can't use :predicate and instead you'll need to specify a separate TURN-ON function in the globalized mode definition:

(defun my-mode-turn-on ()
  "Maybe turn on `my-mode'."
  (when (derived-mode-p 'prog-mode 'text-mode 'conf-mode 'dired-mode)
    (my-mode 1)))
1
  • You could alternatively use this approach to create two custom global modes, directly controlling hl-line-mode and display-line-numbers-mode respectively (rather than defining your own buffer-local mode). It seems simpler to me to combine all of the desired functionality into a single mode, however. If you prefer that alternative though, make sure you still use sane non-conflicting naming (bearing in mind that both of the minor modes being controlled already have associated global modes by default).
    – phils
    Mar 19 at 23:19
0

Here comes derived-mode-p function to your rescue.

(defun my-unset-minor-modes-hook ()
  (when (derived-mode-p 'prog-mode 'text-mode 'conf-mode 'dired-mode)
    (hl-line-mode -1)
    (display-line-number-mode -1)))
(add-hook 'after-change-major-mode-hook 'my-unset-minor-modes-hook)

You can alternatively consider change-major-mode-hook in the place of after-change-major-mode-hook to add your hook to.
[Edit] As phils noted in comments, this may do the work but in spirit, change-major-mode-hook is meant to do cleanup operations by the exiting mode & after-change-major-mode-hook is the more suited candidate to do mode initialising operations. So, it is wise to add the hook to after-change-major-mode-hook.

3
  • 1
    @Saravana: Good answer, but I'm confused by the change-major-mode-hook mention -- that one runs while the previous mode is still active. It's usually used to enable a major mode to clean up after itself just before some other mode takes its place. You certainly couldn't use it in place of after-change-major-mode-hook.
    – phils
    Mar 19 at 22:12
  • Gosh! the names din't tell the difference, at least to me. thanks for clarifying, phils.
    – Saravana
    Mar 20 at 3:30
  • 1
    You're welcome. You might find stackoverflow.com/a/19295380/324105 to be a worthwhile read.
    – phils
    Mar 20 at 8:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.