13

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?

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 use M-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 calls hl-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)
  • 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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.