VIM之魅(下)

如果说Vim的理念是“减”字诀,那么实现这些理念的方法便是“增”字诀: 增加模式种类、增加移动方式、增加操作对象、增加操作方式、增加定制方式。

1. 增加模式种类

前文提到引入普通模式的重要意义,但Vim并未就此止步,还提供了另外四种基本模式: 选择模式(select mode)、可视化模式、命令行模式和ex模式。

选择模式与可视化模式的目的均是选择文本以备后续操作。前者类似Windows下的选择模式, 在方便程度与威力上远不及后者,故略去不谈。

在普通模式下,按下 v 、 V 、 Ctrl-V 将分别开启以字符、行或块为单位 的可视化模式,此后用户可以通过多次使用Vim的移动命令来选定所需文本。 例如,在普通模式下输入 v/the 并回车后,从光标初始处到此后最近的 the 之前都将被高亮。如发现并非所求,继续按 n 将使选择区域扩大到下一处 的 the 之前,以后还可多次用 j 或 k 来延伸或压缩所选区域的行,或者 反复用 h 或 l 进行左右微调,用 w 或 b 来增减被选单词, 用 f 或 t 确定字符,等等。 相比直接通过正常模式操作,可视化模式允许用户在对文本片段进行编辑 之前加以检视、调整和确认,因而更加直观、准确和安全,并且让所选文本成为 批处理的整体对象,增加了编辑效率,同时减少了操作的碎片化。 与用鼠标相比,若是操作短文本或规律明显的文本,速度多不及普通模式下的操作; 若是操作文本较长且不太规律的文本,则往往不及可视化模式下的操作。 比如,要选中从当前行到倒数第二行的文本,若中间相距较远,用鼠标定然慢而不便。 性急如我者,一不留神就可能把鼠标拖出桌面之外。 使用可视化模式则异常轻松: VGk,完毕。

可视化模式不仅支持行操作,还支持列操作,这也是比普通模式更便利之处。 比如要交换文件中头两列的字符, 可键入 gg<Ctrl-V>Gdp 。

命令行模式与ex模式均可执行ex命令,只是后者在执行后不像前者那样返回正常模式。 普通命令虽然丰富,但远远谈不上完备。比如,无法同时编辑多处文本片段, 无法在不离开当前编辑窗口的条件下操作其他文件,无法获取其他文件中的内容, 无法读取环境变量,无法执行外部命令,等等。 有了ex命令,Vim的潜能开始无限释放,一方面充分地利用了外部资源, 另一方面大大减少了用户离开Vim的概率,从而提高编辑效率。

ex命令源自Unix下的行编辑器(line editor) ex ,Vim将其扩充为 Vimscript语言,成为第三方开发插件的主要工具(也可用Perl、Python、Ruby等语言)。 由于在ex模式下可通过 :normal 执行普通模式下的命令,故而实际上完全涵盖了 后者。只是执行ex命令时前面需要冒号,后面需要回车,效率上有所不及。 根据个人需要,用户可自定义ex命令,也可把常用的ex命令通过 nmap 、 vmap 、 imap等映射为其他模式下的命令,以期最大限度地减少键击次数。

命令行模式除用 : 开启以执行ex命令外,还可用 / 或 ? 来启动正向 或反向的模式搜索,可用 ! 进行文本过滤(filter) (即:将所选文本输入给外部程序,并替之以输出结果),大大增强了Vim的 移动和编辑效率。

此外,在基本模式的基础上,还有六种衍生模式,暂略不提。 从以上可以看出,多种模式的并存令Vim鱼与熊掌兼得——功能上强大而灵活, 使用上便利而快捷。

2. 增加移动方式

真正让Vim有飞一般感觉的是其快速多变的移动能力。可谓上天入地,无所不至,无至不速。 上文提到HJKL代替方向键的意义,但那只能让光标偏移最小单位,并不适宜频繁地单独使用。 否则,那便不是飞行,而是爬行。由于Vim提供的移动命令过多,难免令人眼花缭乱, 为便于说明,特做如下分类,并列举一些典型但远非完备的实例。

行间移动:

行间移动是一种常见的需求,Vim提供了绝对行数和相对行数两种跳转方式。 这里有一个小窍门,为了便于确定移动行数,可通过 :set number 显示绝对行号, 或 :set relativenumber 显示相对行号。建议定制一个功能键(如F6), 让行号随时可以在相对、绝对和隐藏之间循环切换。平时默认为相对行号或许更高效些, 一是相对行号比绝对行号小,通常键入更快;二是相对移动的 j 、 k 命令比绝对移动的 G 命令少用一个shift键。

列间移动:

作为一种近距离微调,列间移动非常实用。除了跳至行首或行末以外,最有用的便是 f/F/t/T/;/, 系列命令了。可惜中文是多字节的,无法享用此功能。

结构移动:

在用户眼中,文本不是单纯的字符集合,不仅具有行列差别,更有结构差别。 常见的结构划分有:单词(word),句子(sentence),段落(paragraph) 和区块(section)等。 除了系统的默认设定外,Vim还允许用户自定义结构的界定方式。 有些遗憾的是,由于中文词与词之间没有分界,无法利用单词的跳转功能。


除基本的文本结构外,程序员还对语法结构感兴趣。 % 是一个方便的命令,它能让光标在匹配项之间来回游走。 除了 ( 与 ) 、 [ 与 ] 、 { 与 } 的括号匹配外, 还支持C风格的注释( /* 与 */ )与宏( #if 、 #ifdef 、 #else 、 #elif 、 #endif 等)匹配。 Vim更提供了matchit的插件(但并未默认安装),支持其他编程语言的匹配结构。 若仍嫌不满,用户也可自定义其他的匹配方式。 下面的命令是给程序员额外的福利——


Vim具有折叠功能,故而支持折叠结构的跳转。

屏幕移动:

在浏览文件时,用户往往更希望调整的是屏幕而不是光标。


鉴于滚屏操作十分常用而Ctrl键不便,建议用 nmap 将^f/^b/^d/^u/^e/^y 分别替换为空格键、Shift+空格键、Enter键、Backspace键、下方向键、上方向键。

搜索移动:

几乎所有的编辑器都有搜索功能,但很少有Vim这般强大和方便。 强大体现在它不仅支持最基本的正则表达式,还支持懒惰模式 ( lazy mode ), 甚至可以 跨行 匹配。方便则体现在搜索过程对用户友好,没有讨厌的弹出窗口, 支持高亮匹配,支持增量搜索(光标在关键词输入过程中即开始运动,不必等待回车键), 支持智能大小写判断,一键重复正向或反向搜索,等等。另外,特别推荐四个诱人的命令::


如果要在多个文件中搜索,可以用内部命令 vimgrep 或外部命令 grep 。 前者在正则表达式上更强大,也更通用(Windows下没有自带的grep),但速度不及后者。 我个人的选择是在配置中加上一行: set grepprg=ack -a ,以便让更好用的 ack 来取代grep。

定点移动

定点移动是Vim又一特色,迅速而精确的远程移动让浏览和编辑变得前所未有的轻松。

Vim的标记(mark)相当于一种书签,用来记录用户关注的热点位置。 假设用户对当前光标所处位置感兴趣,可输入命令 mx (x可以是任何字母)。 以后只要输入 `x 即可返回原标记处,或用 ‘x 返回原标记处所在的行首。 用作标记的字母有大小写之别。小写为局部标记,用于缓冲区内部跳转, 仅对当前编辑的文件有效,且在缓冲区关闭后失效。 大写为全局标记,可在不同的文件之间跳转,不因缓冲区关闭而失效(需设定 viminfo )。

比用户标记更有用的是系统标记。Vim贴心地在一些热点上留下暗记,以便用户回访。 例如,用户每次退出Vim时的光标位置都会被保留,最近十个分别用数字0到9来命名。 下面是其他一些实用的标记跳转:


所有标记均可通过命令 :marks 来显示,以供查询。要获得更好的视觉效果, 不妨试试 ShowMarks插件,它利用Vim的sign功能将隐性标记显性化了。

除了标记列表外,Vim还维护了一张变化列表。 该表记录了用户每次修改文本的位置,运行命令 :changes 即可察看。 相应的跳转命令是:


程序员在浏览代码时经常需要在不同的源文件中跳转(比如察看某个函数的定义), Vim为此提供了标签(tag)支持。 与标记不同,标签需要依赖外部工具如 ctags 、 cscope 等来产生。 使用命令 :tag <tagname> 可实现标签跳转,不过更简便的还是将光标置于 关键词之上,然后通过 Ctrl-] 转至其定义处,必要时可用 Ctrl-T 返回。 用鼠标亦可完成以上任务,但并不推荐。

最后,上述各种移动命令所产生的跳跃点均保存于跳转列表中(最多不超过100个), 可通过命令 :jumps 检视。这意味着用户可以在原来的轨迹上来回跃迁——

其他移动

还有一些其他类型的移动,试列举一二:

3. 增加操作对象

Vim丰富的移动方式让用户以最小的代价——包括手指、目光、时间和精力——把所感兴趣的 文本带入视线范围并将光标精确定位。 这只是编辑的第一步,下一步是在当前位置进行文本操作。 一般编辑器修改文本多通过Backspace键、Delete键、方向键并结合鼠标完成,效率低下。 究其原因,主要在于操作对象太过单一:要么是以字符为单位,粒度太小; 要么以高亮区域为单位,选定太慢。 Vim则不同,提供了各种粒度的操作对象,供用户在不同需求下选择,大大提高了编辑效率。

Vim的编辑命令具有统一的形式:数字 + 操作符(operator) + 文本对象, 表示对某一文本对象进行指定次数的操作。其中,数字部分为可选项,默认为1; 有些操作符后不接对象,正如不及物动词后不接宾语。 一切似乎都平淡无奇,直到文本对象与移动命令自然而奇妙地结合在一起,瞬时光芒四射。 具体地说,文本对象可由移动命令所扫过的字符片段来定义。 于是,有多少移动方式,便对应多少文本对象。 这已经是我们第二次看到移动命令的重用(reuse)了(前一次用于可视化模式下)。

举例来说,操作符 d 表示删除,移动命令 w 表示前进到下一单词,则 dw 将删除从当前光标至下一单词之前的所有字符。 假设当前光标处于某单词的首部, 2dw 将删除该单词及其后一单词。 如果知道移动命令本身也可加数字前缀,则 2d3w 将删除6个单词,与 3d2w 、 6dw 、 d6w 的效果相同。 类似地,操作还可以句子、段落、语法结构为单位, 以行或列为单位,以屏幕为单位,以匹配模式为单位,或以定点为界限,等等。

进一步地,Vim在Vi的基础上新增了其他的文本对象,用于可视化模式下的选择 和普通模式下的编辑。兹列几项如下——


不妨看一个典型用例。假设有一段文字:


用户希望将 Shen Xin 换成 Zhang Ming ,此时光标位于该行的头部。 Vi(不是Vim)的常见做法是: f" 到达第一个引号, l 右移一个字符, 然后 ct" 清空人名并进入插入模式。 而在Vim下,只要键入 ci" 即可达到同样效果,节省了一半键击。

再看一个HTML片段::


假定光标位于某个 td 标签内部,要拷贝整个HTML行(即 tr 标签块),Vi的一种做法是: ?<tr 并回车到达行首, y 开启拷贝操作, //tr>/e 指定行尾,再回车完成任务,共需十多次按键。 利用行数或段落移动可能会省一些键,但都不如Vim来得惬意: y2at 。

还不止于此,Vim甚至允许用户自定义文本对象。比如设置一个操作符待定模式 (opeator-pending mode,Vim的六种衍生模式之一)下的映射: omap af :normal [[v%<CR> (af意指“a function”) ,便建立了一个C语言风格 的函数对象。若觉得晦涩,不妨回顾一下, :normal 表示执行普通模式命令, [[ 跳至函数头部的 { 处, v 开启可视化模式, % 表示选择区的 末端为与 { 匹配的 } ,由此定义了一块函数区域,用 af 来命名。 同样地,用户可按缩进、折叠、语法或其他区域划分方式来定义文本对象。

除文本对象外,用鼠标或键盘产生的选择区域、用折叠命令隐藏的文本片段等皆可 作为整体的操作对象。

在命令行模式下,还可按行数范围或模式匹配来指定操作对象。 比如, %s/x/y 将所有行( % )中的第一个 x 换成 y , /第一章/+1,/第二章/-1d 将删除光标后“第一章”与“第二章”之间的所有文字, .-5,.+5w a.txt 把以光标为中心的11行文字保存到名为 a.txt 的文件中。

通过增加文本对象,Vim使文本操作的粒度更加多样化,从而提高了批量处理的概率。 同时,由于文本对象更趋结构化与语义化,让思维与文字之间的转换更加流畅自然, 从而减少了编辑失误的概率。

4. 增加操作方式

当用户把光标移至合适的位置,并选定合适的操作对象以后,剩下的就是执行具体操作了。

新增文本

最常用的操作是增加新文本。Vi在普通模式下用 a 、 A 、 i 、 I 、 o 或 O 切换到插入模式。Vim新增了 gI ,与 I 的区别是,它保证将 光标置于首列。此外Vim还增加了 gi ,便于从上回退出缓冲区的插入点继续工作。 一个貌似简单的插入操作便有这么多的花样,目的很明确,那就是尽量减少键击次数。 另外,恐怕很少人意识到以上命令均支持数字前缀。如在普通模式下输入 100a , 然后插入一些文字,当用户再次回到普通模式下时,方才输入的文字将自动重复一百次。 从这里再次印证两件事:一是Vim为减少按键可谓处心积虑——本来复制操作就支持数字前缀, 但仍为插入操作加此功能;二是插入模式的确被视为暂态,随时等待退出。

一般不建议在插入模式下使用命令,但 Ctrl-Y 与 Ctrl-E 有时还是有用的。 它们分别在插入模式下重复光标上行或下行对应列的字符。 这么做并不省手,但省眼省心,符合Vim的理念。

插入模式下另一对命令 Ctrl-P 与 Ctrl-N 可实现文本的自动补全, 以 Ctrl-X 开启的子模式可产生更具体的完成提示,如后接 Ctrl-F 指 文件名匹配, 后接 Ctrl-K 指字典匹配,后接 Ctrl-S 指拼写建议, 后接 Ctrl-O 指万能补全(omnicompletion)等。 不过经设置或安装插件,可尽量通过Tab键或Shift-Tab来完成, 如此更符合减少手指移动的原则。

删除文本

与插入操作相对的是删除操作。最普通也是最低效的方式是在插入模式下使用删除键。 使用 x 或 X 同样可删除字符,但不需退出普通模式,还支持数字前缀。 d 命令既可前接可视化区域,又可后接文本对象,是万能的删除方式。 作为频繁使用的行删除命令, dd 被设计为叠字无疑是明智的,而用 D 命令 代替另一常用的 d$ 也是省键之举。 除此之外,如前例所示,命令行下也能完成删除操作。

替换文本

替换操作的方式更加多样。最简单的是以字符为单位的 r (在可视化模式下也可批量替换), 如需连续替换可用 R 进入替换模式,如需删除若干字符后进入插入模式,可用 s 。 与负责删除操作的 d 命令相对应的是负责改动操作的 c 命令: {可视化模式}c 、 c + 文本对象 、 cc 与 C 。

文本编辑经常涉及大小写变换,Vim对此当然不会视而不见。 ~ 是大小写转换符,可将若干字符的大小写对换。如 6~ 作用于 aBcDeF 的结果是: AbCdEf 。 g~ 更强大,后可接文本对象。于是前面的命令可用 g~e 代替,虽然多敲一字符,却不必数字符,以手动换心动,效率只高不低。 利用可视化模式 ve~ 也可完成任务,但产生了可视化区域的副作用。 不出意料地, g~~ 将当前行所有字母进行大小写转换。 如果希望将目标字符固定为大写或小写,则用 gU 或 gu 命令。 类似地, gUU 将当前行所有字母变成大写, guu 将当前行所有字母变成小写。

对文本进行格式上的调整也是一项常规需求。 Vim提供了 J 与 gJ ,能将多行文字并为一行,极为实用。 < 、 > 分别将文本左移或右移一个缩进单位 (在插入模式下用 Ctrl-T 或 Ctrl-D )。 = 命令能自动调节缩进, gq 命令能格式化选定的文本。 按惯例,以上命令在应用于行时,均采用叠字: << 、 >>、 == 、 gqq 。 若需左、中、右对齐,则分别使用 :left 、 :center 、 :right 。

Ctrl-A 、 Ctrl-X 是一对鲜为人知但却非常有用的命令, 它们能分别 对光标之上或之后的数字进行增减运算,并且不仅支持十进制,还默认支持八进制和十六进制。 假设光标后有一个字串 0x2a ,输入 2<Ctrl-X> 后它将变成 0x28 , 且光标也移至 8 处。适当设置后,此二命令甚至能对单字母进行增减。

在命令行模式下可执行更复杂的替换操作。 命令 :s 可充分利用正则表达式的威力进行文本替换, 若要重复上次替换,只需键入 & , 若要对所有行重复上次替换,只需键入 g& 。 前面还提到,通过 ! 能调用外部程序进行过滤操作。 比如 !apsort 将当前段落( 即 ap 对象)中的所有行按字母进行排序 (即 sort )。 由于过滤程序毫不受限,这便意味着用户能随心所欲地对文本进行任何替换。

复制、移动与粘贴

对于编辑器都支持的复制、移动、粘贴等功能,Vim也是花样百出。 一般编辑器的剪贴板只保存用户显式剪切或拷贝的文字,而Vim把任何被改动、删除或 拷贝(yank)的文字都存于相当于剪贴板的寄存器中。 利用此特点,组合命令 xp 轻易实现了两个相邻字符的交换, 不仅比输入模式下少一次按键,而且手指不会因方向键而离开主键区。

更美好的是,用户可指定具体的寄存器。比如 "a5dd 将删除的5行存于名为 a 的寄存器中,以后可用 "ap 粘贴到其他地方。 指定寄存器虽增加了按键次数,但也保证了重要的文本不被轻易覆盖。 另一有趣的用法是,以大写字母命名的寄存器会将新内容追加(而不是覆盖)到 对应小写字母的寄存器中。比如继刚才的操作之后,用户在某处敲入 "A2yy , 则此时寄存器中将有7行文字。

Vim的寄存器不仅不止一个,而且不止一种。 例如,最近的修改、删除或拷贝的文本存在 无名寄存器(说是无名,实则有名: " )中, 最近拷贝的存在 数字寄存器 0 中, 最近修改或删除的存在寄存器 1 至 9 中。 对于少于一行的删除(如 dw )还专门有个名为 - 的 微删除寄存器 。 如果不希望改变寄存器内容,可使用名为 _ 的 黑洞寄存器 。 为便于与Vim的外部环境交流, 系统寄存器 * 与 + 对应系统剪贴板,寄存器 ~ 保存最近一次从外部程序(如Word)拖放(Drag-and-Drop)过来的文本。 此外,还有 只读寄存器 % 、 # 、 . 和 : , 以及记录最近搜索模式名为 / 的寄存器。 依然是惯例, :registers 列出重要寄存器的名字及内容。

最后,如果用户不愿意移动光标或屏幕,也可在命令行模式下完成复制、移动与粘贴的任务。

重复

Vim让人觉得重复不再是一件令人乏味的事,有时甚至是一种享受。

命令 . 重复上次的变化,但不包括命令行命令。 若要重复上次的命令行命令,可用 @: 。 如同在unix shell中一样,在命令行模式下用 !! 重复上次的shell命令。 若要在一定范围内重复多次命令,可用 范围:g/模式/Ex命令 或 范围:g/模式/normal 普通命令 , 即在指定范围内对匹配某一模式的行重复执行给定的命令。 将其中的 g 换成 g! 或 v 则将命令作用于非匹配行。

复杂的重复可使用Vim的宏(macro)。比如用 qq 将记录接下来的按键,用 q 结束。 以后可用 @q 重放,再后来可用 @@ 重放。 与简单的拷贝不同,宏不仅能复制插入模式下的按键,还能记录普通模式下的按键。 举个稍微复杂的例子,假设光标所在行的文字是 (1) , 在普通模式下依次键入 qqYp<Ctrl-A>q8@q ,便能产生从一到十的编号列表 (提示: Y 表示行复制, p 表示粘贴, Ctrl-A 是前述的数字增加命令)。

即使更复杂的重复在Vim下也是可能的,命令 :source 或 :runtime 可调用 Vimscript脚本,不过那主要是程序员们大展身手的地方了。

撤销与恢复

在撤销与恢复的操作方面,Vim再度显示其独到之处。

首先,在基本的撤销命令 u 与 恢复命令 Ctrl-R 之外,还有 行撤销命令 U ,即撤销对某行最近进行的所有修改。 其次,以上命令与其他普通命令一样,也支持数字前缀,即可一次性执行多次 撤销或恢复。 更有特色的是,Vim的撤销与恢复状态不是堆栈(stack)结构,而是树(tree)结构。 通俗地举例来说,在一般编辑器中,如果在作出某种修改后发布撤销命令, 然后再作另一种修改,再撤销。此时用户若执行恢复命令,将恢复第二次修改的内容, 而第一次修改的痕迹完全被抹去。一旦用户意识到操作错误,很可能会后悔不迭。 为避免此类事件发生,Vim提供了撤销列表,保留了各个时刻的文本状态,用户随时 可通过命令 g- 和 g+ 来前后遍历,也可用命令 :earlier 或 :later 根据修改时间的范围来恢复状态,还可用命令 :undo 跳到状态树的某个分支 (此后再用 u 与 Ctrl-R 前后遍历)。在此推荐插件Gundo,它不仅清晰地展示了撤销与恢复的树形结构,实现快速恢复,还能预览各个修改版本之间的差别。

一般编辑器在文件关闭后无法撤销或恢复上次的修改,Vim则不然。 用户只要略加设置,便能实现文件修改状态的持久化。

文件操作

Vim提高文件操作效率的关键在一个字——  ,即多分区、多缓冲区(buffer)、 多窗口、多标签页、多文件浏览、多文件读写与多类型文件处理。

多分区指通过文本折叠将一个文件分为多个折叠区,让文件的结构层次更加清晰, 并且减少了因文件过长而带来的频繁的手指移动和目光移动。 Vim中以 z 开头的折叠命令超过20个,足见其丰。

多缓冲区(buffer)指在一个窗口内可同时编辑多个文件。 缓冲区之间的切换方式很多,如用 :bn 或 :bp 进行缓冲区翻页, 用 Ctrl-^ 在两个缓冲区之间来回切换,用 :e #N 按缓冲区编号编辑, 用 :b 文件名 按缓冲区的文件名选择编辑对象 (文件名支持自动完成,且不必输全,如 :b R 可转至文件README.txt)。 如果同时载入缓冲区的文件过多,还有大量优秀的管理缓冲区的插件可资利用,比如 command-tFuzzyFinderbufexplorer等。

要想充分利用屏幕资源,减少缓冲区切换的代价,或希望在多个文件之间进行对比编辑, 可利用Vim的多窗口功能。Vim可对窗口进行横向和纵向多次切分, 还能调整窗口的大小、位置、焦点等。

当多缓冲区、多窗口仍不满足需求时,可开启多标签窗口(tabbed window), 以提高对多文件的处理能力。

大多用户习惯用文件浏览器来选择或管理文件,幸好在Vim中可不假他求,其内置的 netrw插件便具备此功能。不过,非内置的 NERD tree 插件 似乎更受欢迎。

读取和写入文件是编辑器最基本的功能。Vim的特点是能在不离开当前缓冲区的条件下, 读取其他文件的内容,或把缓冲区中的部分文本写入其他文件中。 如 :35r infile 将名为 infile 的文件内容插入到当前文件的第35行下, 1,7w outfile 把当前文件的头7行保存到名为 outfile 的文件中。

最后,为减少用户离开的机会,Vim能透明读写多种类型的远程文件、压缩文件和加密文件。 如果利用插件或其他第三方工具,还可对更多类型的文件进行读写操作。

自动操作

贴心的编辑器应当尽可能地减少用户不必要的操作。 Vim可根据用户设置,完成自动换行,自动缩进,自动将Tab换成空格、自动折叠、 自动读取、自动保存等任务。 不过真正“懒惰”的Vim用户是不会满足于此的,他们会“辛勤”地端起终极武器 autocmd 。

简单地说, autocmd 是一种事件驱动式(event-driven)的命令, 能在某些用户感兴趣的事件发生之时自动执行预先指定的命令。例如, autocmd BufLeave,FocusLost * wa 将在用户离开当前缓冲区时自动保存当前文件, autocmd BufNewFile *.html 0r template.html 将在用户编辑一个全新的HTML文件 时自动加载模版文件 template.html , autocmd BufReadPost *.doc %!antiword % 可在打开Word文档后利用 Antiword 将其转化为文本格式,等等。 考虑到适用于autocmd的事件多达近80种,涵盖了与编辑相关的各个阶段和行为,用户 足以借此打造一个高度自动化的编辑平台。

其他操作

Vim的操作方式还有许多,比如通过一系列的 z 命令操作拼写列表; 通过 :help 或 K 显示在线帮助;通过 :sh 开启一个shell; 通过 :redir 将命令行或shell的执行结果导入文件、寄存器或变量; 用命令 :hardcopy 和 :TOhtml 将所选文字分别以PostScript和HTML的形式输出; 用命令 ga g8 或 :list 显示非打印字符; 用命令 g Ctrl-G 显示行、列、字符、词等统计数据,等等。 此外,用户还能计算表达式、显示各种变量、设置和状态,并能实时改变各种设置或状态, 包括文件类型、界面格式、键盘映射、高亮设置、配色方案(colorscheme),等等。

最后,Vim虽不推荐用鼠标,但仍在Vi之上增加了鼠标操作,并且提供了菜单和工具条。 一方面,这降低了学习难度,对初学者显得更加友好。 另一方面,毋庸讳言,鼠标也有优于键盘的时候。 例如,在双手离开主键区时,用鼠标定位视线所及的区域往往更快捷; 在浏览长文件时,用鼠标滑轮前后滚屏通常更轻松。

5. 增加定制方式

窥一斑而知全豹。上述简介虽远未穷尽Vim的功能,但其丰富与强大已是彰显无遗。 难能可贵的是,Vim的灵活性与可扩展性并不因此而稍减,这让用户拥有足够的控制权。

启动定制

启动Vim需要一系列步骤,每步皆可由用户定制。以从命令行启动为例, 可供选择程序的就有:vi、vim、gvim、view、gview、rvim、rgvim、evim、eview、 vimdiff、gvimdiff等。其中,以g打头的是图形(graphic)模式;以e打头的是简易 (easy)模式,即与其他编辑器一样,只有输入模式(除非按 Ctrl-L 进入普通模式); 以r打头的是限制(restricted)模式,该模式下无法启动shell命令; 以view结尾的是只读(read-only)模式;以diff结尾的是比较模式。 每个程序又可接不同的参数,进一步控制Vim的行为。

Vim最重要的配置是vimrc文件,实为一些ex命令的集合,在Vim初始化时自动执行。 图形模式下的Vim还会追加执行gvimrc文件中的命令。除了缺省的 .vimrc(Windows下是_vimrc)和.gvimrc(Windows下是_gvimrc)外, 用户也可通过命令行参数来指定其他文件,或用特殊文件名 NONE 跳过初始化。

此外,除非在命令行参数中指明禁用插件,Vim启动时还会在运行时路径(runtimepath) 下寻找并执行插件,包括plugin目录下的 全局插件 与ftplugin目录下的 文件类型插件 。 所谓插件,无非是一些可重用的能完成某种特定功能的Vim脚本。 用户可以自己编写,也可利用他人的成果。 Vim的 官方网站 有近4千种插件供人下载, 极大地扩展了Vim的现有功能。

正常情况下,Vim启动时还会执行viminfo文件中的命令。该文件保留了以前命令行历史、 搜索字符串历史、 搜索与替换模式、寄存器内容、标记、缓冲区列表、全局变量等等, 使得一些重要编辑历史不因退出而消失。

viminfo储存的是一些全局信息,如果希望保留某一窗口的设置,可以利用view文件; 如果希望保留所有窗口的设置,可以利用session文件;如果希望保留撤销与恢复信息, 可以利用undo文件。这些文件均为自动生成,但用户也可手工修改。 它们在Vim下次启动之时将被加载,以恢复原先的窗口、 标签页、折叠区域、光标位置、 标记位置、跳转位置以及其他设置。 毫无疑问,这种持久化(persistence)有效地减少了因关闭Vim而带来的重复操作。

对Vim最重要的定制均应在启动之前完成,以下略加展开。

命令定制

Vim尽管提供了足够多的命令,依然给予用户充分的定制空间。

一种定制方式是:用户为原有的命令赋予新的行为。 比如,对于缩进命令 = 和格式化命令 gq ,用户可分别通过定义 equalprg 或 formatprg 调用指定的外部程序, 也可分别通过定义 indentexpr 或 formatexpr 调用自定义的表达式。

另一种定制方式是:通过 nmap 、 vmap 、 omap 等命令建立各种模式下的键盘映射。 用户借此可自定义各种实用的组合命令,定义新的文本对象(见前例),也可改变原命令的定义。 只要用户愿意,他甚至可以将原有的命令集合改得体无完肤,重建一套截然不同的命令体系。

最强大的定制方式,当然是利用VimScript或其他脚本语言及外部程序来定义全新的命令, 这也是许多插件所做的工作。

编辑定制

Vim能根据文件的后缀或内容自动识别上百种文件类型,并据此选用相应的预处理机制、 语法高亮机制、文本宽度、缩进风格、折叠方式等。 在此基础上,用户可新增或修改文件类型、语法结构、处理机制以及各种格式选项。 通过这类定制,为用户创造良好的编辑环境,有利于提高编辑效率。

针对不同类型的文件结构,用户可进一步量身定制,以期最大限度地减少按键。 比如对于冗余度较高的XML、HTML类型的文件,可通过安装插件 xmledit 、 ZenCoding 或 sparkup 来减轻编辑负担。

一个常用节省键击的方式是通过 iabbrev 来定制输入模式下的缩写。 要想实现更高级的类似TextMate的片段(snippet)功能,可安装插件 snipMate 。

自动补全(autocomplete)能大大提高输入的效率和准确度。 Vim根据用户对 complete 选项的设置, 从当前缓冲区、其他窗口缓冲区、加载缓冲区、 字典(dictionary)、词典(thesaurus)、包含文件(included files)、 标签文件等中选择,也可通过设置 omnifunc 调用万能补全函数。

另外,要避免文本的拼写错误,用户可设置语种及其相应的拼写文件。 同理,要避免代码的语法错误,用户也可通过定制或安装插件来完成。

其他定制

Vim提供给用户的选择还有很多。比如,用户可打造完全个性化的界面,包括鼠标、 菜单、 工具栏、光标、状态栏、标签页、滚动条、提示框(tooltip)、字体、配色方案,等等; 可定制折叠方式(foldmethod)、折叠级别(foldlevel)、折叠文本(foldtext)等; 可定制搜索是否高亮匹配、增量移动、大小写敏感、智能判断、循环扫描等; 可定制交换(swap)文件以及备份(backup)文件的保存路径、后缀名以及是否禁用; 可定制错误格式(errorformat)以利用QuickFix来调试程序; 可定制文件的编码、格式(dos、unix或mac)、加密方法(zip或blowfish); 可定制个人的帮助文件、笔记乃至知识管理系统;如此等等,恕难一一列举。

结语

本文不是对Vim功能简单的堆砌和罗列——那只会让初学者更加望而却步—— 而是试图总结它的设计理念和实现方法,以此来说明其“编辑器之神”之誉既非过词, 亦非幸致。与之相应地,Emacs有“神之编辑器”的称号。假如果真如此,那说明 人类用户还是选择Vim为好——他们不是神,无法变出第三只手去hold住修饰键 (此为戏言,还请Emacs的拥趸勿恼)。

或许有人认为编辑器效率对工作效率的影响十分有限,毕竟人们大部分时间是花在思考上, 而不是花在编辑上。但不要忘记,用户的大部分思考需要参考不同的文件以及同一文件的不同 片段,有时还需要对文本进行必要的调整或修改,如果编辑器不能给予用户 “移动如飞、改动如电”的能力,必然频繁消耗用户的目光、手指和注意力,最终降低工作效率。 换言之,“快”的意义绝不仅仅在于节省的那些浏览或修改的时间,更重要的是 节省思维与文字之间的转换开销,让思考活动与编辑行为尽可能地交融无碍。

有种说法是“程序高手都用Vim”,还有种说法是“用Vim的都是程序高手”。 说实话,程序高手与是否用Vim并无必然联系。但有一点可以肯定,优秀的程序员总会不断 追求更加高效的工作方式,就像不断追求更加高效的代码一样。 无论是开源的Vim、Emacs、Eclipse或NetBeans,还是闭源的Visual Studio、Xcode、 SlickEdit或TextMate,各有其长,关键在于使用者能否做到“运用之妙,存乎一心”。 话说回来,见到那些声称只用Notepad来写代码的“高手”,心头还是不免为之一紧: 这是怎样的自虐啊。

惯用Vim者会发现它有一个副作用:一旦换到其他编辑器下,就像一个习惯奔跑的人 不得不停下来踱步一样,那种感受直可用“兔心龟步”来形容。 不过仅仅用“快”来形容Vim还是不够的,还得加上一个“柔”字,即它具有高度的灵活性和 可扩展性。毫不夸张地说,正是Vim的高可定制化让其原本已极其巨大的威力变得几无极限。 假如用户艳羡其他编辑器中的某项功能,大可利用Vim的柔性复制过来。

Vim丰富的命令与灵活的定制为熟手津津乐道,也让生手视为畏途。 实际上,正如此前所指出的那样,Vim的多模式特征让其命令更有意义也更易记忆。 下面以普通模式下的命令为例(注意箭头后的首字母):


由上可见,除了代替方向键的 hjkl 四小写字母外,剩下只有三对字母 Q(q) 、 z(Z) 和 x(X) 了。这三个均属最难组词的字母, 其中 x(X) 代表删除或剪切,与以前打字机删除字符相同,形状上也像剪刀; z 主要用于折叠命令,可以理解为 Z ip(拉链),形状上也具折叠态。 其他如 | 代表列、 > 代表缩进、 ( 代表句首, { 代表段首等 皆极具象征意义。至于命令行模式下的就更不用说,大多都是单词或词组的简写。

除了富有涵义的单词和隐喻之外,Vim的命令和选项的设计还处处透着一致性, 进一步减少了记忆的负担。事实上,这也是Vim的 宗旨 之一。 比如,移动命令与文本对象相一致;相同字母的大小写 命令之间通常是对应的,且小写的更常用;相同首字母的命令中叠字者更常用, 且多表示行操作;代表方向的 hjkl 应用于折叠移动( zj 、 zk )、 窗口移动( Ctrl-W-J 、 Ctrl-W-K 、 Ctrl-W-H 、 Ctrl-W-L )、 插入模式下的移动( Ctrl-G-J 、 Ctrl-G-K )等 以及ex命令中 ! 的用法,等等。

至于定制方面,网上有大量的Vim配置可供参考。初学者可先行“拿来主义”, 挑一个合适自己的来用,以后慢慢学习领会,渐进改造直至称心如意。

Vim虽以难著称,其实有一个宗旨:让新手尽快上路,然后在使用中逐步累积知识。 对于老手而言,Vim又是常学常新的。纵是在Vim世界里浸淫十余年者,也随时可 享俯拾遗珠之乐。

人们常常强调Vim陡峭的学习曲线,却忽略事情的另一面,即它的效率曲线也是陡峭的。 当然Vim的独特性决定了它永远成不了最流行的编辑器,但这实在不重要, 重要的是凡窥其门径者,无一不留恋难舍,大有“除却巫山不是云”之感。 好在即使离开了Vim,手指依旧可以在别处弹奏着同样独特的韵律。 在许多编辑器或IDE下,都可使用Vi或Vim的键盘绑定,其中包括:Emacs、 Eclipse、 NetBeans、IntelliJ、Visual Studio、SlickEdit、XCode、TextMate、Komodo等。 在非编辑器的环境中,同样有Vi(Vim)的身影。 在Firefox、Chrome、Safari等浏览器中都有类Vim的插件, 在*nix的命令行终端下经配置也可使用Vi, 在Mac下有一些软件可以为大多数应用程序(Cocoa Application)的文本框绑定Vi输入法。 所有这些的背后,都站着一大批受到Vim魅力感召的人们。

回到本文开篇的问题:Vim的魅力何在?我的答案是:表面上在于快和柔, 本质上则在于对用户的最大尊重——尊重用户的体验与感受,尊重用户的自由与智慧。 Vim以其独有的理念充分地发挥了用户的能动性与想象力,从而营造出一个空寂的世界, 在那里无按钮菜单之分神,无弹出窗口之聒噪,无鼠标之不便, 只有意念在键盘上静静而自然地流淌,让人沉浸其中而不愿自拔。 最后,请允许我用一段绝非刻意而为之的对比来重述自己的观点:


(全文完)