There is an underlying idea in my previous articles: if you have to repeat some actions often, automate the process. Previous articles focused on long-term improvements though. Is it worth it to automate a task that you will only use a few times and then never again? It is, but only if the process of automating is very cheap. This is where macros shine; or, the almost magical multiple-cursor alternative.
Emacs Macros allow you to repeat a given sequence of commands easily. It's like programming a sequence of actions, except that instead of typing code, you actually perform the actions. The sequence is recorded and you can then replay it any number of times.
For instance, in a previous article I showed how I bound a key to the following sequence: "go to end of line, delete the end of line character, delete extra space, go back to initial point". There is no decision-making involved. One can obtain a macro which behaves similarly like this:
C-x (
to begin macro recording;End
to go to the end of the current line;Delete
to delete the end of line character;M-Space
to delete extra space;C-x )
to end macro recording;C-x E
to replay the macro (and press E
again any number of times
to repeat it immediately).The important key bindinds to remember are C-x (
, C-x )
and C-x E
.
The rest is just an example.
Compared to a function, this is much easier to set up. However, this is also more limited. As far as I know: you lose the macro if you start recording another one; you can only record sequences and not include decision points or loops; and in the above example, it's not easy to "go back to initial point" so I don't do it. You can save a macro as a function, however the resulting code is rather messy.
This means that macros are well-suited to a specific task: repeating a sequence of actions that you are going to perform right away.
Let's transform the following code:
Orange Apple Banana Kiwi
into:
print_endline "Orange -> ORANGE";
print_endline "Apple -> APPLE";
print_endline "Banana -> BANANA";
print_endline "Kiwi -> KIWI";
First, place the cursor on the "O" of "Orange". Then, perform the following keystrokes:
C-x (
to begin recording;print_endline "
to start the current line;C-S-Right
to move to the end of the current fruit
while selecting it;M-w
(or C-c
depending on your settings) to copy selected text;->
(with spaces around it);C-y
to paste;C-Left
to move to the beginning of what you just pasted;M-u
to set the next word to uppercase and move at the end of it;"; RET Delete
to finish the current line and delete the space;C-x )
to stop recording;C-x E E E
to apply the macro three times, for the remaining fruits.Note that in some modes which automatically indent,
the Delete
may not be necessary. Also, this Delete
will also happen
after the last fruit (Kiwi
) even though there is no space; this will
delete the end of line character instead. If that is a problem, just add
a space after Kiwi
before running the macro on it.
In the fruit example, fruits don't always have the same size.
This is why we used C-Left
instead of just Left Left Left Left Left Left
,
which would work for Orange
and Banana
but not for Apple
and Kiwi
.
This kind of mistake is very easy to make with macros (much less with
multiple-cursors, see below).
But C-Left
usually stops at characters like hyphens (-
) or underscores
(_
). I could not find a fruit with a hyphen in its name, so I will invent
one: what if I want to apply the fruit macro to a fruit named
Kiwi-berry
? Luckily C-M-Left
, which has a larger notion of "word",
would usually work in this case. The difference between C-Left
and C-M-Left
depends on the current mode, so you just have to try.
Often you will want to apply a macro on several lines, one time per
line. For instance, we could have put each fruit on a single line
before recording our macro. It is a good habit to end the macro with
Right
(or Down Home
) to go to the beginning of the next line, so
that repeating the macro with C-x E E E
actually works. Otherwise we
would have to type C-x E Right C-x E Right C-x E Right
to fix the
cursor position between each application of the macro.
It is ofen useful to set up the text in a certain way before recording
and executing a macro. For instance, if a fruit has a space in its
name, like Stone fruit
, we can't use C-S-Right
nor C-M-S-Right
to select it. If it was on its own line though, we could select it
using C-S-End
. I call this "preparing" for the macro.
In the Stone fruit
example, even if it is on one line we can't use
M-u
to set the whole fruit to uppercase; it would result in STONE
fruit
and the cursor would be between STONE
and fruit
, which
would break the next commands of the macro. There are several tricks
to fix this:
Stone fruit
case by hand (probably the best way if
there are only a few cases of such complicated cases);End
after M-u
to ensure that the cursor is at the end of
the line (and fix STONE fruit
into STONE FRUIT
by hand later);C-y
step by " C-y "
to surround the pasted text with
quotes, which make it possible to select the text again using
C-M-S-Right
(then you have to remove the quotes);M-u
with M-u End C-Left M-u
, which
works for one-word and two-words fruit names but not more.It is usually a good idea to not record very long macros. There is often something which goes wrong in the middle. Instead, split it into several macros.
Note that C-s
(search) works inside macros. If you make a macro to
turn our sequence of print_endline
s into the origin fruit list, you
can use C-s
to search for "
, then C-Space
to start selecting,
then C-s
searching for ->
to select the fruit name independently
of its length.
Multiple-Cursors is an extension for Emacs which allows to have several cursors. Each keystroke is applied to all cursors at the same time (except for a configurable set of commands, such as saving the current file). This is not a new functionality, other editors have it as well.
It is basically a way to have visible macros: you record a macro and at the same time you apply it to all cursors. This allows to notice and fix mistakes much quicker, especially when a sequence of commands works for the first item but not for some of the next ones.
The drawback is that it is not very convenient if the cursors do not
fit in the current view of the buffer, i.e. if the text spans across
several pages. Also, C-s
(search) is not supported.
Other than that, all the above tips for macros also apply to multiple-cursors.
The readme is very well written and I urge you to give it a try.