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
orwoman
) - Help buffers and Info documents
- Git logs (
magit
orvc
) - 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.