About Vimgolf
Following some tweet I found myself desultory watching an episode of the awesome VimGolf in Emacs video series by Tim Visher. Those series are about picking some challenge from vimgolf and implementing it with our favorite editor instead. Because Emacs Rocks guys.
Let me tell you upfront that I really dislike the whole idea of the vim golf challenge. I’ve been a user of both Emacs and Vim for many years, and finally decided to switch to living in Emacs; or if you prefer, climbing my way up from level 2 as in The Levels Of Emacs Proficiency. The reason why is that I found that in my case, using Vim would mean spending more time thinking about how to do some editing operation rather than the problem I wanted to solve by editing some text, most often code.
Of course, the main effect of Vim Golf is to make you focus even more on the wrong problem. There’s still a good side of it though, which is that such challenges are good excuses to discover new features of your editor. So let’s use that excuse to talk about some nice Emacs features.
The challenge
The previous image will lead you to a particular challenge where it’s all
about filling in an array with consecutive
hexadecimal numbers written as
0xab
, where you begin with a template containing only the
0x00
entry. The
idea is of course to use the
Vim feature that will increment the
number at
point, and is available through the
C-a
keystroke.
unsigned int hex[] = {
0x00,
};
A first solution
Emacs does not ship with an
increment-number-at-point, much less so with one
that would support
decimal,
octal and
hexadecimal and even automatically
recognize
0x
as a prefix meaning that the next number is
hexadecimal. But
Emacs ship with
Emacs Keyboard Macros and those have a counter, so it’s easy
enough to fill in numbers from 1 to 255 that way:
M-1 F3 F3 , F4
will
register a macro where the counter starts at 1, and each time you hit
F4
it
will insert the current counter value, increment it and insert a coma. You
want to do that 254 times, so you do
C-u 2 5 4 F4
and
Emacs just does that.
Now, to transform those decimal numbers into their
hexadecimal
representation, you can use advanced
Emacs Regexp Replace features. Replace
[0-9]+
with the result from the following
Emacs Lisp code:
\,(format "0x%02x" (string-to-number \&))
. The
\&
in there will be replaced by the
matching text, so that will do what we need here, turning
10
into
0x0a
.
Let’s get some better tools
We could do better, though. I happen to already use a key chord to duplicate the current line, and we would need a function to Increment Number At Point. Those I found over at EmacsWiki were not to my taste as they were not able to figure out easily which base to use. So here’s a little Emacs Lisp example showing how to extend your favorite editor to have some Vim common features, which is why Emacs ships with Emacs Lisp in the first place.
(defun duplicate-current-line (&optional n)
"duplicate current line, make more than 1 copy given a numeric argument"
(interactive "p")
(let ((nb (or n 1))
(current-line (thing-at-point 'line)))
(save-excursion
;; when on last line, insert a newline first
(when (or (= 1 (forward-line 1)) (eq (point) (point-max)))
(insert "\n"))
;; now insert as many time as requested
(while (> n 0)
(insert current-line)
(decf n)))
;; now move down as many lines as we inserted
(next-line nb)))
(global-set-key (kbd "C-S-d") 'duplicate-current-line)
(require 'cl) ; destructuring-bind is found there
(defun dim:increment-number-at-point (&optional prefix)
(interactive "p")
(let* ((beg (skip-chars-backward "0-9a-fA-F"))
(hexa (save-excursion (forward-char -2) (looking-at-p "0x")))
;; force the prefix to hexa (4) we see "0x" before the number
(prefix (if hexa 4 prefix))
(end (re-search-forward "[0-9a-fA-F]+" nil t))
(nstr (match-string 0))
(l (- (match-end 0) (match-beginning 0)))
(fmt (format "%%0%d" l)))
(message "PLOP: %d" prefix)
(destructuring-bind (base format)
(case prefix
((1) '(10 "d")) ; no command prefix, decimal
((4) '(16 "x")) ; C-u, hexadecimal
((16) '(8 "o"))) ; C-u C-u, octal
(let* ((n (string-to-number nstr base))
(n+1 (+ n 1))
(fmt (format "%s%s" fmt format)))
(replace-match (format fmt n+1))))))
(global-set-key (kbd "C-c +") 'dim:increment-number-at-point)
So if you’re using
Another solution
Anyway, now that we are much better equipped, we can picture a better way to
solve the problem. Instead of using a macro that inserts the next counter
value, we can use one that duplicate current line, increment number at point
(and figures out on its own that the number prefixed with
0x
is
hexadecimal), and does that 254 times more. Then it’s all about reformatting
the text so that if fits nicely on screen, and for that the command
M-q runs the command fill-paragraph
is exactly what we need. The command
C-x f runs the command set-fill-column
can be used to set the maximum column we allow
Emacs to reach before going to the next line.
Our
Golf then becomes a
19
steps solution if you start with the cursor at
the
','
in the previous example:
C-x f 5 6 RET
F3 C-S-d C-c + F4
C-u 2 5 4 F4
C-SPC M-< C-n M-q
First, set the
fill column, then register a macro (in between
F3
and
F4)
that will duplicate current line (using
C-S-d
) then increment number at
point (using
C-c +
). Third line, replay that macro 254 times (
C-u 2 5 4 F4
).
Fourth line, select all those
hexadecimal numbers and fill the paragraph
they form correctly, so as to get:
All those tips for…
unsigned int hex[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
};
Conclusion
Thanks to the excuse of that challenge we now have a generic facility to increment a number at point in different common bases, which is a nice building block for all kinds of Emacs Keyboard Macros. We also now have a function to duplicate the line at point, which is something I use very often myself.
More importantly, we’ve been refreshing our memory on how to use some advanced replacement facilities wherein you can actually use inline Emacs Lisp code as a replacement pattern, and for the most interested readers here we have a good excuse to learn some more about Emacs Lisp programming.
The main thing I want to say is that using Emacs Keyboard Macros is an interactive process: you don’t have to pause your current activity to write some code in another language (here, that would be either Vim Script or Emacs Lisp) just to save a few minutes on a boring task.
How effective your are at solving that challenge, for me, is not at all about measure how many keystrokes you ended up using, it’s all about being able to get some precious help from your working tools without having to stop focusing on the main problem you are solving.
I wouldn’t ever get to write such
Emacs Lisp code when doing that kind of
editing once. I would only do that when I’m thinking I’ve just been doing a
boring task by hand one time too many already. Like for example copying and
pasting the
pg_backend_pid()
of the
PostgreSQL backend I’m working with at
the
psql
prompt so that I can attach
gdb
to it. I’ll get back to talking
about
pgdevenv-el later!
Hope you did enjoy that article, whose goal is to help you while you’re journeying in The Levels Of Emacs Proficiency.
Update
While looking at the docs for the
Keyboard Macro Counter to check how to
reset it without having to record the macro again, I just stumbled on this
part of the docs:
C-x C-k C-f runs the command kmacro-set-format
. So another
way to solve our problem with only facilities that come with a bare Emacs is
the following:
C-x f 5 6 RET
C-x C-k C-f 0x%02x RET
C-1 F3 SPC F3 , F4
C-u 2 5 4 F4
DEL C-SPC C-a C-q
We’re now at 30 keystrokes, so much more than previously, but it’s stock
Emacs features and that
kmacro-set-format
is a wonderful little tool you
might as well have a need for in the future.