Moving Text Elegantly in Emacs
20 Jul 2023 Charles Choi
Suppose that you want to edit a list ["a", "b", "c", "d"]
so its ordering is ["b", "c", "a", "d"]
. How would you do it? A common approach is to manually select "a"
, cut and paste it into the third position, correct the commas, and call it a day. Doing this once doesn’t seem so bad; having to do this multiple times gets laborious though. Now extrapolate that to a life-time of manually editing around balanced delimiters. Surely there’s a better way?
Recently I learned Emacs can make short work of the above via the transpose
family of functions. In particular, transpose-*
can be used to build functions that move blocks of text forward or backward. Coupled with the repeat
function, these functions provide a set of building blocks to edit text in an elegant fashion. For the above example, we could do the following:
- Move the pointer to the front of
"a"
. - Invoke
cc/move-sexp-forward
, a custom function using transpose-sexps. (I’ll explain sexp later) - Either invoke
cc/move-sexp-forward
again or use therepeat
command.
Here’s a video showing this at work:
In the above example, Emacs considers the string between quotes (example "a"
) to be a balanced expression (or in Lisp terminology a sexp). Balanced expressions have opening and closing delimiters and an Emacs mode will typically recognize different pairs of delimiters as a sexp. Some examples are ""
, ''
, {}
, []
, <>
, and ()
.
The following code example describes two functions that can move a balanced expression either forward or backward in text. (Verified on Emacs 28.2)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Different block types of text can be moved, in symmetry to what is provided by the tranpose
family of functions:
- word
cc/move-word-backward
cc/move-word-forward
- sentence
cc/move-sentence-backward
cc/move-sentence-forward
I leave out the block types char, line, region, and paragraph due to preference and brevity, but they are easily added given the code pattern shown above.
Usability
There are functions that you use occasionally but really don’t have the desire (or even capacity) to remember their names for, much less assign a keybinding to. I think such functions are ideal candidates for being put into a menu. So it is with my thinking about the transpose-*
and move-*
functions described above.
Detailed below shows how to configure submenus for transposing (cc/transpose-menu
) and moving (cc/move-text-menu
) text. These submenus can be invoked either via a context menu or via the top level Edit menu. First some screenshots of the menus themselves:
Now for the source:
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 |
|
Instrumenting these submenus in the top-level Edit menu is as follows:
1 2 3 4 5 |
|
This link shows how I've instrumented my context-menu (among many other things) with the above transpose and move text submenus.
Summary
Moving text in Emacs can be elegant via the transpose family of functions, used directly or indirectly. Operations around balanced expressions are compellingly powerful. Coupled with Avy (an amazing navigation package which I just recently learned of thanks to Irreal) and the world is your oyster.