Today I want to talk a little about a really nice feature in Emacs.
That feature is keyboard macros. The work by recording your actions on
the keyboard and then replaying those actions. These macros can then be
combined with C-u
to run the following command, in this case the
just recorded macro, num number of times.
The most recent task I used macros for is while extending some functionality
in Erlang. I needed to add a lot of function clauses for a set of some fifteen
new patterns, and use those to translate, from the pattern to a one value, and vice versa.
There was already functionality for this. All I had to do was to replicate
the two functions for all the new patterns.
Lets say that we start out with the following list of defined items.
-define(THING_ONE, 1).
-define(THING_TWO, 2).
-define(THING_THREE, 3).
-define(THING_FOUR, 4).
-define(THING_FIVE, 5).
-define(THING_SIX, 6).
-define(THING_SEVEN, 7).
-define(THING_EIGHT, 8).
-define(THING_NINE, 9).
-define(THING_TEN, 10).
-define(THING_ELEVEN, 11).
-define(THING_TWELVE, 12).
I then needed to transform that into the following form:
to_value(?THING_ONE) -> 1111;
to_value(?THING_TWO) -> 2222;
...
to_value(?THING_TWELVE) -> 1212.
As well as this format:
to_macro(1111) -> ?THING_ONE;
to_macro(2222) -> ?THING_TWO;
...
to_macro(1212) -> ?THING_TWELVE.
This is really straight forward by combining rectangles and keyboard
macros.
First use put your cursor right after the first -define(
and then press
C-x SPC
to enable rectangle mode. Mark all of the definitions so that your
selection looks like this:
THING_ONE, 1
THING_TWO, 2
THING_THREE,
THING_FOUR,
THING_FIVE,
THING_SIX, 6
THING_SEVEN,
THING_EIGHT,
THING_NINE,
THING_TEN, 1
THING_ELEVEN
THING_TWELVE
Copy the region with M-w
and paste it where you want to define your to_value
function.
To get rid of the trailing commas and numbers you select the region again and run M-x replace-regex
with ,.*
as your pattern. Voila! You now have a pristine region to work with.
Do a rectangle select on the first column of the values and then use M-x string-insert-rectangle
to insert to_value(?
at the start of all the lines.
You rectangle select again to mark the last column in your partial function
definition. Now do the same M-x string-insert-rectangle
and insert ) -> ;
.
Now you need to do some manual work to insert all the values for the function and you’re done with the first function.
Now for the exciting part where we leverage keyboard macros.
Copy the whole to_value
function and paste it. Create a region for it and
then to M-x %
to replace to_value
with to_macro
to_macro(?THING_ONE ) -> 1111;
to_macro(?THING_TWO ) -> 2222;
...
to_macro(?THING_TWELVE) -> 1212.
To get rid of all the extra white space we’ll mark the entire function and do M-x replace-regexp
with the pattern \b\s-*)
and replace it with )
.
Now we’ll record a macro:
- Start with your cursor on the first row. Press
C-x (
to start to record the macro. C-s
and search to the first?
press back arrow so that the cursor is on top of the?
. Then we pressC-SPC
to create a region for the parameter by usingC-s
again to search to the first)
. Remember to use back arrow again to get the selection just right.C-w
to kill/cut it.- Use
C-s
to search for>
, and press right arrow. Paste item we just cut by usingC-y
. - Create a region and press
C-e
to select to the end of the line. Press left arrow to deselect the semi-colon. Cut the region withC-w
. - Press
C-r
to search backward and search for)
press RET andC-y
to paste. Now press down,C-a
, and stop recording withC-x )
. Now we can useC-x e
to execute our macro. Did you notice that it did the exact same thing that you did and ended up with the cursor on the third function clause? Now we can replace the rest by usingC-u 10 C-x e
. Emacs will execute the macro another ten times.