9

Such as in the built-in variable mode-line-modified which displays an asterisk in the mode line if the buffer is modified.

Specifically, I want to turn hl-line-mode off when the buffer is modified and on when it's not.

I've read the documentation about "Change Hooks", but I want hl-line-mode to toggle when I undo a change like the mode line asterisk.

nanny
  • 5,704
  • 18
  • 38
  • 3
    [Relevant hooks](http://www.gnu.org/software/emacs/manual/html_node/elisp/Change-Hooks.html) appear to be `after-change-functions` and `first-change-hook`. If, for some reason, undoing from a modified buffer to an unmodified one doesn't count as a "change" for these hooks, you could advise `undo` to perform the toggle. – Dan Dec 04 '14 at 17:10
  • See also the function `buffer-modified-p` to test for whether a modification has occurred. I am using the `after-save-hook` and `first-change-hook` to toggle a modified indicator on my tabbar. – lawlist Dec 04 '14 at 17:43
  • 3
    if the change-hooks don't work perfectly when undoing, the surest way is a `post-command-hook` that checks `buffer-modified-p`. – Malabarba Dec 04 '14 at 21:42

1 Answers1

6

Building off of the comments, here are two ways to achieve what you're trying to do. (Not extensively tested, so YMMV.)

Option 1: post-command-hook

Create a function that tests whether or not the buffer is modified, and then hook it into post-command-hook:

(defun hl-line-mode-toggle-maybe ()
  "Turn on `hl-line-mode' when buffer is unmodified, turn it off
when it is modified."
  (hl-line-mode (if (buffer-modified-p) -1 1)))

(add-hook 'post-command-hook #'hl-line-mode-toggle-maybe)

The advantage is that it's pretty simple. The disadvantage is that Emacs will run this test after every single command, which seems like overkill.

Option 2: first-change-hook, after-save-hook, and undo

Create a function to turn hl-line-mode off and hook it into first-change-hook. Create a function to turn hl-line-mode on and hook it into after-save-hook. Provide after advice on undo such that it turns hl-line-mode on when an undo had returned the buffer to an unmodified state:

(defun hl-line-mode-off ()
  "Turn off `hl-line-mode'."
  (hl-line-mode -1))

(defun hl-line-mode-on ()
  "Turn off `hl-line-mode'."
  (hl-line-mode 1))

(add-hook 'first-change-hook #'hl-line-mode-off)
(add-hook 'after-save-hook   #'hl-line-mode-on)

(defadvice undo (after hl-line-when-unmodified activate)
  (unless (buffer-modified-p)
    (hl-line-mode 1)))

The advantage is that it calls these functions infrequently rather than after every command. The disadvantage is that you've had to delve into advice, and there may be other, unanticipated ways to get a buffer to an unmodified state that don't trigger the "turn hl-line-mode back on" step.

Dan
  • 32,584
  • 6
  • 98
  • 168
  • Ahh, yes, thanks for putting that into an answer. I ended up using the second option so as not to use a `post-command-hook`. – nanny Dec 05 '14 at 18:09
  • 1
    `revert-buffer` seems like another common case that would put the buffer in to an unmodified state. You could use the `after-revert-hook` to handle this one. – glucas Dec 05 '14 at 20:08
  • You'd also need some way to turn on highlighting for newly opened buffers -- perhaps the `after-change-major-mode-hook`? – glucas Dec 05 '14 at 20:18
  • 1
    Note that `add-hook` has a `local` parameter, so if you just want to override the behaviour in certain major modes or buffers, you can. – Wilfred Hughes Jul 29 '16 at 03:53