Eliminating ESC delays in vim

Published on Monday October 01, 2018

Context

After using vim a lot at work, I got progressively more annoyed with the delay that occurred when switching from insert mode into command mode. I would press escape, and then after an annoyingly noticeable delay, the INSERT at the bottom.

In fact, I use lightline (yet another powerline, but very lightweight, configurable, and easy to install). I highly recommend it if you want something lightweight. If you want more integration and use zsh (as you should), go for powerline. This plugin makes it very easy to see what mode you're in, but also has the side effect of making any lag in the mode transition very noticeable and frustrating. And when you notice it, you cannot un-notice it ever agin.

The reason I never really thought I could remove that, was that I thought it was necessary for other keys as an escape character. Examples of such keys are the arrow keys. Indeed, setting the timeout for escape to zero reduces that delay but also breaks stuff. Bummer.

Solution

Turns out, that you can separate the delays for those, and apply different timeouts. The Vim doc defines timeoutlen and ttimeoutlen. Now I'm going to simplify it a bit, partly because it's complicated and I want to make this miniguide simple, and partly because I don't want to read through and understand all the intricate details of this nuance myself. Sorry. If you find yourself desiring more information, I'll gladly refer you the wikia page that provides all the details.

In short, timeoutlen specifies the timeout for the length of a mapped key, whereas ttimeoutlen specifies the timeout for a key code.

Great now that everything is cleared up, we can ... jk let me explain what those really mean. A keycode is a representation of a key press, and a mapped key sequence is a succession of keycodes that represents an action.

Explanation

More specifically, there are two types of keycode: the terminal key codes and the vim ones. The former change from one terminal to another, the latter do not. Both are ways of representing a key. Vim maintains a mapping between them here. So when you press a modifier key together with a normal key (eg. control F1), your OS sends that to vim, which will then be represented by vim as a single "unit".

An example might help clarify this. Suppose your terminal maps escape to ^[ (the keycode for escape), and this is is often the case. Now suppose you map (or, more likely, some existing mapping is already present) that maps ^[N4 (making this one up) to some action (such as inserting the current time in the open buffer, loading your ~/.vimrc, or whatever else). Now suppose that ^[N4 is actually the keycode for some key combination, such as control-n.

In the case above, if you set a long ttimeoutlen, vim will wait to see if you meant escape or control-n. If it's short, it will assume it's just escape sooner.

Setting timeoutlen works within vim, to control how multikey combinations resolve. These are combinations manually entered by the user, not combinations made by the OS. For example, if you map kk to go up 10 lines (not very practical, but bear with me here), then timeoutlen controls the amount of time that needs to elapse for vim to consider that k as 'up one line' and stop waiting for a potential second k to arrive.

That was a lot more tedious to explain then expected, sorry if you're more confused than before. In any case, copy pasting the below should solve the problem for you.

Ok great, now what do I copy paste into .vimrc?

Thus, in my .vimrc, I have:

set timeoutlen=1000
set ttimeoutlen=0

Now the latter might be a little presumptuous but it's been working for me so far. If it causes you grief, just set it to something like 50ms, should still be okay.