本文来自依云's Blog,转载请注明。
首先祝大家新年快乐!
一直都感觉Vim下快捷键不够用,于是在某一天,我开始使用Alt开头的组合键,然后发现了问题——
在很多终端中,Alt 组合键发送的是 Esc 前缀键码,而图形界面中则是置位最高位。举例来说,Alt-x在图形界面下向Vim发送的是ø
(在Vim插入模式下使用Ctrl-V Alt-x可以看到),其编码为0xf1
,而x的编码为0x78
,区别在于前者二进制编码的最高位是 1,而后者是 0。
而在gnome-terminal、konsole中则是另外一番景象。Alt-x和快速地按Esc x的效果是一样的,仅有xterm 和 rxvt 等终端可选地支持像图形界面的那样处理(参见Vim手册:help :map-alt-keys)。而且,使用置位最高位的终端将导致shell中的Alt-f之类的键绑定失效。
Emacs能处理这种不一致,但Vim不能,于是我一直是使用脚本,使得在终端下和图形界面下使用不同的键绑定。这样图形界面下没什么问题,但终端下比较郁闷:因为映射了Esc开头的键,而Esc是用于回到普通模式的,于是每次按Esc想退回到普通模式时都得等一秒('timeoutlen的值)。这个值又不能设小,不然\ww这种需要多次按键的映射就难用了。
前些天,偶然在帮助文档里看到了这个:
*:set-termcap* *E522* 需要 {option} 的地方,可以使用 "t_xx" 形式来设置终端选项。这些选项覆盖相应的 termcap 值。设置后,可以用于映射。如果 "xx" 包含特殊字符,须用 <t_xx> 形式: > :set <t_#4>=^[Ot 也可用来翻译普通键的特殊键码。例如,如果 Alt-b 产生 <esc>b,可用: > :set <m-b>=^[b (这里 ^[ 是真正的 <esc>,用 CTRL-V <esc> 来输入) 这个方法优于映射之处在于它能适用于所有情况。
也就是说,可以在终端下把Alt组合键都设置到Esc开头的键码,这样一是不用每次设置键映射时设置两个,更重要的是,其本质变了:这样的设置不是键映射,而是指定键码!这样会使用'ttimeoutlen'的值来等待后续键码,和映射无关了,我完全可以把它设置得很小。于是写出新的脚本:
1 " escalt.vim 控制台下让用 <M-x> 也可用 2 " Author: lilydjwg <lilydjwgATgmail.com> 3 " Last Change: 2010年12月15日 4 " ---------------------------------------------------------- 5 " Load Once: 6 if &cp || exists("g:loaded_escalt") || has("gui_running") 7 finish 8 endif 9 let s:keepcpo = &cpo 10 let g:loaded_escalt = 1 11 set cpo&vim 12 " ---------------------------------------------------------- 13 " Functions: 14 function Escalt_console() 15 for i in range(65, 90) + range(97, 122) 16 exe "set <M-".nr2char(i).">=\<Esc>".nr2char(i) 17 endfor 18 set ttimeoutlen=50 19 if &term =~ 'xterm' 20 set <F1>=^[OP 21 set <F2>=^[OQ 22 set <F3>=^[OR 23 set <F4>=^[OS 24 set <Home>=^[OH 25 set <End>=^[OF 26 endif 27 for i in ["", "c", "i", "x"] 28 exe i . "map Ï1;2P <S-F1>" 29 exe i . "map Ï1;2Q <S-F2>" 30 exe i . "map Ï1;2R <S-F3>" 31 exe i . "map Ï1;2S <S-F4>" 32 endfor 33 endfunction 34 " ---------------------------------------------------------- 35 " Call Functions: 36 call Escalt_console() 37 " ---------------------------------------------------------- 38 " Restoration And Modelines: 39 let &cpo= s:keepcpo 40 unlet s:keepcpo 41 " vim:fdm=expr:fde=getline(v\:lnum-1)=~'\\v"\\s*-{20,}'?'>1'\:1
注意到其中对于F1到F4等键进行了特殊的设置。没办法,这几个键特殊,这样设置我觉得是最优的解了。设置'ttybuiltin'也可以,但是经过一些时间的试用后发现有副作用,具体是什么我忘记了。
PS: SyntaxHighlighter 不支持 Vimscript,还好 Vim 有TOhtml
命令。
最新脚本在 GitHub 有。直接复制上面高亮过的代码是不行的。
Jan 01, 2011 08:51:08 AM
bz,新年快乐!!bz辛苦啦
Jan 01, 2011 10:02:58 AM
新年快乐,嘿嘿
Jan 01, 2011 06:36:56 PM
没怎么看明白为何要处理F1-F4?
Jan 01, 2011 09:52:25 PM
那你不处理它们试试,在 TERM=xterm 时按 F1 会产生 ÏP。
Jan 01, 2011 10:09:21 PM
我用xterm和gnome-terminal试了,没有什么异常。启动vim前预先export TERM=xterm过(本来是xterm-256color)
Jan 01, 2011 11:27:59 PM
xterm-256color 在我这里也一样。难道你的 Vim 没有编译 ++builtin_terms 支持,或者你关掉了 ttybuiltin?又或者,你的RP比较好?
Jan 01, 2011 11:59:45 PM
用的debian sid里的vim。有++builtin_terms支持,ttybuiltin也是开启的。
无论在普通还是插入模式,按F1都是打开帮助,F2~F4也和我设的快捷键一致
Jan 02, 2011 12:48:22 AM
还是 Debian 好啊,Ubuntu、 Arch 和 Fedora 都不正常的。。。
Mar 10, 2011 11:01:29 PM
1、Gentoo下不用处理F1-F2(xterm配置问题?)
2、百思不得其解,为什么在我机器上一进入for i in range(65, 90) + range(97, 122)
这个循环的时候会在一旦i=80(P),终端就会BEEP,然后 VIM做一些乱七八糟的动作。。。。能贴以下你的xterm配置文件么?
Mar 11, 2011 11:55:52 AM
我不用 xterm 的。。你可以 strace 看下那个时候 Vim 到底向终端输出了什么。
Mar 12, 2011 10:40:37 AM
具体是没看明白,应该自定义 自己绑定的快捷键吧。
Mar 12, 2011 10:42:47 AM
Aug 31, 2011 12:27:10 AM
能否倒过来,自动把 <Esc>x 变成 <A-x> 呢?
Aug 31, 2011 12:44:37 PM
不太理解你是什么意思。。
Aug 31, 2011 03:21:01 PM
说我开始理解有误,当我没问。
Aug 31, 2011 03:21:27 PM
是我开始理解有误,当我没问。
Sep 18, 2011 06:25:21 PM
那就转Debian吧 :)
Apr 20, 2012 10:00:16 AM
这玩意现在用得着了
Jan 11, 2013 02:18:09 PM
非常感谢 就在找这个呢
Aug 24, 2013 06:34:57 PM
let c='a'
while c <= 'z'
exec "set <A-".c.">=\e".c
exec "imap \e".c." <A-".c.">"
let c = nr2char(1+char2nr(c))
endw
这种方法好像更好...而且F1-到F4不会失效
不过两种方法都导致esc要等一小段时间才有效,烦........
Aug 24, 2013 10:19:29 PM
前半部分和我的第一部分一样。后半部分的 imap 一是太局限,二是会导致 Esc 键无法快速响应。使用键码的方式再配置 'ttm' 的设置能将 Esc 延迟降低到 'ttm' 的值。
Nov 04, 2013 05:35:05 AM
那這個呢?
http://vim.1045645.n5.nabble.com/How-to-Map-Alt-Key-tp3324666p3336194.html
Nov 04, 2013 04:34:02 PM
你是指 lainme 的代码吗?那个没我这里的全 ;-)
Nov 27, 2013 01:38:28 AM
非常感谢,很好用~
Dec 10, 2013 02:10:46 PM
在我使用vim里的alt映射之初也发现了这个坑爹的问题. 当时没有能找到这个map-alt-keys帮助文档. 所以一直我太清楚问题的原因. 不过由于知道主要的矛盾在esc键的延迟上. 我就想出了一个比较简单方法:
"使用更容易按到,但几乎从来用不到的"`"键代替<esc>的返回普通模式功能
imap ` <esc><left><right>
为了防止alt映射串位,所有映射中需要使用<esc>的地方都使用
<esc><left><right>代替,如果:
imap ^[y <esc><left><right>:x<cr>
这样做的缺点有:
所有和alt相关的映射还是需要写两个版本, 一个给vim用,一个给gvim用
<esc>键还是很慢,不过这时需要用到他的地方也很少了
vim无法正常输入"`"了,不过这个符号一辈子都用不到几回.真的需要输入的时候通过在其他地方写下然后拷贝到vim中
Dec 10, 2013 08:36:21 PM
如果你写 markdown 文档的话会经常用到 ` 的 =w=
其实有办法的,<C-v>` 这样子就可以直接输入了。
Dec 10, 2013 09:07:14 PM
对哦, 怎么没想到<c-v>`呢...
Dec 23, 2013 01:52:49 PM
已经弃用`映射改用云哥的escalt.vim了, 经测试目前出现的副作用是诸如
imap <c-j> <esc>gja
imap <c-k> <esc>gka
这样的映射无法正常工作, 不过该成下面的样子就可以了:
imap <c-j> <esc><left><right>gja
imap <c-k> <esc><left><right>gka
escalt.vim是个好东西哦, 不仅使<esc>键反应正常,减少了vimrc的长度,还能让linux的vim和gvim和windows下的gvim统一配置.云哥威武!
Dec 23, 2013 05:09:47 PM
嘻嘻,我很注重让我用到的各种平台、各种环境 Vim 功能的「优雅降级」的(于是我修改了好几个插件,这也是我一直不用 pathogen 之类的原因之一) =w=
escalt.vim 是在特殊情况下有问题的,比如通过慢速连接使用远程 Vim 的时候,Esc + 别的键会被当成 Alt+那个键。没办法的事情,总是会有问题的,所以我只能尽可能地适合(我遇到的)大多数场景。
Dec 24, 2013 01:00:29 AM
奋战到半夜, 发现imap,nmap和vmap映射中出现<esc>出错的问题可以通过禁止递归映射来解决
因此之前的:
imap <c-j> <esc><left><right>gja
imap <c-k> <esc><left><right>gka
更好的映射方式是:
ino <c-j> <esc>gja
ino <c-k> <esc>gka
所有映射中只要保证被映射的内容中不使用另一个映射
键都可以通过ino,nno和vno避免重映射问题.
Dec 25, 2013 09:48:20 PM
云哥:
function Escalt_console()
for i in range(48, 57) + range(65, 90) + range(97, 122)
exe "set <M-".nr2char(i).">=\<Esc>".nr2char(i)
endfor
" 10, 22, 34, 62, 124 can't be set
set <M-,>=,
set <M-.>=.
为什么会有一些快捷键不能被设置过来呢?
Dec 26, 2013 11:56:35 AM
因为那些是特殊字符,它们分别是 '\n', '\x16', '"', '>', '|'。你自己试试就知道了。有些即使可以设置,也会出现奇怪的行为。
Mar 21, 2014 10:55:07 AM
一定要这么复杂才可以么……想用个Alt这么困难……
Mar 21, 2014 12:16:38 PM
只是为了统一一下啦。而且就一个 .vim 文件哪里复杂啦?
Mar 21, 2014 01:11:53 PM
我的vim里有这样一句配置:
inoremap <silent> <buffer> <M-n> <c-r>=EchoFuncN()<cr>
不过贴上你的配置后还是不起作用,是我的人品问题吗……
我的terminal(mintty in cygwin32 on Win7)下在vim中输入Ctrl+v Alt+n 和输入 Ctrl+v ESC v 都会显示 ^[n
Mar 21, 2014 01:26:56 PM
可能还是没能理解你这段代码的意图 lol 貌似这段代码就是为了使Alt+x 和 ESC x 等同……
Mar 21, 2014 01:48:45 PM
对啊 ,所以如果一切顺利的话,你在这个 buffer 的插入模式按 Alt-n 是会触发那个映射的。不过那个 EchoFuncN() 是这样用的么,你需要 set noshowmode 才能看到提示的吧(如果有的话)。
Mar 24, 2014 11:09:37 PM
你这ID容易好像 chroot.
Mar 29, 2014 02:20:04 PM
嘿嘿是啊 很有迷惑性~
Mar 29, 2014 02:21:26 PM
明白代码意图就好说了 我最后映射了 插入模式下的 Ctrl+n/p 发现其实不会有冲突 这样反而更好用了
Nov 28, 2014 03:40:04 PM
有一个小问题哦 输入:. 会变成:® 需要: <C-v> . 才能输入.
看了下代码(虽然不太懂)里的range好像也没有涉及到 . 的ascii(46) 不知道是哪里引起的 谢谢~
Nov 28, 2014 04:04:46 PM
你输入的是 <alt>. 吧?
Nov 30, 2014 03:59:21 PM
抱歉回复晚了(不知道为什么没有回复邮件提示 >_)
在插入模式和:命令模式下输入.都会变成® 并不是输入<alt-.>
rc的话是在这 https://github.com/lythesia/arch/blob/master/home/.vimrc
Nov 30, 2014 04:27:00 PM
大概邮件提示坏掉了吧……
你只给出了 vimrc 所以我没有办法尝试重现。不过,你引用的 escalt.vim 文件是错误的。你从我的 GitHub 项目里取一份试试?
Nov 30, 2014 05:06:37 PM
我引用的是这里escalt.vim 和你github上一样的
https://github.com/lanjiann/escalt.vim/blob/master/escalt.vim
目前不在arch环境 待会会折腾下
(顺便发现gmail自动把is-programmer给列为spam了..)
Nov 30, 2014 05:40:38 PM
那个不一样的,只是在 GitHub 上看起来一样而已。
Nov 30, 2014 06:04:44 PM
没有看出^[真是对不起...问题解决了 谢谢:)