Vim Macros and Poor Decisions

🠔 Back to Blog Index


id: edcdab90-31f9-48f8-a2bc-bfb69e2953a3
tags: neovim,vim

I drafted my previous blog post in Markdown and converted it by hand to HTML. That was a poor decision on multiple levels. I do not have an automated rendering process because I do not use a static site generator or markdown converter at this time. Why? Because I want to make my own, and I'm stupid like that. The pain gives me incentive to solve the problem.

Tragically, in the meantime, I still need to publish content. While the brutal slog got me working directly on automating this process, I also came up with a short term solution to expedite the manual work: Vim Macros. They're handy and I use them every so often, but in this case they were essential for finishing the conversion process.

So yes, I deliberately made a poor decision but it ended up giving me something to share. If you need to convert Markdown to HTML by hand (or some other repetitive annoying task), consider Vim macros if you use Vim or a Vim-mode in your favorite editor.

♯Table of Contents

♯The Basics of Macros

In this post I'm referring to a macro as a sequence of commands stored to a register. The basic flow of macro definition and use consists of the following:

Defining a Macro

  • Press q to initiate macro definition.
  • Press a letter, a-z to select the register which will store the macro. Note that the macro definition will replace the contents of the register if you saved something else there.
  • Enter a series of commands.
  • Press q to finalize and store the macro.

Using a Macro

  • Press @<register> to run the macro once. If you stored your register to a, you would press @a.
  • Press @@ to repeat the last executed macro.
  • Add a number n prior to the @ to run the macro n times. For example, to repeat the macro stored in a three times, you would press 3@a.
  • Use the command :registers to view the content of all registers.

♯Converting Inline Code

I use prism.js, so all inline code looks like:

<code class="language-none">some inline code</code>

An example Markdown block might look like this:

I'm writing some paragraph, and want to add
`inline code` to my text.

The task to automate is taking the text from inside the backticks and placing it within the correct HTML. The first thing I do is take my HTML template and rip it into a register by selecting it in visual mode, and pressing "iy. Now to paste my HTML template I just need to enter the command "ip. The HTML template in this case is a code block without content:

<code class="language-none"></code>

With that in hand, I'm ready to define a new macro: /`^Myi`da`"iPa ^[F<"0P (as emitted by the :registers command). Let's break that down piece by piece:

  • /`^M search for the next occurrence of a backtick. ^M represents pressing enter.
  • yi` yank the text from within the backticks. Will remain available in register 0.
  • da` delete the backticks and everything contained within.
  • "iP paste the contents of register i before the current position.
  • a advance the cursor, enter insert mode, and add a space character. The previous commands have the HTML butting up against the next word. Note that this obviously doesn't work if the code is followed by a period or some punctuation -- this isn't a fully generalized macro, just one that hits on my most common use cases. Also deleting the space if needed is easy.
  • ^[ return to normal mode. This is the escape key.
  • F< move backwards to the nearest previous occurrence of the < character. This will place us at the beginning of the closing code tag.
  • "0P paste the contents of register 0 just prior to the current character. Since we're at the closing tag, it will populate the tag with the contents of the backticks that we ripped into the register.

This macro is imperfect and the point isn't to develop a 100% generalized converter. It's to make something good enough to automate conversion with some minimal (or no) modifications required.

My links have a URL and display text, with no special classes or additions.

The Markdown

This is text with [an inline link](https://meager-software.com/).

The HTML Template

Placed within register a.

<a href=""></a>

The Macro

/[^M"aPyi)F""0Pyi]F<"0Pf[df)
  • /[^M search for the next occurrence of an opening square bracket. ^M represents pressing enter.
  • "aP paste the contents of register a before the current position. The HTML template now dirctly precedes the Markdown.
  • yi) yank the text within the parentheses. The URL is now in register 0.
  • F" move backwards the the nearest " character. This puts us at the closing quote of the href.
  • "0P paste the contents of register 0 before the current position.
  • yi] yank the text within the brackets. The link text is now in register 0.
  • F< move backwards to the nearest < character. This puts us at the beginning of the closing tag.
  • "0P paste the contents of register 0 before the current position.
  • f[ move forward to the nearest [ character (the beginning of the Markdown).
  • df) delete all characters through the nearest ) character (the end of the Markdown link).

Once again there are some assumptions here, but this worked for all of my links.

♯Conclusion

I leave you with the following points:

  • Vim macros are great and can make repetitive tasks easy.
  • Don't be me. Just use a damn static site generator. Use your macros for joyous tasks like editing and/or refactoring code.