I'm using hl-mode as a minor mode for deft. How do I make the highlighted line change color (e.g. to gray) when the deft window isn't the current window, and then back to the default highlight color when the deft window becomes the current window again?
Asked
Active
Viewed 943 times
13
-
It turns out there is an option for this, see http://emacs.stackexchange.com/a/15141/780. – glucas Aug 28 '15 at 12:57
2 Answers
5
I've implemented this for hl-line-mode
using the buffer-list-update-hook
.
Here's the code:
(defface hl-line-inactive
'((t nil))
"Inactive variant of `hl-line'."
:group 'hl-line)
(defun hl-line-update-face (window)
"Update the `hl-line' face in WINDOW to indicate whether the window is selected."
(with-current-buffer (window-buffer window)
(when hl-line-mode
(if (eq (current-buffer) (window-buffer (selected-window)))
(face-remap-reset-base 'hl-line)
(face-remap-set-base 'hl-line (face-all-attributes 'hl-line-inactive))))))
(add-hook 'buffer-list-update-hook (lambda () (walk-windows #'hl-line-update-face nil t)))
What this code is doing:
- Define a new face
hl-line-inactive
to be used in inactive windows. You can useM-x customize-face
to modify the attributes of this face to your taste. - Define a function to temporarily remap the highlighting face in inactive windows. A window is considered inactive if it is not displaying the same buffer as the currently selected window.
- Add a hook to
buffer-list-update-hook
that callshl-line-update-face
for all the visible windows.
Old answer
The code above (which I'm using in my own init
file) is much simpler than what I originally posted. Thanks @Drew for the suggestion to use walk-windows
. I also read more about face remapping (see Face Remapping in the Emacs Lisp Manual) and realized I could remove a lot of the code.
For posterity, here's what I originally posted:
;; Define a face for the inactive highlight line.
(defface hl-line-inactive
'((t nil))
"Inactive variant of `hl-line'."
:group 'local)
(defun toggle-active-window-highlighting ()
"Update the `hl-line' face in any visible buffers to indicate which window is active."
(let ((dups))
(mapc
(lambda (frame)
(mapc
(lambda (window)
(with-current-buffer (window-buffer window)
(when hl-line-mode
(make-local-variable 'face-remapping-alist)
(let ((inactive (rassoc '(hl-line-inactive) face-remapping-alist)))
(if (eq window (selected-window))
(progn
(setq dups (get-buffer-window-list nil nil 'visible))
(setq face-remapping-alist (delq inactive face-remapping-alist)))
(unless (or inactive (memq window dups))
(add-to-list 'face-remapping-alist '(hl-line hl-line-inactive))))))))
(window-list frame)))
(visible-frame-list))))
(add-hook 'buffer-list-update-hook #'toggle-active-window-highlighting)

glucas
- 20,175
- 1
- 51
- 83
-
See also function `walk-windows`, which you can use to iterate over the windows of visible frames, etc. (And +1 for `buffer-list-update-hook`, which gets called by `select-window`.) – Drew Aug 11 '15 at 20:37
-
+1 for this! Not so much for the hl-line stuff in specific, but I've been looking off and on for years to find a reliable way of shading the backgrounds of inactive windows in a frame. This works! Thanks! – Aaron Miller Dec 29 '15 at 22:59
-
Thank you, this is awesome! Unfortunately, it seems to have problems when using `global-hl-line-mode`. It seems that `global-hl-line-mode` doesn't load `hl-line-mode` for each buffer. It uses an overlay instead of using the `hl-line` face. I've been fiddling with it for a while with no luck. Any suggestions? Should I open a separate question? – Lorem Ipsum Nov 21 '18 at 16:31
-
You may want to use `window-state-change-hook` instead, because `buffer-list-update-hook` is call the buffer is changed. – updogliu Mar 31 '21 at 12:03
2
You can use window-selection-change-functions
. By adding a function locally the function is called each time when the window showing that buffer has been selected/deselected (see the docstring for more info).
This can be used to toggle hl-line-face
face for the deft buffer:
(defvar-local deft-hl-line-toggle-cookie+ nil)
(defun deft-toggle-hl-line-face+ (w)
(cond ((eq (current-buffer) (get-buffer deft-buffer))
(when deft-hl-line-toggle-cookie+
(face-remap-remove-relative deft-hl-line-toggle-cookie+)))
(t
(with-current-buffer (get-buffer deft-buffer)
(setq deft-hl-line-toggle-cookie+
(face-remap-add-relative
hl-line-face `(:inherit ,hl-line-face :background "gray")))))))
(defun deft-hl-line-toggle-setup+ ()
(add-hook 'window-selection-change-functions
'deft-toggle-hl-line-face+ nil t))
(add-hook 'deft-mode-hook 'deft-hl-line-toggle-setup+)

clemera
- 3,401
- 13
- 40
-
Thanks for answering this question despite it being so old! Your solution of using a buffer-local hook is much cleaner and less resource-intensive than previous solutions. – shankar2k May 17 '21 at 09:20