Rethinking Minibuffer Movement
18 Dec 2024 Charles Choi
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 | |
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 | |
Why stop there? Let’s change it for Elisp mode:
1 2 3 4 | |
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.