[vim/vim] Differentiate/decouple Esc key on keyboard from escape of escape sequences (#7946)

147 views
Skip to first unread message

reportaman

unread,
Mar 10, 2021, 7:25:42 AM3/10/21
to vim/vim, Subscribed

I would like to press physical Esc key on my keyboard only once to exit insert mode & enter normal mode, irrespective of other usual settings in .vimrc. With my .vimrc settings, I always need to press Esc key twice to fully exit insert mode. With same .vimrc loaded in neovim, I just need to press Esc once to exit normal mode.

As a test/proof, try something like: :imap <Esc> <Nop> in vim and neovim; enter insert mode and press arrow or mouse keys. Cursor would scroll correctly in neovim, but in vim we will see partial escape sequences being appended to where we started scrolling.

The solution is to decouple physical Escape key on keyboard from escape sub-sequence ^[, and vim can then (like neovim) detect Escape key on keyboard to always enter normal mode from insert mode on single keypress. This decoupling could also allow remapping physical Escape key to other keys (again like neovim one source)

Things I tried (TL;DR): I have tried many possible workarounds (like mapping F1 key to <Esc><Esc>), but they are all ugly IMHO (F1 is shared with screen brightness on my computer, and one keyboard I have doesn't even have dedicated function keys (requires pressing two keys)). Other trials like inoremap <Esc> <Esc><Esc>, make rest of the keys that generate some escape sequence like ^[ dangerous (as they write spurious codes to the buffer). Yet another solution I tried, successfully moves vim out of insert mode on pressing escape once, but pressing any arrow keys/ delete/mouse keys also exits it out of insert mode. The popular inoremap jj <Esc> moves the cursor to left by one position anytime I press j; even if it worked I would never set this. In short there seems to press Esc key once, and exit insert mode once a user has some reasonable .vimrc file.

Given Vim is modal, and users most often move between insert and normal mode, it is worth the optimization.


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or unsubscribe.

Bram Moolenaar

unread,
Mar 10, 2021, 8:35:54 AM3/10/21
to vim/vim, Subscribed

You can do this by putting the terminal in 8-bit mode. Then instead of ESC [ it will send the one byte CSI character. No "escape sequences" will then start with the ESC byte.
Problem is that the tendency is that terminals use the ESC prefix for meta. That's a terrible choice. It works fine most of the time, which is why users tend to do this, but it always causes a problem eventually.
Check your options for timeouts, you probably have ttimeoutlen set to a larger number. For most situations the value 100 (as it's set in defaults.vim) works well.

reportaman

unread,
Mar 10, 2021, 4:21:38 PM3/10/21
to vim/vim, Subscribed

@brammool Thanks! I had set notimeout & given by default ttimeout is off, my settings made it never timeout on key codes. This is what I did, and resolves it solves my problem:

set notimeout
set ttimeout
set ttimeoutlen=10

A couple of questions:

  1. Am not a terminal emulator expert, I wonder if terminal emulators really send partial key codes to a listening program when arrow or mouse keys are pressed? In my imaginary world, it should send complete sequences at once to a listening program. In that case ttimeoutlen=0 should work (src), and the real value of setting ttimeoutlen to large value would be for users to test/play with key-code stuff.

  2. :h ttimeoutlen suggests it is also used to timeout <C-\><C-n> "... Also used for CTRL-\ CTRL-N and CTRL-\ CTRL-G
    when part of a command has been typed. ...". I don't see that happening. When I open a terminal window using :terminal, and do this: <C-\> (wait few seconds) <C-n>, it works (and am happy about that as I cannot enter <C-\> & <C-n> with 10ms delay in between). So what do you mean by that statement?

Bram Moolenaar

unread,
Mar 11, 2021, 1:13:47 PM3/11/21
to vim/vim, Subscribed

Yes, I have seen each byte arrive separately. It depends on the system and what kind of connection is being used (when using a remote terminal). That's also why it works fine for most people until they start a remote connection.
For 'ttimeoutlen' note the remark "when part of a command has been typed". Thus not the whole command.

Bram Moolenaar

unread,
Mar 11, 2021, 1:13:50 PM3/11/21
to vim/vim, Subscribed

Closed #7946.

Reply all
Reply to author
Forward
0 new messages