17

I'm experiencing a weird problem with a normal mode mapping of Esc.

If you create the file escmapvimrc with the contents:

set nocompatible
set showcmd " Doesn't affect the problem: just makes it easier to see
nnoremap <Esc> :noh<CR><esc>

And then start vim using this vimrc:

vim --noplugin -u escmapvimrc

Then vim will start in operator-pending mode with a c command waiting for further input, displaying an empty file, and with the command-line displaying :noh.

If you remove the nnoremap line, then the problem goes away.

If you debug and step through everything you get the following output:

Entering Debug mode.  Type "cont" to continue.
/[...]/escmapvimrc
line 1: set nocompatible
>s
/[...]/escmapvimrc
line 2: set showcmd " Doesn't affect the problem: just makes it easier to see
>s
/[...]/escmapvimrc
line 3: nnoremap <Esc> :noh<CR><esc>
>s
/[...]/escmapvimrc
line 4: End of sourced file
>s
Press ENTER or type command to continue

After you press enter, the Vim startup screen is displayed, and underneath:

Entering Debug mode.  Type "cont" to continue.
cmd: noh
>s

The Vim startup screen then disappears, and you're in operator-pending mode, as described above.

What's going on?

EDIT: Behaviour is as described in Vim 7.3. In Vim 7.4.52, the nmap causes Vim to start up in Replace mode when starting Vim without a file. (If Vim 7.4.52 is started with a file, however, it also starts up with a c-command underway.) Either way, the problem goes away when the nmap is removed.

3
  • I did reproduce this with vim, but the command line did not show :noh for me. Doing the same with gvim did not show this behavior. Mar 18, 2015 at 12:04
  • 1
    A common mapping for clearing the search highlight is: nnoremap <c-l> :noh<cr><c-l> Mar 18, 2015 at 14:43
  • As a side note, you can use /alksdjflaskj to clear up search highlight which is quite fast as well.
    – Shahbaz
    Mar 15, 2018 at 19:52

5 Answers 5

17

Vim sends during startup some special terminal codes (that usually contain the <esc> key) to determine several things (colors, bs,...) If you mapped <esc> this will most likely confuse the parser of the return codes and weird things can happen.

Therefore, use your above map only after everything has been setup correctly (e.g. via a VimEnter autocommand).

6
  • 1
    Well, they're sent whenever the 'term' option is set. That's usually only during startup, but there may be scenarios where it is set at runtime.
    – jamessan
    Mar 18, 2015 at 20:25
  • In this particular case, this seems to be caused by may_req_ambiguous_char_width() which is only called on startup Mar 18, 2015 at 21:48
  • I was planning to try exactly this (which is why I didn't accept the other answer). Good to have confirmation it should work, though.
    – Rich
    Mar 19, 2015 at 10:01
  • This has done the trick, although VimEnter specifically didn't work.
    – Rich
    Mar 20, 2015 at 15:57
  • 1
    @shawnhcorey no. Why should I? Feb 6, 2017 at 6:19
14

The Linux terminal uses ANSI escape sequences (i.e. strings of characters starting with <Esc>) to send special keys to Vim, and as part of the communication protocol with which the application queries for its capabilities. Your mapping interferes with that, and thereby leads to these "strange" behaviors.

Therefore, don't map <Esc>. Use another key. The problem is less pronounced in GVIM, but I wouldn't recommend it there, neither.

5
  • Unfortunately, I've had this mapping almost since I started using Vim, so it's pretty well burned in to my muscle memory by now. Thanks for the explanation, though.
    – Rich
    Mar 18, 2015 at 14:22
  • I should probably add, for posterity's sake, the problem described is the only problem that I know is definitely caused by this mapping, and the only weird unresolved problem I can recollect having with Vim.
    – Rich
    Mar 18, 2015 at 14:53
  • 1
    @Rich how hard would it be to get used to using something like <Esc><Esc>?
    – Random832
    Mar 19, 2015 at 15:28
  • @Random832 That's an intriguing idea.
    – Rich
    Mar 20, 2015 at 10:18
  • 2
    All xterm programs will do this because they emulate VT-100 terminals. It has nothing to do with Linux. iOS, which is based on BSD not Linux, will also have xterms that emulate VT-100s. Feb 5, 2017 at 21:28
2

I tried setting up an autocommand to set the mapping later on in startup, but the problem still occurred.*

I eventually created an autocommand to occur the first time I enter Insert mode. This is obviously not a perfect solution, but for me it'll work most of the time, and it seems to be the best I can do:

FURTHER UPDATE: If you’re using a vaguely recent version of Vim, @BLayer’s answer to this duplicate question is a much nicer solution. (It uses the timers feature to delay setup of the mapping instead of my Insert mode hack.)

UPDATE: After using the longer version below without issue for a few years, I decided it was possibly slightly over-engineered, and since then I've used this much simpler version, which instead just resets the mapping every time you enter insert mode:

augroup escape_mapping
  autocmd!
  autocmd InsertEnter * call s:setupEscapeMap()
augroup END

function! s:setupEscapeMap()
  nnoremap <Esc> :noh<CR><Esc>
endfunction

The mapping doesn't need to be reset every time you enter insert mode, but it also doesn't do any harm for Vim to do so.

ORIGINAL VERSION:

if !exists('g:escape_mapped')  " Only need to set the mapping up once.
  augroup escape_mapping
    autocmd!
    " Create the autocommand, to fire when Insert mode is entered
    autocmd InsertEnter * call s:setupEscapeMap()
  augroup END
endif

function! s:setupEscapeMap()
  " Actually create the mapping
  nnoremap <Esc> :noh<CR><Esc> 

  " Now the map exists, so we won't ever need the autocommand again.
  let g:escape_mapped = 1

  " Tidy up the autocommand and group
  autocmd! escape_mapping InsertEnter *
  augroup! escape_mapping
endfunction

* I tried attaching it to various events: VimEnter, BufReadPost, BufWinEnter, and even CursorMoved(!), but these all seem to fire too early.

3
  • 2
    Have you ever tried the TermResponse autocommand? Jul 30, 2018 at 16:12
  • 1
    @ChristianBrabandt I have now. It doesn't work well for me :(.
    – Rich
    Feb 4, 2020 at 9:55
  • 3
    I just recently learned, that the TermResponse is not triggered for all the terminal related query commands, that vim just sends out. (t_RV, t_u7, t_RF, t_RB, and possibly others.) So this might also depend on your terminal Feb 4, 2020 at 10:32
1

Try this:

augroup escape_mapping
  autocmd TermResponse * nnoremap <Esc> :noh<CR><esc>
augroup end

cf https://stackoverflow.com/a/16027716/400545

1
  • 1
    This doesn't work well for me. After, replacing the mapping in my escapemapvimrc file with this, when Vim startup completes, I'm in command-line mode, with the command line containing the following contents: :83/94/95^G (that's a literal CTRL-G at the end). This bit of the :help seems to suggest this might not be the best time to set up the mapping: Note that this event may be triggered halfway executing another event, especially if file I/O, a shell command or anything else that takes time is involved.
    – Rich
    Feb 4, 2020 at 9:53
1

I have found that mapping ESC in /home/.vimrc will open vim with replace command. Instead, I did the exact mapping but in /etc/vim/vimrc file and the mapping is executed successfully without the popping of replace command at startup.

2
  • Interesting. It's not immediately clear to me from the documentation how using the system vimrc would change the behaviour of this: hopefully someone will be along later to explain why it does!
    – Rich
    Oct 11, 2021 at 10:10
  • Welcome to Vi and Vim!
    – D. Ben Knoble
    Oct 11, 2021 at 18:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.