Vim macros behave strangely sometimes
03 Oct 2017The 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
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:
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.