notes from /dev/null

by Charles Choi 최민수


Personalizing Emacs Doc Navigation

12 Dec 2023  Charles Choi

TBH, I’ve long had a love/hate relationship with the documentation modes (e.g. Info, Help, Man, Shortdoc) provided by Emacs. While I’m grateful for their existence, I’ve never been enamored of using them because of their UX (user experience) defaults. To my fingers their default keybindings are at times arcane and inconsistent. The net result is that I when I need help, I’ll generally go to the web first to get an answer and only use the installed documentation as a last resort.

But this being Emacs, one can always alter this to preference. This post shares what I’ve landed on to make reading Emacs documentation more “natural”. While “natural” is a very subjective term here, some broader conventions influence where my fingers expect to be and eyes expect to see. Perhaps you’ll think likewise.

Guiding Principles

Scrutinizing further on what seems “natural” to me, I’d count the following influences:

  • Many years of using Unix-style (including Linux and macOS) systems with tool keybindings heavily influenced by vi and Emacs
  • The usage of web browsers as the predominant interface for documentation

Given the above, some guiding principles on how I like my keybindings:

  • Navigation on read-only content using vi-style “hjkl” keybindings.
    • Emphasis on key usage either on or close to the home row.
  • Forward and backward history navigation using web browser-style M-[, M-] keybindings. (Alternately C (control) for Linux and Windows)
  • Reinforce the Emacs convention of using p and n to navigate within the same read-only buffer content.
    • RANT: I’ve been burned countless times by the default keybindings of p and n in Info, which navigate to different logical documentation nodes, only to lose track of where I was at. Further compounding my confusion was trying to navigate back - I’d make the error of trying to use p and n again mistakenly confusing it with historical navigation (default bound to l and r which I struggle to this day to remember).
  • Use highlight line mode (hl-line-mode) to help visually track where the current point is in an Emacs window.
  • Support fine-grained (one-line) scrolling when reading documentation.

Implementation

Listed below is the source of my UX modifications to the different documentation modes (Info, Help, man, shortdoc). Note the paragraph movement functions cc/docview-backward-paragraph and cc/docview-forward-paragraph which are intended to be used with hl-line-mode to highlight the first line of a paragraph.

Verified on GNU Emacs 29.1.

  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
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
;;; cc-doc-mode-ux.el --- Documentation Mode UX Modifications by cchoi

;;; Commentary:
;; UX modifications for different Emacs documentation modes.
;; Covers Info, Help, Man, and Shortdoc.

;;; Code:
(require 'info)
(require 'help)
(require 'shortdoc)
(require 'man)
(require 'hl-line)

(defun cc/docview-backward-paragraph ()
  "Move point backward paragraph such that the first line is highlighted.

This function is intended to be used with `hl-line-mode'."
  (interactive)
  (backward-paragraph 2)
  (forward-line))

(defun cc/docview-forward-paragraph ()
  "Move point forward paragraph such that the first line is highlighted.

This function is intended to be used with `hl-line-mode'."
  (interactive)
  (forward-paragraph)
  (forward-line))

;; Info
(add-hook 'Info-mode-hook
          (lambda ()
            ;; Use web-browser history navigation bindings
            (define-key Info-mode-map (kbd "M-[") 'Info-history-back)
            (define-key Info-mode-map (kbd "M-]") 'Info-history-forward)
            ;; Bind p and n to paragraph navigation
            (define-key Info-mode-map (kbd "p") 'cc/docview-backward-paragraph)
            (define-key Info-mode-map (kbd "n") 'cc/docview-forward-paragraph)
            ;; Bind <f1> to help
            (define-key Info-mode-map (kbd "<f1>") 'Info-help)
            ;; Bind M-j, M-k to scrolling up/down line
            (define-key Info-mode-map (kbd "M-j") 'scroll-up-line)
            (define-key Info-mode-map (kbd "M-k") 'scroll-down-line)
            ;; Bind h and l to navigate to previous and next nodes
            ;; Bind j and k to navigate to next and previous references
            (define-key Info-mode-map (kbd "h") 'Info-prev)
            (define-key Info-mode-map (kbd "j") 'Info-next-reference)
            (define-key Info-mode-map (kbd "k") 'Info-prev-reference)
            (define-key Info-mode-map (kbd "l") 'Info-next)
            ;; Bind / to search
            (define-key Info-mode-map (kbd "/") 'Info-search)
            ;; Set Bookmark
            (define-key Info-mode-map (kbd "B") 'bookmark-set)
            ;; Bind side mouse buttons on Logitech mouse
            (define-key Info-mode-map (kbd "<mouse-5>") 'Info-history-forward)
            (define-key Info-mode-map (kbd "<mouse-4>") 'Info-history-back)))

(add-hook 'Info-mode-hook 'hl-line-mode)

;; Help
(add-hook 'help-mode-hook
          (lambda ()
            ;; Use web-browser history navigation bindings
            (define-key help-mode-map (kbd "M-[") 'help-go-back)
            (define-key help-mode-map (kbd "M-]") 'help-go-forward)
            ;; Bind p and n to paragraph navigation
            (define-key help-mode-map (kbd "p") 'cc/docview-backward-paragraph)
            (define-key help-mode-map (kbd "n") 'cc/docview-forward-paragraph)
            ;; Bind <f1> to help
            (define-key help-mode-map (kbd "<f1>") 'describe-mode)
            ;; Bind M-j, M-k to scrolling up/down line
            (define-key help-mode-map (kbd "M-j") 'scroll-up-line)
            (define-key help-mode-map (kbd "M-k") 'scroll-down-line)
            ;; Bind j and k to navigate to forward and backward buttons
            (define-key help-mode-map (kbd "j") 'forward-button)
            (define-key help-mode-map (kbd "k") 'backward-button)
            ;; Bind side mouse buttons on Logitech mouse
            (define-key help-mode-map (kbd "<mouse-5>") 'help-go-forward)
            (define-key help-mode-map (kbd "<mouse-4>") 'help-go-back)))

(add-hook 'help-mode-hook 'hl-line-mode)

;; Shortdoc
(add-hook 'shortdoc-mode-hook
          (lambda ()
            ;; Bind <f1> to help
            (define-key shortdoc-mode-map (kbd "<f1>") 'describe-mode)
            ;; Bind M-j, M-k to scrolling up/down line
            (define-key shortdoc-mode-map (kbd "M-j") 'scroll-up-line)
            (define-key shortdoc-mode-map (kbd "M-k") 'scroll-down-line)
            ;; Bind h and l to navigate to previous and next sections
            ;; Bind j and k to navigate to next and previous
            (define-key shortdoc-mode-map (kbd "h") 'shortdoc-previous-section)
            (define-key shortdoc-mode-map (kbd "j") 'shortdoc-next)
            (define-key shortdoc-mode-map (kbd "k") 'shortdoc-previous)
            (define-key shortdoc-mode-map (kbd "l") 'shortdoc-next-section)))

(add-hook 'shortdoc-mode-hook 'hl-line-mode)

;; Man
(add-hook 'Man-mode-hook
          (lambda ()
            ;; Bind <f1> to help
            (define-key Man-mode-map (kbd "<f1>") 'describe-mode)
            ;; Bind M-j, M-k to scrolling up/down line
            (define-key Man-mode-map (kbd "M-j") 'scroll-up-line)
            (define-key Man-mode-map (kbd "M-k") 'scroll-down-line)
            ;; Bind j and k to navigate forward and backward paragraphs
            (define-key Man-mode-map (kbd "j") 'cc/docview-forward-paragraph)
            (define-key Man-mode-map (kbd "k") 'cc/docview-backward-paragraph)
            ;; Bind K to kill buffer to replace override of default k above
            (define-key Man-mode-map (kbd "K") 'Man-kill)))

(add-hook 'Man-mode-hook 'hl-line-mode)

(provide 'cc-doc-mode-ux)
;;; cc-doc-mode-ux.el ends here

If the above is stored in the file cc-doc-mode-ux.el, then it can be invoked with the following Elisp statement:

1
(require 'cc-doc-mode-ux)

Closing Thoughts

What a difference a couple of keybinding changes can make! Turns out I’m much more delighted using the keybindings j, k, p, n, M-[, and M-] as my primary means of navigation through Emacs documentation. The visual aid that hl-line-mode provides gives great comfort for tired eyes. After so many years of using Info and Help at arms-length, I might go so far as to say that I finally like using them now.

emacs

 

AboutMastodonInstagramGitHub

Feeds & TagsGet Captee for macOS

Powered by Pelican