在vimrc中映射<esc>键会导致奇怪的箭头行为

22

我是一位快乐的VIM用户,虽然我承认我的熟练程度还有很长的路要走。我发现了这个不错的帖子:Vim clear last search highlighting 我想如果我每次进行搜索时不必敲打一个随机字符序列,那么我就可以变成更好的人。现在,我也正在使用来自这里的vimrc配置文件。

我的问题是,当我将nnoremap <esc> :noh<return><esc>这行代码添加到vimrc中时(不管我将其放在哪里似乎都没有关系),当我在命令模式下使用箭头键时出现了奇怪的行为,即从A到D的字母会出现在新行中并且我会被切换到插入模式。

肯定存在一些映射冲突,但我就是找不出来在哪里。

编辑: 根据答案中提到的情况,最终发现"Ultimate vimrc"部分与此无关,即使它是唯一的vimrc条目,上述nnoremap命令也会导致箭头键的行为发生改变。更改标题以使其更具信息性。

PS. 我知道我不应该使用箭头键,希望有一天我可以做到。


我曾经在使用数字键盘时看到过你所描述的情况,但从未见过箭头光标键出现这种情况。 - Michael Berkowski
1
如果你真的想停止使用箭头键,只需将它们重新映射为无操作即可(示例)。 - xaizek
我理解,但这个方法对我来说仍然有点过于激进了 :) - Tomek Kaftal
5个回答

21
映射
nnoremap <esc> :noh<return><esc>

这将与所谓的“灰色键”冲突,我认为它应该只在GVim中使用或者在终端Vim中由不使用特殊键(如箭头)的人使用。

根据我了解(和猜测)Vim处理按键的方式,我认为这是不可能的。要让Vim识别特殊键,所有组件都必须连成一排,因此当您按下 向左箭头 时,Vim会得到以下代码序列:

<esc> [ D

但是在您的映射之后,向左箭头 变为:

: n o h l <cr> <esc>

[ D

Vim看到两个不同的序列,并将 <esc> 视为单击 Escape 键,因此下一个 左箭头 键的代码失去了其特殊含义。

因此,我建议您将 :noh 映射到其他按键序列上(例如以 <leader> 开头的序列,请参阅 :help mapleader;我不建议您使用 F 键,使用它们就像使用箭头键一样糟糕)。


知道它是如何工作的很好,即使我不得不选择另一个解决方案。 - Tomek Kaftal
5
“<c-l>”是常用的映射方式。nnoremap <c-l> <c-l>:noh<cr>表示先按下“<c-l>”,再输入“:noh<cr>”来取消当前vim编辑器中高亮显示的文本。 - Peter Rincker
4
我一直是 map <silent> <Leader><Leader> :nohlsearch<cr> 的粉丝,当然这只是个人偏好。 - Neg_EV
鼠标=a存在类似的问题。 - Charlie Parker

18

原因已经被很好地解释了,但解决方案没有提到。然而有一个直截了当的方案。

如果你明确告诉Vim有从<esc>[开始的键序列

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

当单击<esc>时,Vim会等待一秒钟(或不同的时间,请参见:h 'timeoutlen')或等待下一个键(例如第二个<esc>),然后才使用:noh<CR>替换。


3
这段代码是Vim编辑器的命令映射,将按键序列 <esc>^[ 映射成按键序列 <esc><esc> - David Unric
这两行代码可以包含在.vimrc文件中吗?如果可以,是按照字面意思还是需要修改? - Ari
6
半年后... @fanuch 或其他人有同样疑问:是的,这两行代码可以放到您的 .vimrc 文件中。不需要加冒号,并确保在映射语句上下不要放置任何注释(最好在上面或下面放置一些注释,以便提醒您为什么使用它们或它们的功能)。半年过去了,嗯... - fede s.
这是一个对于这个问题以及其他相关问题的天才解决方案。 - Spidey
不幸的是,在Vim上的Git Bash(MSYS2,Windows 10)这里无法工作。 - Spidey

15

这个解决方案保留了ESC映射到:nohlsearch

这个答案的评论解释了为什么会发生这种情况,它告诉我们vim的TermResponse行为是根本原因。可以通过将映射包装在TermResponse事件的自动命令中来补偿此问题。

这确保绑定在设定响应之后才发生,从而防止Esc也向vim发送类似]>1;3201;0c的字符串。

将您的vimrc中的行更改为:

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

augroup 命令并非必需,但可防止在重新加载 vimrc 时出现多个映射而无需退出 vim。

编辑: 如果您还使用像 Gvim 或 Macvim 这样的图形化 vim,则 TermResponse 事件将不会触发。假设您只使用一个 vimrc,则需要一些附加代码,如下:

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

在我的情况下,简单地注释掉这行代码就可以了: nnoremap <esc> :noh<return><esc> 这是由别人编写的代码复制/粘贴时添加的。吸取了教训。 +1 让我走上了正确的轨道。 - Jason Elwood
1
我喜欢这个想法,但它对我没用。这里的两个版本(一个只适用于终端vim,另一个具有if / else以适用于GUI和终端)在终端运行时都会导致我的箭头键失效。我的终端vim是由苹果作为Mac OS X 10.11“El Capitan”的一部分提供的VIM 7.3;也许它取决于VIM的较新版本中的功能? - jbyler
TermResponse方法对我来说也不起作用。当Vim启动时,它处于命令行模式下,命令行包含以下内容::83/94/95^G(这是一个字面上的CTRL-G)。 - Rich

3
我对此很幸运。
if $TERM =~ 'xterm'
  set noek
endif
nnoremap <silent> <esc> <esc>:noh<cr>

缺点是在插入模式下无法使用功能键。
:h ek

很不幸,它对我也是同样的情况 :( - Tomek Kaftal
尝试不使用if语句。只需使用set noeknnoremap <silent> <esc> <esc>:noh<cr> - Peter Rincker
抱歉,这是我和我的终端机所使用的方法。我猜你接收到的按键码可能不同。你知道你的$TERM是什么吗? - Peter Rincker
@PeterRincker:根据文档(和我的测试),这个选项仅适用于插入模式。你确定它对你有所改变吗? - xaizek
抱歉,这对你不起作用。@xaizek:对我使用的机器很有效。如果我删除 set noek,每次在终端中打开vim时它都会以一些初始命令开始。我知道至少是 2c,因为我有 set showcmd - Peter Rincker

3
问题在于当你按箭头时,终端会发出类似于 <Esc>OA 的东西。支持终端的 Vim 部分似乎使用相同的映射机制来完成工作: 当你使用 nmap <Esc>OA 时什么都不会显示,但是 call feedkeys("\eOA") 会将光标向上移动一行,call feedkeys("\eOA", 'n') 会在当前行之后添加字母 A。由于您的映射是 noremap 类型,因此您禁止 Vim 使用 <Esc> 作为键的一部分。问题在于您需要可重映射映射,但是只有以 {lhs} 开头的映射才能具有可重映射性而又不会出现递归,但是 <Esc>:noh<CR>OA 是不能正常工作的。我认为以下代码会(它使用了 <expr> 和带有副作用的函数,使得 <Esc> 成为实际 {rhs} 的第一个字符,并仍然启动 :noh),但事实上并不是这样:
function s:NoHlSearch()
    nohlsearch
    return "\e"
endfunction
nmap <expr> <Esc> <SID>NoHlSearch()

我没有其他办法来解决这个问题,即如何拥有非递归可重映射映射,其中包括{lhs}但不在开头。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接