View-Mode - Emacs's Hidden Modal Editing Gem?



The Rabbit Hole

I’ve fallen down another rabbit hole, but then again, this is Emacs we’re talking about!

In this case it relates to a fundamental editing principle.

I was trying to understand some code, so of course that involved plenty of point/cursor navigation, scrolling, etc. After an hour or so of doing this, I started to wonder if there was a more efficient navigation method involving single keypresses. If I know I only want to view the file, then why not get rid of this modifier key nonsense? 😁 I’m aware that the basic functionality I’m talking about here is modal editing.

There are many different packages out there from boon to meow. So does Emacs have something built-in? Well, throughout my series of blog posts, I often ask this question, and the answer always comes back emphatically the same: “Why yes, of course it does!” This might be a little misleading, as though there isn’t a specific built-in that gives modal editing right out of the box, there is a mode that can be tweaked a little to achieve something similar.

View Mode

So what is the thing that’s been hiding in plain sight? Well, it is view-mode. Although strictly not a modal editing option, I think with a little tweaking I could possibly turn it into one. After a bit of internet stumbling, I came across a Reddit post from someone who has tried something similar. I think I shall add my own spin. The link is https://www.reddit.com/r/emacs/comments/fojc1y/using_viewmode_for_modal_navigation/

If you have been reading my posts over the years, you probably know my general design ethos on such a solution, and that is to use built-in Emacs-centric philosophies and to make sure it can easily be set up on an air-gapped system. Well, for this solution, I’m going to deviate slightly from this idiom and incorporate not just more Emacs-ish keybindings but also some Vim ones! My main reasoning for this is that evil-mode is very popular, and from time to time I have recourse to using Vim over ssh, and the jkhl setup does feel pretty natural.

View-mode has been part of Emacs forever, but its default keybindings feel a bit… antiquated. z and w for scroll up and down? Space for page scroll, Return for forward one line, etc. (Maybe it works in the latter case, as it is more of a legacy Unixy vibe, but really I think we should be using more Emacs-ish keybindings, and as an option, why not Vim ones, as they tend to be mutually exclusive)

The Setup

Of course, view-mode is completely customizable, so here’s my setup that combines the best of both worlds, familiar Emacs patterns and efficient Vim-style navigation:

;; Enable view-mode when entering read-only
(setq view-read-only t)

;; Enhanced keybindings
(with-eval-after-load 'view
  ;; More Emacs-ish keys

  ;; Navigation
  (define-key view-mode-map (kbd "n") 'next-line)
  (define-key view-mode-map (kbd "p") 'previous-line)
  (define-key view-mode-map (kbd "f") 'forward-char)
  (define-key view-mode-map (kbd "b") 'backward-char)

  ;; Beginning/end of line
  (define-key view-mode-map (kbd "a") 'beginning-of-line)
  (define-key view-mode-map (kbd "e") 'end-of-line)

  ;; Vim-ish keys

  ;; Quick exit to edit mode
  (define-key view-mode-map (kbd "i") 'View-exit)
  (define-key view-mode-map (kbd "j") 'next-line)
  (define-key view-mode-map (kbd "k") 'previous-line)
  (define-key view-mode-map (kbd "h") 'backward-char)
  (define-key view-mode-map (kbd "l") 'forward-char)

  ;; Page movement
  (define-key view-mode-map (kbd "u") '(lambda()
                                         (interactive)
                                         (View-scroll-page-backward 3)))
  (define-key view-mode-map (kbd "d") '(lambda()
                                         (interactive)
                                         (View-scroll-page-forward 3)))

  ;; Beginning/end of line (Vim style)
  (define-key view-mode-map (kbd "0") 'beginning-of-line)
  (define-key view-mode-map (kbd "$") 'end-of-line)

  ;; Beginning/end of buffers
  (define-key view-mode-map (kbd "g") 'beginning-of-buffer)
  (define-key view-mode-map (kbd "G") 'end-of-buffer)

  ;; Other bespoke bindings
  (define-key view-mode-map (kbd ";") 'other-window)

  (define-key view-mode-map (kbd "SPC") 'nil))

;; Quick toggle keys
(global-set-key (kbd "C-<escape>") 'view-mode)
(global-set-key (kbd "C-<tab>") 'view-mode)

;; Optional: return to view-mode after saving
(add-hook 'after-save-hook
          (lambda ()
            (when (and buffer-file-name (not view-mode))
              (view-mode 1))))

;; Visual feedback - box cursor in view mode, bar when editing
(add-hook 'view-mode-hook
          (defun view-mode-hookee+ ()
            (setq cursor-type (if view-mode 'box 'bar))))

Baby Proofing

Here’s where it gets interesting (possibly). I have a baby, and babies have this amazing ability to slam their tiny hands down on keyboards at the most inopportune moments. Nothing quite like a random key smash right in the middle of an important file!. Wouldn’t it be great to have a protective read-only shield over a file? So I decided, why not put all files into view-mode by default?, this way, accidental keypresses won’t modify anything, and I can intentionally switch to edit mode only when I actually need to make changes.

;; Enable view-mode for files by default
(add-hook 'find-file-hook
          (lambda ()
            (unless (or (derived-mode-p 'dired-mode))
              (view-mode 1))))

I’m currently using the automatic activation to see how it goes, but I have to say I’m not completely comfortable with setting files to read-only mode. Although more baby-proof, it does mean I have to activate edit mode (or deactivate view-mode) each time I want to edit a file and that doesn’t feel very Emacsy to me.

Cursor

You will notice that in the setup there is a modification of the cursor depending on the mode state. Although the modeline will indicate if view-mode is on or off, I’m actually quite liking the differentiation in editing modes to be reflected in the cursor. It is simple, and my brain is starting to recognize instinctively which mode I am in with no extra visual baggage.

Subtleties

It is also worth pointing out some more subtleties:

It is worth noting, of course, that all normal Emacs navigational keybindings are still available, so you also have that option too within view-mode.

Foibles

So far I have noticed some, lets say, foibles

Improvements?

Conclusion

Anyways, the result is a surprisingly pleasant modal editing experience so far that already feels natural in Emacs while giving me the ergonomic benefits of Vim-style navigation and comfortable single-key navigation options.

Plus, my codebase is now baby-resistant!

Comments

comments powered by Disqus