Mapping <esc> in vimrc causes bizarre arrow behaviour

The mapping

nnoremap <esc> :noh<return><esc>

will conflict with so called "grey keys" and I believe that it should be used either in GVim only or in terminal Vim by someone who does not use special keys like arrows.

From what I know (and guess) how Vim processes keys, I would say that it's impossible to do anything with this. For Vim to recognize special key all its components should go in a row, so when you press Arrow Left Vim gets the following sequence of codes:

<esc> [ D

But after your mapping Arrow Left becomes:

: n o h l <cr> <esc>

[ D

Vim sees two separate sequences and treats <esc> as a single press of Escape key, thus next two codes of Left Arrow key lose their special meaning.

So I suggest you to map :noh to some other key sequence (e.g. to one starting with <leader>, see :help mapleader; I don't recommend you to use F-keys, using them is as bad as using of arrow keys).


This solution preserves the ESC mapping to :nohlsearch.

The comment on this answer explaining why this is happening tells us that the root cause is the TermResponse behavior of vim. This can be compensated for by wrapping the mapping in an autocommand for the TermResponse event.

This ensures that the binding doesn't happen until after the term response is set, which prevents Esc from also sending a string like ]>1;3201;0c to vim.

Change your line in vimrc to this:

augroup no_highlight
    autocmd TermResponse * nnoremap <esc> :noh<return><esc>
augroup END

The augroup commands are not strictly necessary, but they prevent multiple mappings when you reload your vimrc without quitting vim.

EDIT: If you also use a graphical vim like Gvim or Macvim, the TermResponse event will not fire. Assuming you use a single vimrc, you'll need some additional code like

if has('gui_running')
  nnoremap <silent> <esc> :nohlsearch<return><esc>
else
  " code from above
  augroup no_highlight
    autocmd TermResponse * nnoremap <esc> :noh<return><esc>
  augroup END

end

Problem is that when you press an arrow terminal emits something like <Esc>OA. Vim part that supports terminal seems to use the same mapping mechanism to do the job as you are using: while nmap <Esc>OA will tell you nothing, call feedkeys("\eOA") will move one line up and call feedkeys("\eOA", 'n') will add letter A beyond current line. With your mapping being noremappable you forbid vim to use <Esc> as a part of the key. The problem is that you need remappable mapping here, but can have remappable mapping without it being recursive as well only if it starts with {lhs}, but <Esc>:noh<CR>OA is not going to work. I thought the following code will (it uses <expr> and function with side effect to make <Esc> be the first character of the actual {rhs} and still launch :noh), but in fact it does not:

function s:NoHlSearch()
    nohlsearch
    return "\e"
endfunction
nmap <expr> <Esc> <SID>NoHlSearch()

. I have no other idea how to solve the problem of having non-recursive remappable mapping which includes {lhs} but not at the start.


The cause had been explained well, but solution was not mentioned. However there is a straight one.

If you’ll tell to Vim explicitly that there are key sequences starting from <esc>[

:nnoremap <silent><esc> :noh<CR>
:nnoremap <esc>[ <esc>[

than when single <esc> will be pressed Vim will wait for a second (or different time, see :h 'timeoutlen') or for a next key (second <esc> for example) and only then replace it with :noh<CR>.

Tags:

Vim