为什么在 Vim 里要用 alt 键代替 meta 键
我发现最近写了两篇“看本文就够了”的文章,不免稍微感觉自己像那某些 CSDN 搞笑技术博主……
总之,最近尝试在 Vim 里配一个带 alt 的快捷键,发现还是有很多艰险的。上网搜了一大堆,总结在这里了!
终端是通过输字符来操作的
为什么你按 a 就能进 Vim 的插入模式?不是因为 Vim 在监听你的键盘或者检测你的按键之类的,而是因为一按 a ,你的键盘就往终端,往 Vim 的标准输入流里,输入了一个 a 字符。
也就是说,之所以你按键盘,终端会有反应,只是因为你每一次按键盘都在往终端里输东西。如果你没理解,可以先去学习一下标准输入输出。
按照这个说法,我按 ctrl + a Vim 都能检测到我按的不是 a ,难道是因为我把 ctrl + a 给输进去了?没错,你按 ctrl + a 键盘也会输出字符,对应的 ASCII 码是 \0x01 \(=1\) ,即这个“”引号里的字符(因为不是 ASCII 可视字符,所以你大概看不见引号里有字),表示“标题开始”的意思。
脱字符表示法
我们之所以可以用 ctrl + a 输出 \0x01 ,是因为键盘的 ctrl 键遵守一种叫做脱字符表示法的规则,这种规则允许你用 ctrl 搭配 ASCII 里的可视字符(就是能印在键盘上的字符,比如 akS2!#/ )来表示所有控制字符。这样子你的键盘就能输出全部 \(33\) 个控制字符加上 \(95\) 个可视字符总共 \(128\) 个 ASCII 字符了。
\0x01 的脱字符表示法是 ^A ,所以你可以用 ctrl + a 按出它来;再比如换行符 \0x0d 表示为 ^M ,就可以用键盘 ctrl + m 按出来,所以在终端里,键盘上的 enter 键其实是 ctrl + m 的别名——不信你在终端里试一试!其他的控制符号也都可以通过 ctrl 和另一个普通字符的方法组合出来,感兴趣可以在网上搜到所有控制符的脱字符表。
组合按键
根据上文我们知道在 Vim 里按 Page Up 能翻页,是因为按 Page Up 之后键盘往终端里输入了字符。
那输入了啥?普通按键加上 ctrl 好像已经能表示所有 ASCII 符号了,如果 Page Up 输出的也是 ASCII 符号,为什么键盘上只有一个 Page Up 能用来翻页呢?这是因为 Page Up 是由多个 ASCII 符号组成的。
通过 Linux 终端运行 showkey -a 并按下 Page Up 能看到
$ showkey -a
Press any keys - Ctrl-D will terminate this program
^[[5~ 27 0033 0x1b
91 0133 0x5b
53 0065 0x35
126 0176 0x7e
Page Up 实际上相当于几乎同时按下 ctrl + [ [ 5 ~ 一共四套按键。通过这种组合键的方法,就可以给终端表示不止 \(128\) 个单独的 ASCII 按键了。
如果你试试比如上下左右、 insert 、 home 之类的键,会发现基本都是由好多个字符组合起来的。
meta 键是啥
那么我们知道 ASCII 字符是 \(8\) 位长度,理论上可以表示 \(2^8=256\) 种字符。但因为第一位都是 0 ,长这样 0xxx xxxx ,所以只定义了 \(2^7=128\) 个字符,剩下 \(256-128=128\) 个位置没有定义字符。
到现在为止我们的键盘只能输入 ASCII 里定义的 \(128\) 个字符,那剩下 \(128\) 个开头是 1 的位置,我们的键盘就按不出来,是不是有点浪费?反正发明 meta 键的人可能是这么想的。
当你按住 meta 键,再按其他键,输出的就是开头改成 1 的 ASCII 字符,这种字符称为被“ Meta 化”的字符。比如按 a 输出的是 \0b01100001 \(=97\) ,那么 meta + a 就输出 \0b11100001 \(=97+128=225\) 。这样子加上这些被 Meta 化的 ASCII 字符,你的电脑总共就可以输出 \(256\) 种字符了。
为啥 meta 键没了
然而如果你学过 UTF-8 之类的编码(没有学过的话可以看这篇文章),相信看到这里你已经能猜测为什么 meta 已经从键盘上消失了——想象一下,一个认识 UTF-8 的终端怎么才能知道你的 Meta 字符是 Meta 字符而不是一个格式错误的 UTF-8 多字节字符?所以不只是这个按键没了,实际上大部分终端也不再支持这个按键了。
以上是比较浅显的描述,具体可以从这个 issue 不断往下探索。
没有 meta 怎么办
我们知道 Page Up 的原理是组合键,很多人觉得借鉴这个组合键方法来让 alt 代替 meta 很好,也是因为这俩按键在键盘上的位置好像差不多。
最流行的方法是在终端里用 ^[x 也就是 \0x1b\0x78 这个组合键来表示你按下了 alt + x (比如 Windows Terminal 默认就是这么干的,而且目前改不了),之后在 Vim 等软件里将 ^[x 映射为所谓 <M-x> 也就是 meta + x,就可以用 alt 代替 meta 了。
有意思的是,就像 enter 是 ^M 的别名一样, ^[ 也有别名 esc ,也就是说你按 alt + x 和快速按 esc x 两个键(得非常快速)在终端里是一样的。
怎么在 Vim 里用 alt 代替 meta
请看这篇文章。
总结一下就是把下面的代码放进 ~/.vimrc 里
function! Terminal_MetaMode(mode)
set ttimeout
if $TMUX != ''
set ttimeoutlen=20
elseif &ttimeoutlen > 80 || &ttimeoutlen <= 0
set ttimeoutlen=80
endif
if has('nvim') || has('gui_running')
return
endif
function! s:metacode(mode, key)
if a:mode == 0
exec "set <M-".a:key.">=\e".a:key
else
exec "set <M-".a:key.">=\e]{0}".a:key."~"
endif
endfunc
for i in range(26)
call s:metacode(a:mode, nr2char(char2nr('a') + i))
call s:metacode(a:mode, nr2char(char2nr('A') + i))
endfor
for i in range(10)
call s:metacode(a:mode, nr2char(char2nr('0') + i))
endfor
if a:mode != 0
for c in [',','.','/',';','[', ']','{', '}']
call s:metacode(a:mode, c)
endfor
else
for c in [',','.','/',';','{', '}']
call s:metacode(a:mode, c)
endfor
endif
for c in ['?','-','_']
call s:metacode(a:mode, c)
endfor
endfunc
command! -nargs=0 -bang VimMetaInit call Terminal_MetaMode(<bang>0)
:VimMetaInit
如果你在用 tmux ,那还要在 ~/.tmux.conf 里加一行。
set-option -g escape-time 30
博客园原文链接:https://www.cnblogs.com/QiFande/p/18911372,转载请注明。
如果你对本篇文章感兴趣,不如来看看肉丁土豆表的其他文章,说不定也有你喜欢的。
浙公网安备 33010602011771号