Adam Sjøgren's blog <p><strong>Saving and restoring an Emacs macro</strong></p> <p>From time to time I add domains to a configuration file, <code>header_checks</code>, for my mail server, <a href="https://www.postfix.org/" rel="nofollow noopener noreferrer" target="_blank">Postfix</a>, to block them as spam.</p> <p>To do this, I copy the email-address from a spam email, paste it into the configuration file, and modify it to escape the <code>.</code> and remove up until the <code>@</code>.</p> <p>Doing that for a lot of spam is of course tedious, and so I usually create a macro in Emacs to do the pasting and massaging.</p> <p>However, creating that macro is a bit tedious and that makes me not do it for the first couple of spams. Until I get too annoyed. So it keeps me from updating the checks. Not great.</p> <p>Emacs is capable of generating the lisp code corresponding to a macro, so I thought "Hah, I will save the macro in a comment in the file, then I can evaluate it when I'm updating the file, and I don't have to record the macro every time!"</p> <p>Alas, the command <code>M-x insert-kdb-macro RET</code> inserted some code that gave an error when evaluated - how odd?!</p> <p>At first I assumed I was doing something wrong - the documentation only mentions saving a named macro and I was saving the last, unnamed one - but after looking at <a href="https://git.savannah.gnu.org/cgit/emacs.git/tree/lisp/macros.el#n69" rel="nofollow noopener noreferrer" target="_blank">the lisp code of the function</a> <code>insert-kbd-macro</code> (Emacs makes this easy), it was clear to me that it was supposed to work: it contains specific code to handle the last macro case.</p> <p>I also tried an earlier version of Emacs (I usually run a development version), the one in Debian stable - version 28.2, and there the code generated was different, and, even more interestingly, worked!</p> <p>So, I created a bug report for Emacs (<a href="https://debbugs.gnu.org/cgi/bugreport.cgi?bug=77317" rel="nofollow noopener noreferrer" target="_blank">#77317</a>) and was swiftly told by one of the current Emacs maintainers that the documentation only says it works for named macros.</p> <p>I argued that the function specifically has a branch for the last (and unnamed) keyboard macro, and that the functionality was broken in 2022, and Cc'ed the previous Emacs maintainer who made the breaking change (which was an improvement to the other branch of the function).</p> <p>He asked for an explanation of my use case, and after I supplied that, he made a change to fix the problem, which I tested swiftly; it worked, and <a href="https://git.savannah.gnu.org/cgit/emacs.git/commit/lisp/macros.el?id=10eb57169da01f3f96a5954aa42673d10086824f" rel="nofollow noopener noreferrer" target="_blank">pushed it to</a> <a href="https://git.savannah.gnu.org/cgit/emacs.git/commit/lisp/macros.el?id=513a05dd8761aebc14ffe4ee0a8a6e96feb10531" rel="nofollow noopener noreferrer" target="_blank">the development version of Emacs</a> a couple of days later.</p> <p>Nice!</p> <p>So, now I could easily generate the code to restore the macro and put it in a comment in the file.</p> <p>Shortly after it hit me: Emacs can evaluate code from a file when loading it, I could have it restore the macro automatically on opening the file!</p> <p>A quick 3 lines at the bottom of the file later:</p> <pre><code># Local Variables:
# eval: (setq last-kbd-macro (kmacro--keys (kmacro "| C-y M-<left> <left> [ <right> ] C-r @ <right> M-<backspace> C-s ) <left>")))
# End:
</code></pre><p>and now, more than ever, Bob is indeed my uncle!</p>