利器系列之 —— 编辑利器 Vim 之快捷键配置

无论我多爱 Vim,不容否认的一点是它的快捷键对于现在的大部分人来说比较诡异, 这其中的有很深的历史渊源。这篇文章将会重点介绍 Vim 的快捷键的由来以及个 人偏好的一些快捷键设置。

诡异的快捷键

看似诡异的 Vim 快捷键其实并不诡异,这一切的一切要从 Bill Joy 和那台 ADM-3A 说起。所有学计算机的人,或者说所有用电脑的人都应该记住 Bill Joy 这个人,因为他主导开发了 BSD Unix 系统。这个系统是整个操作系统发展史上的 一座丰碑,它给我们提供了虚拟内存、网络套接字、exC shell 还有 Vi 等 等强悍的功能,而这些东西大部分都是 Bill Joy 写的。虽然 BSD 已经停止开 发,但是它的后代还在不断的发挥余热,苹果的操作系统的内核 Darwin 就是基于 BSD 的。

Bill Joy 的作品太多,这里要说的是它的神作 Vi,这个后来发展成为 Vim 传世神器。Vim 的快捷键之所以这么诡异就是拜它所赐,当年 Bill Joy 编写 Vi 的时候年代还比较久远,那时候他们用的不是普通的键盘而是终端机,当时 Bill Joy 使用的终端机叫做 ADM-3A,它的键盘的按键分布和我们现在的普通键 盘的按键分布大有不同,我在 wikipedia 上找到一张它的键盘分布图如下:

adm3a

看完这张图你大概会明白为什么 Vim 使用 hjkl 作为方向键;为什么把如此常 用的回到普通模式功能绑定在现在很难输入的 Esc 按键上;为什么大部分的常用 键都绑定为单字符快捷键而命令输入快捷键要绑定在 : 这个需要按住 shift 才 能输入的按键上面;为什么用来绑定快捷键的 mapleader 会是 \ 这个按键,毕 竟快捷键是为了输入快捷,但是 \ 这个字符在大部分的键盘上输入都比较慢。

快捷键的修改

Vim 源自 Vi,它沿用了后者的快捷键,而这些快捷键中有一些目前显得太过于 别扭了。幸运的是 Vim 是一个高度可配置的编辑,你可以随意的更改它的快捷键 ,也可以创建自己喜欢的快捷键。

递归绑定和非递归绑定

Vim 的快捷键绑定分为递归和非递归两种,比如:

" 非递归方式
noremap <space> :
noremap : /

" 递归方式
map <space> :
map : /

第一个绑定最终的结果是输入空格会变成命令模式 :,输入 : 则会变成搜索按 键 /。第二个绑定最终的结果是输入空格最终变成搜索按键,而你没有办法再输入 : 进入命令模式(千万不要做这种配置,这里只是举了一个不恰当的例子)。

绑定模式

Vim 的快捷键绑定比较复杂,因为它可以基于不同的模式绑定不同的快捷键,而 Vim 包含六类的 mapping 模式,下面这段话摘自 Vim 的帮助文档:

There are six sets of mappings

  • For Normal mode: When typing commands.
  • For Visual mode: When typing commands while the Visual area is highlighted.
  • For Select mode: like Visual mode but typing text replaces the selection.
  • For Operator-pending mode: When an operator is pending (after “d”, “y”, “c”, etc.). See below: |omap-info|.
  • For Insert mode. These are also used in Replace mode.
  • For Command-line mode: When entering a “:” or “/“ command.

其中用的比较多的是 Normal, Insert, Command-line 这三种,这篇文章中主 要讲解的也是这几种模式的快捷键配置。这几种按键绑定模式的用法如下:

inoremap jk <Esc>
nnoremap <space> :
cnoremap <C-a> <Home>

这些绑定最前面的字符 [inc] 表示不同的模式。如果你没有指明你使用的是哪一种 模式,比如你直接使用

noremap <space> :

那么它表示同时绑定到 normal, visual, operator-pending 三种模式。

常用的快捷键绑定

避免输入 Esc

ADM-3AEsc 这个按键在目前的 Tab 键的位置,输入起来非常方便,但 是现在的键盘中这个按键输入起来非常的不方便,为了避免把手指移动到左上角,你 可以使用下面几种方式。

  1. 在你的系统中 Caps Lock 映射到 Esc 中。这个方式我不是很推荐,因为我 更喜欢把 Caps Lock 映射到 Ctrl 按键。

  2. 在输入普通模式命令之前先输入 Alt,因为大部分的终端编辑器会在输入 Alt 之后产生一个 Esc 按键,所以你输入 Alt-o 就可以直接在输入模式 中在当前行下面新建一行开始编辑。

  3. 绑定其他按键到 Esc 上,比如我把 jk 绑定为 Esc

    inoremap jk

    经过这个绑定,我可以直接在 insert mode 下输入 jk 退出编辑模式。

替换 :

Vim 的命令输入快捷键 : 需要输入两个字符(你需要先按住 SHIFT),对于一 个常用的快捷键来说,速度太慢了一点。我习惯把 绑定为 : 这样,我可 以直接输入空格进入命令模式。

noremap <space> :

快速切换到行首行尾

Vim 切换到行首和行尾的快捷键分别是 ^$,这两个字符在正则表达式中 也代表着同样的含义。这两个按键虽然比较容易记住(只要你了解一些基本的正则 表达式知识),但是它们比较难以输入,为了能够快速的切换到行首行尾,我给这 两个功能绑定了新的快捷键。

noremap H ^
noremap L $

使用大写的 H 切换到行尾,小写的 L 切换到行末。这两个按键原本绑定为快速 切换到页首和页尾,不过这两个功能我不是用的很多所以我把它们重新绑定到了行首 和行尾上面。

此外要提醒的一点是你可以使用 0 这个快捷键把切换到行首,还有就是如果你只 是想要在行首和行尾插入字符,直接使用 IA 就可以完成任务,没有必要使 用这两个快捷键定位到行首尾之后再进行插入操作。

大小写转换

我在系统设置中把 Caps Lock 按键映射到了 Ctrl 按键上,也就是说我的键盘 上目前没有大小写转换功能。在日常编程中,有些时候我们还是需要输入比较多的大 写字符,比如在 C 语言的宏定义大部分都使用大写字符。

Vim 中我比较常用的做法是先输入小写字符然后通过 gUw 按键转换成大小字 符。个人觉得 Vim 的大小写切换功能的快捷键绑定的不是特别直观。它使用 gu 表示转换成小写,gU 表示转换成大写;这和一般的 u 表示大写 l 表示小写 比起来直观性上相差较远且更不容易操作(gUw 其实要按四个按键)。我把大小写切 换功能的快捷键做了重新绑定。

nnoremap gu gU
nnoremap gl gu

这样你可以使用 guw 把当前的单词转换成大写,glw 把这个单词转换成小写。

更便捷的文字输入

Vim 的大部分的快捷键是使用可输入字符序列组成的,比如 hjkl 用来移动光标 。这种方式的一个好处是快捷键可以非常方便的输入,因为这些字符大部分都位于键 盘的核心输入区域。

这种方式最大的问题在于这些快捷键是可输入的字符,我们无法在把它们作为快捷键 的同时又把它们输入到文本中去。这个问题的解决方式有两种,第一是使用不同的模 式处理文本,比如 Vim 在普通模式下这些字符被映射成为快捷键而在输入模式下 这些快捷键失效变成可输入字符。第二种解决方案就是使用组合键,比如 Emacs 使用 C-fC-b 移动光标,因为组合键不是可输入的字符,前面提到的问题也就 不存在了。

Vim 的方式让快捷键变得更加的便捷,但是你需要不停的切换模式。比如我想要在 编辑模式下移动光标的位置,我只能使用角落里的方向键或者更传统的做法,切换回 到普通模式移动光标到合适的位置再进入编辑模式开始编辑。

这是一种不太方便的操作方式,为了可以在编辑状态快速的移动鼠标,我借鉴了 Emacs 的做法,定义一些组合键来完成这些事情从而避免不断的切换模式。

inoremap <C-k> <Up>
inoremap <C-j> <Down>
inoremap <C-a> <Home>
inoremap <C-e> <End>

这里定义了 C-[kj] 而没有 C-[hl] 因为 C-k 在输入模式下原本绑定为输 入一些特殊的字符,而 C-j 原本是插入新行。这两个功能我用的不是多,前者使 用本身就少,后者可以用 A-oEnter 来实现。

相对来说,C-h 的删除功能我用的较多,而 C-l 这个键和很多插件都有定义到 ,所以我没有绑定,如果你找到了更好的解决方式千万记得告诉我。

更便捷的命令输入

和前面提到的一点类似,命令行的输入我同样绑定了一些快捷键,方便快速的输入命 令行。

cnoremap <C-j> <t_kd>
cnoremap <C-k> <t_ku>
cnoremap <C-a> <Home>
cnoremap <C-e> <End>

上面这个绑定,使用 C-j 切换到下一条命令,使用 C-k 切换到上一条命令。使 用 C-a 切换到命令行首,C-e 切换到命令行尾就像普通的 shell 一样。

超级用户权限编辑

Vim 在编辑系统配置文件时经常会使用到,你也许也碰到过对一个文件编辑完成之 后才发现自己没有权限写这个文件。这个时候你只能使用 q! 丢弃编辑内容然后重 新以 sudo 权限编辑文件。

幸运的是,就连这么蛋疼的问题,Vim 也有解决的办法,你可以使用如下命令:

w !sudo tee >/dev/null %

我个人不是特别了解这个命令的具体工作原理,但是它确实有用。为了避免输入这么 一大段的命令,我把它绑定到了 sw 这个命令上

cmap sw w !sudo tee >/dev/null %

sw 原本是 swap 文件的写操作快捷键,不过因为我不是 swap 文件,所以我 把它重新绑定到前面这个快捷命令上去。

mapleader

关于 Vim 快捷键配置最后一个需要解释的概念是 mapleader,这是一个特殊的 字符,默认是 \

Vim 的大部分的快捷键都是可输入的字符序列,但是可输入的字符本身就不多,而 且 Vim 已经占用了很多。这就导致在单层空间里面能够提供给我们使用的字符严 重不足,我们绑定的快捷键很有可能和原本的快捷键产生冲突而覆盖它们的快捷键最 终导致快捷键失效,Vim 的各种插件绑定的快捷键让这种情况更加严重。为了解决 这种单层空间的快捷键冲突,你可以使用 mapleader 增加名字空间来解决这种冲 突,比如:

map <Leader>j <Plug>(easymotion-j)
map <Leader>k <Plug>(easymotion-k)

<leader> 这层空间中,把 jk 绑定为 easymotion 相关的插件,从而避免 和原本的 jk 快捷键产生冲突。通过这样的绑定之后你可以通过 \j 来触发 easymotion 相关的功能。

当然在目前的键盘中 \ 键比较偏僻,而 mapleader 在快捷键绑定用的很多,所 以我把这个按键做了更改,把它改成触手可及(原谅我的词语滥用)的 ;

let mapleader=";"

此外你不可以使用 <leader><leader>或者 <leader><leader><leader>... 形成更深层次的空间,不过这给记忆带来太大的难度,不推荐使用。


通过前面的这些例子你大概已经知道了如何去绑定自己喜欢的快捷键了,Vim 的可 配置性太高,我不可能在这里写下所有的快捷键配置,如果你觉得自己的快捷键用的 不顺手或者说你想要寻求新的快捷键功能,尽管定义自己喜欢的快捷键就好了。

下一篇文章将会重点介绍 Vim 的杀手级功能 —— 插件。