jsborjesson Yet another dev blog

Vim macros behave strangely sometimes

The vague title signifies how I've been categorising this issue in my head for a long time. There's basically a ghost messing with some of the macros I try to write, making them do something completely different, and making me look stupid and eventually giving up.

Broken macro

Unsuccessful Vim macro usage

In this gif I try to create a macro to change some comment headers in my dotfiles to a different style (one that creates Vim folds).

Basically I want to change this:

########################################
# Copy / paste
########################################

To this:


# }}}
# Copy / paste {{{

But somehow it just deletes the first line and then stops.

Infuriating.

The problem

If we inspect the recorded macro (you can paste it into the buffer with "qp, "paste from the q register"), we can see that this is what Vim saved:


C# }}}^[jA {{{^[jdd

Those ^[ are how Esc is represented in the terminal. It is not two characters, it is a so called control sequence. In fact, this is how all combinations of control and some other key are represented.

Pressing Ctrl + K is represented as ^K.

Pressing Esc is the same as pressing Ctrl + [.

It is frequently recommended to use Ctrl + [ instead of Esc in Vim, since it can be more ergonomic to press. This is not a separate mapping, Vim simply cannot tell the difference.

Even further, combinations of Alt (or Meta, or Option) and other keys, are represented as Esc followed by that key.

Pressing Alt + J is represented as ^[j

Pressing Alt + J is the same as pressing Esc followed by J.

Herein lies the issue. When Vim executes the macro, it thinks that we are pressing Alt + J, and not Esc followed by J. This issue reveals itself only in the macro, since when we push the actual keys there's a delay between pressing Esc and J. This timeout can be set very low, but a macro is always too fast. (See :help ttimeout)

The workaround

Using Ctrl + C will also get you out of insert mode, and using that in the macro works perfectly:

Successful macro usage

I've tried playing with the timeout settings to no avail, and <C-c> does not exactly equal escape, so mapping escape to that might cause some other issues. I hope understanding this issue can reduce the frustration for someone else dealing with this hiccup, but currently I don't know a reliable way of fixing it other than being aware and carefully avoiding it.

If anyone knows a way to fix this for good, please let me know.