notes from /dev/null

by Charles Choi 최민수


Enhancing Navigation in Emacs View Mode

18 Sep 2023  Charles Choi

For decades I’ve used view mode in Emacs akin to the command line tool less (or its older inspiration, more) where I page through a file using the spacebar in read-only fashion, and navigating through said file with basic motion commands. A recent video on motion from Emacs Elements has gotten me to indirectly reconsider how motion works in view mode which I’ll share in this post.

That video suggested to rebind the key sequences C-v and M-v to heading (or structural) navigation rather than typically scrolling up or down a screen. After trying it for both Org and Markdown modes, I became convinced. But when viewing said files using C-v, M-v to navigate structure, it seemed clumsy. Wasn’t there already a binding convention for quickly navigating structure in read-only buffers? Turns out there is. Many Emacs packages bind n and p to navigation in read-only buffers (e.g. dired, magit, Org Agenda). So I was led to do this with view mode.

Because view mode is a mode (albeit a minor one), it follows the conventions of having a mode map (view-mode-map) and a mode hook (view-mode-hook). The code below customizes view mode to support bindings of n and p that are specific to the major mode it is applied to. If no major mode is specified, then p and n are mapped to scroll-down-command and scroll-up-command respectively.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
(require 'view)

(add-hook
 'view-mode-hook
 (lambda ()
   (cond ((derived-mode-p 'org-mode)
          (define-key view-mode-map (kbd "p") 'org-previous-visible-heading)
          (define-key view-mode-map (kbd "n") 'org-next-visible-heading))
         ((derived-mode-p 'markdown-mode)
          (define-key view-mode-map (kbd "p") 'markdown-outline-previous)
          (define-key view-mode-map (kbd "n") 'markdown-outline-next))
         ((derived-mode-p 'html-mode)
          (define-key view-mode-map (kbd "p") 'sgml-skip-tag-backward)
          (define-key view-mode-map (kbd "n") 'sgml-skip-tag-forward))
         ((derived-mode-p 'python-mode)
          (define-key view-mode-map (kbd "p") 'python-nav-backward-block)
          (define-key view-mode-map (kbd "n") 'python-nav-forward-block))
         ((derived-mode-p 'emacs-lisp-mode)
          (define-key view-mode-map (kbd "p") 'backward-sexp)
          (define-key view-mode-map (kbd "n") 'forward-sexp))
         ((derived-mode-p 'makefile-mode)
          (define-key view-mode-map (kbd "p") 'makefile-previous-dependency)
          (define-key view-mode-map (kbd "n") 'makefile-next-dependency))
         ((derived-mode-p 'c-mode)
          (define-key view-mode-map (kbd "p") 'c-beginning-of-defun)
          (define-key view-mode-map (kbd "n") 'c-end-of-defun))
         (t
          (define-key view-mode-map (kbd "p") 'scroll-down-command)
          (define-key view-mode-map (kbd "n") 'scroll-up-command)))))

The cond expression above can be extended to support more modes as desired.

Highlight Line Mode

With structural navigation in view mode, I find it more useful to highlight the line where the point is. hl-line-mode does just that. The following code shows how to turn on highlight line mode and to disable it upon exiting view mode to edit the file.

1
2
3
4
5
6
7
(add-hook 'view-mode-hook 'hl-line-mode)

(defun cc/view-exit ()
  "Advice function to disable highlighting upon exiting view-mode."
  (hl-line-mode -1))

(advice-add 'View-exit :after #'cc/view-exit)

Source for all code fragments above can be found on GitHub. All code tested with Emacs 28.2.

Closing Remarks

One might argue that the above behavior skirts around modal-style editing and navigation, but it’s really not my intention to go in that direction. That said, since using view mode means you are not editing a file, it seems sensible to take advantage of using single key bindings.

Having been around since Emacs version 16, view mode is both old and for me, overlooked with regards to thinking about customizing despite my frequent use of it. Using structural navigation in view mode has been surprisingly delightful. If this is new to you, I think you’ll find it the same.

emacs

 

AboutMastodonInstagramGitHub

Feeds & TagsGet Captee for macOS

Powered by Pelican