转载自:http://www.20ju.com/content/V190812.htm

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) ,
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键。
H            ——移至屏首L            ——移至屏末M            ——移至屏中5H           ——移至屏首起第5行5L           ——移至屏末起倒数第5行gg           ——移至页首G            ——移至页末50%          ——移至页中20G          ——移至页首起第20行20j          ——下移20行20k          ——上移20行5gj          ——下移5个显示行(与文件行不同,显示行考虑折行)5gk          ——上移5个显示行(同上注)

列间移动:

作为一种近距离微调,列间移动非常实用。除了跳至行首或行末以外,最有用的便是
f/F/t/T/;/, 系列命令了。可惜中文是多字节的,无法享用此功能。
0            ——移至行首$            ——移至行末^            ——移至软行首(即忽略首空白)g_           ——移至软行末(即忽略尾空白)g0           ——移至屏幕行首(考虑折行)gm           ——移至屏幕行中(同上注)g$           ——移至屏幕行末(同上注)20|          ——移至第20列fx           ——移至本行下一个字符“x”处Fx           ——移至本行上一个字符“x”处tx           ——移至本行下一个字符“x”前Tx           ——移至本行上一个字符“x”后;            ——重复上次 f/F/t/T 命令,            ——反向重复上次 f/F/t/T 命令

结构移动:

在用户眼中,文本不是单纯的字符集合,不仅具有行列差别,更有结构差别。 常见的结构划分有:单词(word),句子(sentence),段落(paragraph) 和区块(section)等。 除了系统的默认设定外,
Vim还允许用户自定义结构的界定方式。 有些遗憾的是,由于中文词与词之间没有分界,无法利用单词的跳转功能。
w            ——右移至最近词首W            ——同上,但忽略非空白单词分隔符b            ——左移至最近词首B            ——同上,但忽略非空白单词分隔符e            ——右移至最近词尾E            ——同上,但忽略非空白单词分隔符ge           ——左移至最近词尾gE           ——同上,但忽略非空白单词分隔符(            ——前移至最近句首)            ——后移至最近句首{            ——前移至最近段首}            ——后移至最近段首
除基本的文本结构外,程序员还对语法结构感兴趣。
% 是一个方便的命令,它能让光标在匹配项之间来回游走。 除了
(
)
[
]
{
} 的括号匹配外, 还支持C风格的注释(
/*
*/ )与宏(
#if
#ifdef
#else
#elif
#endif 等)匹配。
Vim更提供了matchit的插件(但并未默认安装),支持其他编程语言的匹配结构。 若仍嫌不满,用户也可自定义其他的匹配方式。 下面的命令是给程序员额外的福利——
[[           ——前移至最近区首(常用于跳至C类型的函数头)]]           ——后移至最近区首(同上注)[{           ——前移至最近代码区的首部(C类型语法)]}           ——后移至最近代码区的尾部(同上注)[m           ——前移至最近方法的首部(Java类型语法)]m           ——后移至最近方法的首部(同上注)[M           ——前移至最近方法的尾部(同上注)]M           ——后移至最近方法的尾部(同上注)gd           ——跳至当前变量的局部声明处gD           ——跳至当前变量的全局声明处
Vim具有折叠功能,故而支持折叠结构的跳转。
[z           ——移至当前展开的折叠区的首部]z           ——移至当前展开的折叠区的尾部zj           ——移至下一折叠区的首部zk           ——移至上一折叠区的尾部

屏幕移动:

在浏览文件时,用户往往更希望调整的是屏幕而不是光标。
Ctrl+F       ——向下滚动一屏Ctrl+B       ——向上滚动一屏Ctrl+D       ——向下滚动一个单位(默认半屏)Ctrl+U       ——向上滚动一个单位(默认半屏)Ctrl+E       ——向下滚动一行Ctrl+Y       ——向上滚动一行zl           ——向右滚动一列(仅在禁用折行时有效)zh           ——向左滚动一列(同上注)zt           ——置当前行于屏首zt           ——置当前行于屏末zz           ——置当前行于屏中10z.         ——置第10行于屏中50z-         ——置第50行于屏末
鉴于滚屏操作十分常用而Ctrl键不便,建议用
nmap 将^f/^b/^d/^u/^e/^y 分别替换为空格键、Shift+空格键、Enter键、Backspace键、下方向键、上方向键。

搜索移动:

几乎所有的编辑器都有搜索功能,但很少有
Vim这般强大和方便。 强大体现在它不仅支持最基本的正则表达式,还支持懒惰模式 ( ), 甚至可以
跨行 匹配。方便则体现在搜索过程对用户友好,没有讨厌的弹出窗口, 支持高亮匹配,支持增量搜索(光标在关键词输入过程中即开始运动,不必等待回车键), 支持智能大小写判断,一键重复正向或反向搜索,等等。另外,特别推荐四个诱人的命令:
*            ——正向搜索光标所在的单词(精确匹配)#            ——反向搜索光标所在的单词(精确匹配)g*           ——正向搜索光标所在的单词(模糊匹配)g#           ——反向搜索光标所在的单词(模糊匹配)
如果要在多个文件中搜索,可以用内部命令
vimgrep 或外部命令
grep 。 前者在正则表达式上更强大,也更通用(Windows下没有自带的grep),但速度不及后者。 我个人的选择是在配置中加上一行:
set grepprg=ack\ -a ,以便让更好用的 来取代grep。

定点移动

定点移动是
Vim又一特色,迅速而精确的远程移动让浏览和编辑变得前所未有的轻松。
Vim的标记(mark)相当于一种书签,用来记录用户关注的热点位置。 假设用户对当前光标所处位置感兴趣,可输入命令
mx (x可以是任何字母)。 以后只要输入
`x 即可
返回原标记处,或用
‘x
返回原标记处所在的行首。 用作标记的字母有大小写之别。小写为局部标记,用于缓冲区内部跳转, 仅对当前编辑的文件有效,且在缓冲区关闭后失效。 大写为全局标记,可在不同的文件之间跳转,不因缓冲区关闭而失效(需设定
viminfo )。
比用户标记更有用的是系统标记。
Vim贴心地在一些热点上留下暗记,以便用户回访。 例如,用户每次退出
Vim时的光标位置都会被保留,最近十个分别用数字0到9来命名。 下面是其他一些实用的标记跳转:
``           ——跳转至当前缓冲区最近跳转点(可实现两点之间的来回跳转)''           ——同上,但仅精确到行`.           ——跳转至当前缓冲区最近修改处`[           ——跳转至上次改动或拷贝处的首部`]           ——跳转至上次改动或拷贝处的尾部`<           ——跳转至最近可视化选择区的首部`>           ——跳转至最近可视化选择区的尾部`"           ——跳转至上次退出当前缓冲区时光标的最后位置(在下)`^           ——跳转至上次退出当前缓冲区时光标的最后位置(在插入模式下)
所有标记均可通过命令
:marks 来显示,以供查询。要获得更好的视觉效果, 不妨试试 插件,它利用
Vim的sign功能将隐性标记显性化了。
除了标记列表外,
Vim还维护了一张变化列表。 该表记录了用户每次修改文本的位置,运行命令
:changes 即可察看。 相应的跳转命令是:
g;           ——跳转至变化列表中的较旧处(支持数字前缀)g,           ——跳转至变化列表中的较新处(支持数字前缀)
程序员在浏览代码时经常需要在不同的源文件中跳转(比如察看某个函数的定义),
Vim为此提供了标签(tag)支持。 与标记不同,标签需要依赖外部工具如 、 等来产生。 使用命令
:tag <tagname> 可实现标签跳转,不过更简便的还是将光标置于 关键词之上,然后通过
Ctrl-] 转至其定义处,必要时可用
Ctrl-T
返回。 用鼠标亦可完成以上任务,但并不推荐。
最后,上述各种移动命令所产生的跳跃点均保存于跳转列表中(最多不超过100个), 可通过命令
:jumps 检视。这意味着用户可以在原来的轨迹上来回跃迁——
Ctrl-O       ——跳转至跳转列表中的较旧处(支持数字前缀)Tab或Ctrl-I  ——跳转至跳转列表中的较新处(支持数字前缀)

其他移动

还有一些其他类型的移动,试列举一二:
gf           ——跳转至光标之上或之后的路径所对应的文件gF           ——同上,但跳至文件后所指定的行数]s           ——跳转至下个错误拼写处[s           ——跳转至上个错误拼写处

3. 增加操作对象

Vim丰 富的移动方式让用户以最小的代价——包括手指、目光、时间和精力——把所感兴趣的 文本带入视线范围并将光标精确定位。 这只是编辑的第一步,下一步是在当前位置进行文本操作。 一般编辑器修改文本多通过Backspace键、Delete键、方向键并结合鼠标完成,效率低下。 究其原因,主要在于操作对象太过单一:要么是以字符为单位,粒度太小; 要么以高亮区域为单位,选定太慢。
Vim则不同,提供了各种粒度的操作对象,供用户在不同需求下选择,大大提高了编辑效率。
Vim的 编辑命令具有统一的形式:数字 + 操作符(operator) + 文本对象, 表示对某一文本对象进行指定次数的操作。其中,数字部分为可选项,默认为1; 有些操作符后不接对象,正如不及物动词后不接宾语。 一切似乎都平淡无奇,直到文本对象与移动命令自然而奇妙地结合在一起,瞬时光芒四射。 具体地说,文本对象可由移动命令所扫过的字符片段来定义。 于是,有多少移动方式,便对应多少文本对象。 这已经是我们第二次看到移动命令的重用(reuse)了(前一次用于可视化模式下)。
举例来说,操作符
d 表示删除,移动命令
w 表示前进到下一单词,则
dw 将删除从当前光标至下一单词之前的所有字符。 假设当前光标处于某单词的首部,
2dw 将删除该单词及其后一单词。 如果知道移动命令本身也可加数字前缀,则
2d3w 将删除6个单词,与
3d2w
6dw
d6w 的效果相同。 类似地,操作还可以句子、段落、语法结构为单位, 以行或列为单位,以屏幕为单位,以匹配模式为单位,或以定点为界限,等等。
进一步地,
Vim在Vi的基础上新增了其他的文本对象,用于可视化模式下的选择 和
普通模式下的编辑。兹列几项如下——
aw           ——一个单词(支持数字前缀)is           ——一个句子内部(支持数字前缀)ap           ——一个段落(支持数字前缀)a"           ——一个双引号区域i}或i{或iB   ——一个“{}”块内部(支持数字前缀)a>或a<       ——一个“<>”块(支持数字前缀)at           ——一个标签块(支持数字前缀)
不妨看一个典型用例。假设有一段文字:
String name = "Shen Xin";
用户希望将
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来完成, 如此更符合减少手指移动的原则。
插入模式下的
Ctrl-R 能输入与寄存器(稍后将会介绍)相关的内容, 比如
Ctrl-R " 将输入最近一次的内部拷贝(即无名寄存器中的内容),
Ctrl-R + 将输入最近一次的外部拷贝(即系统剪贴板),
Ctrl-R . 将输入最近一次的插入文本,等等。 更特别地,
Ctrl-R = 后将输入表达式。例如
Ctrl-R =12*50将打出
600 ,相当于一个小计算器。

删除文本

与插入操作相对的是删除操作。最普通也是最低效的方式是在插入模式下使用删除键。 使用
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 前后遍历)。在此推荐插件 ,它不仅清晰地展示了撤销与恢复的树形结构,实现快速恢复,还能预览各个修改版本之间的差别。
一般编辑器在文件关闭后无法撤销或恢复上次的修改,
Vim则不然。 用户只要略加设置,便能实现文件修改状态的持久化。

文件操作

Vim提高文件操作效率的关键在一个字——
,即多分区、多缓冲区(buffer)、 多窗口、多标签页、多文件浏览、多文件读写与多类型文件处理。
多分区指通过文本折叠将一个文件分为多个折叠区,让文件的结构层次更加清晰, 并且减少了因文件过长而带来的频繁的手指移动和目光移动。
Vim中以
z 开头的折叠命令超过20个,足见其丰。
多缓冲区(buffer)指在一个窗口内可同时编辑多个文件。 缓冲区之间的切换方式很多,如用
:bn
:bp 进行缓冲区翻页, 用
Ctrl-^ 在两个缓冲区之间来回切换,用
:e #N 按缓冲区编号编辑, 用
:b 文件名 按缓冲区的文件名选择编辑对象 (文件名支持自动完成,且不必输全,如
:b R 可转至文件README.txt)。 如果同时载入缓冲区的文件过多,还有大量优秀的管理缓冲区的插件可资利用, 比如 、 、 等。
要想充分利用屏幕资源,减少缓冲区切换的代价,或希望在多个文件之间进行对比编辑, 可利用
Vim的多窗口功能。
Vim可对窗口进行横向和纵向多次切分, 还能调整窗口的大小、位置、焦点等。
当多缓冲区、多窗口仍不满足需求时,可开启多标签窗口(tabbed window), 以提高对多文件的处理能力。
大多用户习惯用文件浏览器来选择或管理文件,幸好在
Vim中可不假他求,其内置的 netrw插件便具备此功能。不过,非内置的 插件 似乎更受欢迎。
读取和写入文件是编辑器最基本的功能。
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文档后利用 将其转化为文本格式,等等。 考虑到适用于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、g
vim、view、gview、r
vim、rg
vim、e
vim、eview、
vimdiff、g
vimdiff等。其中,以g打头的是图形(graphic)模式;以e打头的是简易 (easy)模式,即与其他编辑器一样,只有输入模式(除非按
Ctrl-L 进入
普通模式); 以r打头的是限制(restricted)模式,该模式下无法启动shell命令; 以view结尾的是只读(read-only)模式;以diff结尾的是比较模式。 每个程序又可接不同的参数,进一步控制
Vim的行为。
Vim最重要的配置是
vimrc文件,实为一些ex命令的集合,在
Vim初始化时自动执行。 图形模式下的
Vim还会追加执行g
vimrc文件中的命令。除了缺省的 .
vimrc(Windows下是_
vimrc)和.g
vimrc(Windows下是_g
vimrc)外, 用户也可通过命令行参数来指定其他文件,或用特殊文件名
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类型的文件,可通过安装插件 、 或 来减轻编辑负担。
一个常用节省键击的方式是通过
iabbrev 来定制输入模式下的缩写。 要想实现更高级的类似TextMate的片段(snippet)功能,可安装插件 。
自动补全(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的多模式特征让其命令更有意义也更易记忆。 下面以
普通模式下的命令为例(注意箭头后的首字母):
a(A) => Append                       b(B) => Backward wordc(C) => Change                       d(D) => Deletee(E) => End of word                  f(F) => Findg(G) => Go                           H    => Home of windowi(I) => Insert                       J    => JoinK    => Keyword under the cursor     L    => Last line of windowm    => Mark                         M    => Middle line of windown(N) => Next find                    o(O) => Open a new linep    => Paste                        r(R) => Replaces(S) => Substitue                    t(T) => Tillu(U) => Undo                         v(V) => Visual modew(W) => Word move                    y(Y) => Yank
由上可见,除了代替方向键的
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以其独有的理念充分地发挥了用户的能动性与想象力,从而营造出一个空寂的世界, 在那里无按钮菜单之分神,无弹出窗口之聒噪,无鼠标之不便, 只有意念在键盘上静静而自然地流淌,让人沉浸其中而不愿自拔。 最后,请允许我用一段绝非刻意而为之的对比来重述自己的观点:
最大的障碍不在于其本身,而在于用户的自我束缚。最大的魅力不在于其本身,而在于用户的自我解放。
(全文完)
附:本人的 。

参考文献

  1. Bram Moolenaar. VIM USER MANUAL
  2. Arnold Robbins, et al. Learning the vi and Vim Editors(7ed)
  3. Kim Schulz. Hacking Vim 7.2