Prioritize hl-line Highlight Over Neotree Faces

In hindsight, it looks like my quest to tweak how Emacs looks and feels is to make it more Mac-like. No wonder, because I really like the OS X-era UI. So I’m still using the system default selection highlight colors and want these everywhere.

For example in neotree.

The hl-line current line highlight, combined with lin.el (which I use for half a year) to make it act as a prominent selection instead of a subtle hint in that mode, did lose over neotree’s own face settings.

Before and after the face's priority change.

Inspecting why that looked the way it looked via C-u C-x = to see the character and face information, I got this:

There are 2 overlays here:
 From 565 to 598
  face                 hl-line
  priority             -50
  window               nil
 From 578 to 597
  button               [Show]
  category             default-button
  face                 neo-file-link-face
  follow-link          t
  help-echo            [Show]
  keymap               [Show]
  neo-full-path        [Show]

Never saw this before, but “priority: -50” sounded like the way to go. Raise the priority, then maybe the hl-line face would override the neo-file-link-face of the file “button” at point.

But there’s no face property called ‘priority’. Searching the web for this, I found out about show-paren-priority on Emacs.StackExchange, so it looks like this is commonly a variable, and yes, there is a hl-line-overlay-priority and its value is -50. I don’t know what other priorities there are, so I’m just flipping it to +50 for good measure.

Update 2022-06-10: Setting this in neotree still makes sense, but setting this globally only looked good for a while – until I ran into color issues with selections. If the priority is not negative, it will override the region selection colors. So I couldn’t see when I was selecting file names or file sizes in dired. Here’s a fixed version.

The updated code just enables this for neotree buffers:

(defun my/neotree-hook ()
  (hl-line-mode -1)  ;; Disable if it's on, see explanation below.
  (setq-local hl-line-overlay-priority +50)
  (lin-mode))
(add-hook 'neotree-mode-hook #'my/neotree-hook)

The important part is to set the priority buffer-locally before entering hl-line-mode or lin-mode. So you cannot use lin-mode-hooks to auto-enable lin, but have to do it this way instead. Make sure to remove neotree-mode-hook from lin-mode-hooks so it isn’t enabled early.

But neotree has a neo-hide-cursor setting that auto-enables hl-line-mode before calling any hooks:

(define-derived-mode neotree-mode special-mode "NeoTree"
  "A major mode for displaying the directory tree in text mode."
  (setq indent-tabs-mode nil            ; only spaces
        buffer-read-only t              ; read only
        truncate-lines -1
        neo-buffer--show-hidden-file-p neo-show-hidden-files)
  (when neo-hide-cursor
    (progn
      (setq cursor-type nil)
      (hl-line-mode +1)))
  ...

Geez! So we either have to:

  • not use neo-hide-cursor, which would be a surprise setting to accommodate for the way I configure lin;
  • add an advice to hl-line-mode limited to a neotree buffer instead and change the priority before enabling hl-line-mode;
  • just keep the quick fix to disable hl-line-mode before resetting the priority.

Disable and re-enable hl-line-mode or neotree, and then it looks good!

The next nit I want to pick is to enable horizontal scrolling with the mouse for long filenames, and removing the block cursor when LIN is active (which you don’t see on the screenshots because I timed the picture with its blinking).