more < less < emacs

If you use Emacs for more than editing text, there are many daily occasions to use Emacs as a pager. Here’s my list:

  • Read-only files (with view-mode)
  • Scrolling through emails (notmuch)
  • RSS feed entries (elfeed)
  • Reading ebooks (nov.el)
  • Web browsing (eww)
  • Man pages (man or woman)
  • Help buffers and Info documents
  • Git logs (magit or vc)
  • System logs, compilation and debug output

This list was about half as long ten years ago, it keeps growing!

Emacs (and associated packages) generally do a decent job of acting as a pager. In addition to the default movement commands, all these modes let you page forward with SPC and backward with Backspace or S-SPC, and quit the window (without killing the buffer) with q. Additionally, some modes provide commands to scroll half a page up or down.

And that’s where the pager-ness stops. Unlike a pager, Emacs has to read in the entire document into a buffer. This is fine for man pages, but you’re asking for trouble if the log you want to page through is a few hundred chunky Megabytes, or if the website in question is the single-page Org manual. If your document has long lines in addition, it’s curtains for your Emacs session. But let’s put these heady issues aside for today.

The thing that’s always bothered me about these modes is that the text cursor sticks around. This is generally useless and distracting – I don’t need a cursor hanging around the screen when reading an ebook or a man page. Often it’s annoying – I can’t page the view down by a few lines by scrolling1. The presence of the cursor makes the arrow keys virtually useless.

Prefab construction

I don’t doubt that there’s a pager-mode package somewhere that fixes this. But as usual, Emacs already provides us the ingredients to fix the dangling cursor problem easily:

  • Hide the cursor: The variable cursor-type controls the cursor’s appearance.
  • Scroll the view instead of the cursor: The appropriately named scroll-lock-mode does this. It’s even bound to the ScrLk key, fulfilling the key’s original purpose.

Combining the two gives us a better pager experience in Emacs:

(defvar-local hide-cursor--original nil)

(define-minor-mode hide-cursor-mode
  "Hide or show the cursor.

When the cursor is hidden `scroll-lock-mode' is enabled, so that
the buffer works like a pager."
  :global nil
  :lighter "H"
  (if hide-cursor-mode
      (progn
        (scroll-lock-mode 1)
        (setq-local hide-cursor--original
                    cursor-type)
        (setq-local cursor-type nil))
    (scroll-lock-mode -1)
    (setq-local cursor-type (or hide-cursor--original
                                t))))

I bind hide-cursor-mode to F7, the key to toggle caret browsing in Firefox. The utility is inverted here: hide-cursor-mode turns off “caret browsing” in Emacs instead of turning it on, but the effect is the same.

(define-key global-map (kbd "<f7>") 'hide-cursor-mode)

Here’s me browsing an Elfeed entry without/with hide-cursor-mode. Like in less, searching and other functions still work fine. If I want the cursor back to select a region, it’s a keypress away:

Bonus: half-screen scrolling

Vim/less style half-screen scrolling is particularly useful for paging, and missing in Emacs2. Here’s a really basic implementation:

(defun scroll-up-half ()
  (interactive)
  (scroll-up-command
   (floor
    (- (window-height)
       next-screen-context-lines)
    2)))

(defun scroll-down-half ()
  (interactive)
  (scroll-down-command
   (floor
    (- (window-height)
       next-screen-context-lines)
    2)))

As a bonus, these can be bound to more convenient keys than Vim/=evil-mode=’s C-d and C-u since most keys are available in pager-type read-only buffers.


  1. Instead I have to call recenter (C-l) with a prefix argument ↩︎

  2. Although view-mode provides this. ↩︎