notes from /dev/null

by Charles Choi 최민수


18 Dec 2024

Rethinking Minibuffer Movement

For your consideration, a possible quality of life improvement in using Emacs. Typically the next-to-the-smallest unit of point movement is by word. This is reinforced by binding M-f and M-b to forward and backwards word movement respectively.

When editing prose, this is sensible. For editing code and commands, I would argue this is less so. This is because the unit of text I most want to move by is via symbol. Common examples of symbols include variable/function/class names, command line options & arguments, and Makefile targets. Symbols frequently embed non-alphabetic characters (e.g. ‘foo-bar’, ‘bar_foo’, ‘foo/bar’) which are treated as word-separators by the commands forward-word and backward-word.

Movement by symbol is what I want whenever I’m editing in the minibuffer. Muscle memory wants me to type M-f or M-b, which does the “wrong” thing here. But there are movement commands for a unit of text that handles symbols gracefully: that unit of text is called a balanced expression (aka sexp). By default, moving by balanced expression is bound to C-M-f for forwards movement, C-M-b for backwards.

Personally, I’m loathe to use keybindings involving more than two keys, so I’ve taken this tack: Swap (M-f, M-b) for balanced expression movement and (C-M-f, C-M-b) for word movement in modes that involve a lot of symbols.

Another detail to consider is moving the point so that it is at the start of a unit of text. Both forward-word and forward-sexp are implemented so that the point is set at the end of a text unit. In many cases this adds editing friction for me because what I really want is for the point to be at the start of a text unit. To allow for this, I’ve implemented a function for moving forward a balanced expression that places the point at the start of the next sexp (cc/next-sexp).

Code showing the implementation of cc/next-sexp and configuring the minibuffer to swap the bindings for word and sexp movement is shown below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
(keymap-set minibuffer-mode-map "M-b" #'backward-sexp)
(keymap-set minibuffer-mode-map "M-f" #'cc/next-sexp)
(keymap-set minibuffer-mode-map "C-M-b" #'backward-word)
(keymap-set minibuffer-mode-map "C-M-f" #'forward-word)

(defun cc/--next-sexp-raw ()
  "Raw implementation to move point to the beginning of the next sexp.

This function has no error checking."
  (forward-sexp 2)
  (backward-sexp))

(defun cc/next-sexp ()
  "Move point to beginning of the next balanced expression (sexp)."
  (interactive)
  (condition-case nil
      (cc/--next-sexp-raw)
    (error (condition-case nil
               (forward-sexp)
             (error
              (message
               "Unable to move point to next balanced expression (sexp)."))))))

The same swap can be applied when calling eval-expression (M-:). The keymap to configure here is minibuffer-local-shell-command-map.

1
2
3
4
(keymap-set minibuffer-local-shell-command-map "M-b" #'backward-sexp)
(keymap-set minibuffer-local-shell-command-map "M-f" #'cc/next-sexp)
(keymap-set minibuffer-local-shell-command-map "C-M-b" #'backward-word)
(keymap-set minibuffer-local-shell-command-map "C-M-f" #'forward-word)

Why stop there? Let’s change it for Elisp mode:

1
2
3
4
(keymap-set emacs-lisp-mode-map "M-b" #'backward-sexp)
(keymap-set emacs-lisp-mode-map "M-f" #'cc/next-sexp)
(keymap-set emacs-lisp-mode-map "C-M-b" #'backward-word)
(keymap-set emacs-lisp-mode-map "C-M-f" #'forward-word)

This idea can be extended to other modes such as Eshell.

Closing Thoughts

I’ve been living with this setup for a month now and anecdotally I’ve found this to feel “right” enough to merit making a post. For those readers who do not consider this post’s suggestion heretical, I’d encourage to give these changes a try. Perhaps you’ll find yourself pleasantly surprised.

emacs

Past Articles

2
DEC
2024

Announcement: Casual talk at EmacsConf 2024

I’m giving a talk on Casual at EmacsConf 2024. Here’s some info on getting to see it.

read more
25
NOV
2024

Announcing Casual Calendar

The Emacs built-in calendar & diary gets the Casual treatment.

read more
13
NOV
2024

Styling Text via Keyboard in Org and Markdown

Revisiting how I style text in Org and Markdown via a keyboard-driven command.

read more
21
OCT
2024

Announcing Casual (Redux) and Reorganization

More reorganization for the Casual packages, hopefully for the last time. Announcing Casual, now on MELPA.

read more
8
OCT
2024

Referencing Org Table Cells with Text Regions

Manually dealing with Org table references is not fun. This post proposes a better way using a text region to generate an Org table reference.

read more
23
SEP
2024

Announcing Casual EditKit

Announcing Casual EditKit, an attempt to surface all the wonderful editing commands in Emacs.

read more
17
SEP
2024

Migrating MacPorts in 2024

MacPorts now supports built-in migration. Finally.

read more
9
SEP
2024

Announcing Casual Symbol Overlay

Highlighting symbols in a programming language editor is genuinely useful. For Emacs, Symbol Overlay gives you that. Casual Symbol Overlay offers a Transient menu to make using Symbol Overlay even easier.

read more
3
SEP
2024

Announcing Casual Agenda

Announcing Casual Agenda

read more

Page 1 / 13   >

 

AboutMastodonInstagramGitHub

Feeds & TagsGet Captee for macOS

Powered by Pelican