1
1
2011
47

让Vim在图形界面与终端中的Alt组合键相同

本文来自依云'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 有。直接复制上面高亮过的代码是不行的。

Category: Vim | Tags: linux vim | Read Count: 18800
o(∩∩)o...哈哈 说:
Jan 01, 2011 08:51:08 AM

bz,新年快乐!!bz辛苦啦

lainme 说:
Jan 01, 2011 06:36:56 PM

没怎么看明白为何要处理F1-F4?

Avatar_small
依云 说:
Jan 01, 2011 09:52:25 PM

那你不处理它们试试,在 TERM=xterm 时按 F1 会产生 ÏP。

lainme 说:
Jan 01, 2011 10:09:21 PM

我用xterm和gnome-terminal试了,没有什么异常。启动vim前预先export TERM=xterm过(本来是xterm-256color)

Avatar_small
依云 说:
Jan 01, 2011 11:27:59 PM

xterm-256color 在我这里也一样。难道你的 Vim 没有编译 ++builtin_terms 支持,或者你关掉了 ttybuiltin?又或者,你的RP比较好?

lainme 说:
Jan 01, 2011 11:59:45 PM

用的debian sid里的vim。有++builtin_terms支持,ttybuiltin也是开启的。

无论在普通还是插入模式,按F1都是打开帮助,F2~F4也和我设的快捷键一致

Avatar_small
依云 说:
Jan 02, 2011 12:48:22 AM

还是 Debian 好啊,Ubuntu、 Arch 和 Fedora 都不正常的。。。

broken 说:
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配置文件么?

Avatar_small
依云 说:
Mar 11, 2011 11:55:52 AM

我不用 xterm 的。。你可以 strace 看下那个时候 Vim 到底向终端输出了什么。

o(∩∩)o...哈哈 说:
Mar 12, 2011 10:40:37 AM

具体是没看明白,应该自定义 自己绑定的快捷键吧。

o(∩∩)o...哈哈 说:
Mar 12, 2011 10:42:47 AM

 o(∩∩)o...哈哈  可以发表请呀,这个是你自己弄的了!!!

自由建客 说:
Aug 31, 2011 12:27:10 AM

能否倒过来,自动把 <Esc>x 变成 <A-x> 呢?

Avatar_small
依云 说:
Aug 31, 2011 12:44:37 PM

不太理解你是什么意思。。

自由建客 说:
Aug 31, 2011 03:21:01 PM

说我开始理解有误,当我没问。

自由建客 说:
Aug 31, 2011 03:21:27 PM

是我开始理解有误,当我没问。

Fermat 说:
Sep 18, 2011 06:25:21 PM

那就转Debian吧 :)

fanhe 说:
Apr 20, 2012 10:00:16 AM

这玩意现在用得着了

buzz 说:
Jan 11, 2013 02:18:09 PM

非常感谢 就在找这个呢

tracyoyone 说:
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要等一小段时间才有效,烦........

Avatar_small
依云 说:
Aug 24, 2013 10:19:29 PM

前半部分和我的第一部分一样。后半部分的 imap 一是太局限,二是会导致 Esc 键无法快速响应。使用键码的方式再配置 'ttm' 的设置能将 Esc 延迟降低到 'ttm' 的值。

hepha 说:
Nov 04, 2013 05:35:05 AM

那這個呢?
http://vim.1045645.n5.nabble.com/How-to-Map-Alt-Key-tp3324666p3336194.html

Avatar_small
依云 说:
Nov 04, 2013 04:34:02 PM

你是指 lainme 的代码吗?那个没我这里的全 ;-)

ceclinux 说:
Nov 27, 2013 01:38:28 AM

非常感谢,很好用~

pangchol 说:
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中

Avatar_small
依云 说:
Dec 10, 2013 08:36:21 PM

如果你写 markdown 文档的话会经常用到 ` 的 =w=
其实有办法的,<C-v>` 这样子就可以直接输入了。

pangchol 说:
Dec 10, 2013 09:07:14 PM

对哦, 怎么没想到<c-v>`呢...

pangchol 说:
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统一配置.云哥威武!

Avatar_small
依云 说:
Dec 23, 2013 05:09:47 PM

嘻嘻,我很注重让我用到的各种平台、各种环境 Vim 功能的「优雅降级」的(于是我修改了好几个插件,这也是我一直不用 pathogen 之类的原因之一) =w=

escalt.vim 是在特殊情况下有问题的,比如通过慢速连接使用远程 Vim 的时候,Esc + 别的键会被当成 Alt+那个键。没办法的事情,总是会有问题的,所以我只能尽可能地适合(我遇到的)大多数场景。

pangchol 说:
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避免重映射问题.

pangchol 说:
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-.>=.
为什么会有一些快捷键不能被设置过来呢?

Avatar_small
依云 说:
Dec 26, 2013 11:56:35 AM

因为那些是特殊字符,它们分别是 '\n', '\x16', '"', '>', '|'。你自己试试就知道了。有些即使可以设置,也会出现奇怪的行为。

Cherrot 说:
Mar 21, 2014 10:55:07 AM

一定要这么复杂才可以么……想用个Alt这么困难……

Avatar_small
依云 说:
Mar 21, 2014 12:16:38 PM

只是为了统一一下啦。而且就一个 .vim 文件哪里复杂啦?

Cherrot 说:
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

Cherrot 说:
Mar 21, 2014 01:26:56 PM

可能还是没能理解你这段代码的意图 lol 貌似这段代码就是为了使Alt+x 和 ESC x 等同……

Avatar_small
依云 说:
Mar 21, 2014 01:48:45 PM

对啊 ,所以如果一切顺利的话,你在这个 buffer 的插入模式按 Alt-n 是会触发那个映射的。不过那个 EchoFuncN() 是这样用的么,你需要 set noshowmode 才能看到提示的吧(如果有的话)。

Avatar_small
Fermat618 说:
Mar 24, 2014 11:09:37 PM

你这ID容易好像 chroot.

Cherrot 说:
Mar 29, 2014 02:20:04 PM

嘿嘿是啊 很有迷惑性~

Cherrot 说:
Mar 29, 2014 02:21:26 PM

明白代码意图就好说了 我最后映射了 插入模式下的 Ctrl+n/p 发现其实不会有冲突 这样反而更好用了

lythesia 说:
Nov 28, 2014 03:40:04 PM

有一个小问题哦 输入:. 会变成:® 需要: <C-v> . 才能输入.
看了下代码(虽然不太懂)里的range好像也没有涉及到 . 的ascii(46) 不知道是哪里引起的 谢谢~

Avatar_small
依云 说:
Nov 28, 2014 04:04:46 PM

你输入的是 <alt>. 吧?

lythesia 说:
Nov 30, 2014 03:59:21 PM

抱歉回复晚了(不知道为什么没有回复邮件提示 >_)
在插入模式和:命令模式下输入.都会变成® 并不是输入<alt-.>
rc的话是在这 https://github.com/lythesia/arch/blob/master/home/.vimrc

Avatar_small
依云 说:
Nov 30, 2014 04:27:00 PM

大概邮件提示坏掉了吧……

你只给出了 vimrc 所以我没有办法尝试重现。不过,你引用的 escalt.vim 文件是错误的。你从我的 GitHub 项目里取一份试试?

lythesia 说:
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了..)

Avatar_small
依云 说:
Nov 30, 2014 05:40:38 PM

那个不一样的,只是在 GitHub 上看起来一样而已。

lythesia 说:
Nov 30, 2014 06:04:44 PM

没有看出^[真是对不起...问题解决了 谢谢:)


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter

| Theme: Aeros 2.0 by TheBuckmaker.com