18

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. Commented Mar 18, 2015 at 12:04
  • 2
    A common mapping for clearing the search highlight is: nnoremap <c-l> :noh<cr><c-l> Commented 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
    Commented Mar 15, 2018 at 19:52

5 Answers 5

18

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
    Commented 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 Commented 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
    Commented Mar 19, 2015 at 10:01
  • This has done the trick, although VimEnter specifically didn't work.
    – Rich
    Commented Mar 20, 2015 at 15:57
  • 1
    @shawnhcorey no. Why should I? Commented Feb 6, 2017 at 6:19
15

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
    Commented 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
    Commented Mar 18, 2015 at 14:53
  • 1
    @Rich how hard would it be to get used to using something like <Esc><Esc>?
    – Random832
    Commented Mar 19, 2015 at 15:28
  • @Random832 That's an intriguing idea.
    – Rich
    Commented 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. Commented Feb 5, 2017 at 21:28
3

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.

8
  • 3
    Have you ever tried the TermResponse autocommand? Commented Jul 30, 2018 at 16:12
  • 2
    @ChristianBrabandt I have now. It doesn't work well for me :(.
    – Rich
    Commented Feb 4, 2020 at 9:55
  • 4
    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 Commented Feb 4, 2020 at 10:32
  • 1
    @AntonSamokat I'd recommend using B. Layer's solution that I linked to over mine, but having said that, I'm still using my version across 4 different operating systems and many versions of Vim. What are the glitches you refer to? What terminal are you using?
    – Rich
    Commented Mar 21 at 9:36
  • 1
    @AntonSamokat That's a different-but-related issue which Ingo Karkat explains in his answer. My answer (and B Layer's) fixes the problem with mapping <Esc> during startup. Your problem is the <Esc> mapping interfering with Escape sequences. I suggest you look into the 'ttimeout' option, and if that doesn't help, perhaps ask a new question. It's definitely possible to fix, though. I don't use arrow-keys for movement, but I have mappings on them that work fine for me in the terminal.
    – Rich
    Commented Mar 27 at 10:22
2

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.

3
  • 1
    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
    Commented Oct 11, 2021 at 10:10
  • Welcome to Vi and Vim!
    – D. Ben Knoble
    Commented Oct 11, 2021 at 18:47
  • Tried this. Vim still opens in replace mode. Checked on Ubuntu 20.04 with Vim version: VIM - Vi IMproved 8.1 (2018 May 18, compiled Dec 07 2023 15:42:49) Included patches: 1-213, 1840, 214-579, 1969, 580-1848, 4975, 5023, 2110, 1849-1854, 1857, 1855-1857, 1331, 1858, 1858-1859, 1873, 1860-1969, 1992, 1970-1992, 2010, 1993-2068, 2106, 2069-2106, 2108, 2107-2109, 2109-2111, 2111-2112, 2112-2269, 3612, 3625, 3669, 3741, 1847 Commented Mar 20 at 18:26
1

Try this:

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

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

1
  • 2
    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
    Commented Feb 4, 2020 at 9:53

Your Answer

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

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