《Vim 实用技巧》一书旨在想人们传授如何像 Vim 高手一样思考问题。对于 Vim 高手来说,Vim 能以思考的速度编辑文本。本系列文章从 Vim 实际使用出发,介绍大量的 Vim 实用技巧,每一篇文章都是某一主题的技巧集合。
Vim 对重复性操作进行了优化。它之所以能高效重复,是因为它会记录我们最近的的操作,让我们用一次按键就能重复上次修改。但是前提是我们能够学会规划按键动作。掌握这一理念是高效使用 Vim 的关键。
普通模式是 Vim 自然放松的状态,这篇文章同时会介绍普通模式下一些通用技巧。
基础技巧
在介绍具体的技巧之前,首先介绍一些关于 Vim 帮助信息的按键命令:
:h vimtutor
:获取vim向导;:h key-notation
:获取vim按键帮助文档;:h feature-list
:浏览vim完整的功能列表(vim的功能集包括tiny,small,normal,big,huge)。:version
:查看vim版本及所有可用功能
有时候,我们不希望 vim 的配置文件对我们的操作产生干扰,此时,可以用 vim -u NONE -N
启动 vim。 -u NONE
标志让 vim 在启动时不加载你的 vimrc 文件,当用不加载 vimrc 文件的方式启动时,vim 会切换到 vi 兼容模式,而 -N 标志能防止进入vi兼容模式。
Vim 解决问题的方式
技巧 1:结识 .
命令
.
命令可以让我们重复上次的修改,它是 Vim 中的瑞士军刀。
上次修改
可以指很多东西,一次修改的单位可以是字符、整行,甚至是整个文件。除了普通模式下的修改命令之外,每次我们进入插入模式时,也会形成一次修改:从进入插入模式的那一刻起,直到返回普通模式时为止(输入ESC),Vim 会记录每一个按键操作。此时使用 .
命令,它将会重新执行所有这些按键操作。
.
命令是一个微型的宏。
技巧 2:不要自我重复
如果想在如下内容的行尾添加分号:
1 | this is line 1 |
- 第一种方法:
$a;<ESC>
,如果接下来有多行要做相同的修改,可以使用 `j$.命令进行重复。 - 第二种方法:
A;<ESC>
,如果接下来有多行要做相同的修改,可以使用j.
命令进行重复。
第二种方法更加简单,它通过一个按键移动(j
)移动到目标行,另一个按键执行修改(.)。一键移动、一键修改
的应用模式也是 Vim 中高效操作的常见技巧。
第二种方法中的 A
命令把两个按键 $a
合并成了一次按键,下面是类似的命令:
C
:c$
s
:cl
S
:^c$
I
:^i
A
:$a
o
:A<CR>
O
:ko
这些命令的共同点是:它们都会从普通模式切换到插入模式。
技巧 3:以退为进
如下所示,要在运算符“+”两边各输入一个空格:输入如下的命令序列:
1 | 1+2+3+4+5 |
f+
:将光标移动到当前行中下一个出现+
符号的地方s + <ESC>
:删除+字符,输入+
三个字符,回到普通模式
这里 s
命令删除当前光标所在的单词,然后输入 +
并回到普通模式。先删除一个字符,再添加回三个字符,这是一个小技巧,这样做的好处是我们可以使用 .
命令重复这一修改:如果要继续修改,输入 ;.
即可重复修改:
;
可以重复查找上一个 f, F, t, T 命令所查找的字符,- `.`` 命令重复上次修改
;
命令带我们到下一个目标字符上,而 .
命令则重复上次修改,这也体现了 一键移动,另一键操作
。所以与其和 Vim 区分模式的编辑模型作斗争,倒不如与它一起协同工作。
技巧 4:执行、重复、回退
在面对重复性工作时,我们需要让移动命令和修改都能够重复,这样就可以达到一个最佳编辑模式。
除了 .
命令可以重复上一次修改之外,有些命令也支持以其他方式重复。而且当 Vim 让一个操作或移动可以方便地重复时,它总是会提供某种方式,让我们在不小心做过头时能回退回来。
下表列出了vim中可重复执行的命令,以及相应的回退方式:
目的 | 操作 | 重复 | 回退 |
---|---|---|---|
做出一个修改 | edit comand | . | u |
在行内查找下一指定字符 | f{char}/t{char} | ; | , |
在行内查找上一指定字符 | F{char}/T{char} | ; | , |
在文档内查找下一匹配 | /pattern |
n | N |
在文档内查找上一匹配 | ?pattern |
n | N |
执行替换 | :s/target/replacement | & | u |
执行一系列修改 | qx{changes}q | @x | u |
技巧 5:查找并手动替换
在替换时我们一定要小心每一步操作。所以我们可能会输入查找字符串,对每个匹配手动确认是否需要进行替换。也有一种偷懒的办法,无需输入就可以进行查找:
*
:该命令可以查到当前光标下的单词
所以可以先通过移动命令将光标移动到某个单词后,在输入 *
进行查找。
技巧 6:结识 . 范式
以上编辑技巧,都体现了一个共有的模式,即 . 范式
。在这个范式中,通过 .
命令重复上次修改,再通过一次按键将光标移动到下一个目标上。
. 范式
(一键移动、另一键执行)是 Vim 中的理想编辑模式。
普通模式
技巧 7:停顿时请移开画笔
画家在休息时不会把画笔放在画布上,对 Vim 而言也是这样:程序员只花费很小的时间编写代码,绝大多数时间用来思考、阅读、浏览代码。普通模式就是 Vim 的自然放松状态,在普通模式中,我们也有众多的工具可以利用。
技巧 8:把撤销单元切成块
u 键会触发撤销命令,它会撤销最新的修改。一次修改是可以改变文档内文本的任意操作,其中包括在普通模式、可视模式以及命令行模式所触发的命令,而且一次修改也包括了插入模式中输入(或删除)的文本。
在 Vim 中,我们可以自己控制撤销命令的粒度:从进入插入模式开始,直到返回普通模式为止,在此期间输入或删除的任何内容都被当成一次修改。因此只要控制ESC键的使用,就可以控制撤销的粒度。
多久离开一次插入模式?这是个人喜好问题。最好让每个 可撤销块
对应一次思考过程。
当处于插入模式时,如果光标处于行尾,另起一行最快的方法是使用 enter 键,但是有时也可以尝试 <ESC>o
键,因为在撤销时它会让你有更细的粒度。
有一个小细节需要注意:如果在插入模式中使用 <Up>
,<Down>
,<Left>
或 <Right>
这些光标键,将会产生一个新的撤销块。你可以把这想象为先切换回普通模式,然后使用相应的 h,j,k,l 命令对光标进行移动。
技巧 9:构造可重复的修改
Vim 对重复操作进行了优化,要利用这一点,必须考虑如何构造修改。
在vim中,要完成一件事,总是有不止一种方式。在评估哪种方式最好时,最显而易见的指标是效率。即哪种手段需要的按键次数最少(又名 VimGolf)。
如下所示,假设光标位于行尾的字符 h
处,如果想删除单词 nigh
,有三种方法:
1 | The end is nigh |
- 反向删除:dbx
- 正向删除:bdw
- 删除整个单词:daw
第三种方式这里使用更为精准的 aw
文本对象,aw
代表 a word
,即一个单词。可以把 daw
命令解读为 delete a word
。
虽然三种方式都需要三次按键,即每种方式的高尔夫得分都是3分。但是 daw
可以发挥 .
命令的最大威力。
- 如果使用反向删除,
.
命令会重复删除一个字符(. == x) - 如果使用正向删除,
.
命令会重复删除从光标位置到下个单词开头的内容(. == dw),但是由于已经在行尾了,并没有下一个单词,所以这个场景里,.
命令没有什么用处。 - 如果使用
daw
命令,.
命令会重复删除一个单词(. == daw),而且由于第一次指定daw后,不仅删除了单词 nigh,还删除了一个空格,因此光标直接在单词is
的最后一个字符。此时可以直接使用.
命令重复。
技巧 10:用次数做简单的算术运算
很多普通模式命令都可以带一个次数前缀(:h count
),这样 Vim 就会尝试把该命令执行指定的次数,而不是只执行一次。我们可以利用这个功能来做简单的算术运算。
Ctrl+a
和 Ctrl+x
命令分别对数字执行加减操作。在不带次数执行时,它们会逐个加减。但是如果带一个次数前缀,那么就可以用它们加减任意整数。如果当前光标不在数字上,这两个命令会在当前光标之前或之后的数值上加减整数。即这两个命令会在当前行中查找数字,然后执行相应的加减运算。
Vim 能够识别数字的格式(例如 Vim 把 0 开头的数字解释为八进制值,而不是十进制值)。可以通过 set nrformats=
来关闭这项功能,这样vim把所有数字都当成十进制。
技巧 11:能够重复,就别用次数
如下所示:
1 | Delete more than one word |
要删除 more than
这两个单词(假设光标在 m 上),可以使用如下命令:
- d2w:先调用删除命令,然后以 2w 作为动作命令,即删除 2 个单词
- 2dw:首先次数作用于删除命令,而动作命令只跨越一个单词,即做 2 次删除单词的操作
再来考虑 dw.
:该命令先删除一个单词,然后使用 .
命令重复,因此此时 u 命令和 . 命令提供了更细的粒度。而且计算次数是很讨厌的,尽管使用次数减少了按键数,但浪费了同样的时间去统计次数。口诀是 执行、重复、回退
。
只在必要时使用次数,例如如下所示,需要将 a couple of
修改为 some more
(假设光标在 a
上):
1 | I have a couple of questions. |
此时直接使用 c3w
比较合适。而且使用次数还有另外一个好处:它保留了一个干净的、连贯的撤销历史记录。
对于是用次数风格还是用重复风格,这本身就存在着争论。这需要总结你自己的观点。
技巧 12:双剑合璧,天下无敌
Vim 的强大很大程度上源自操作符与动作命令(有的书称为移动命令)相结合,操作符 + 动作命令 = 操作。
vim 常见的操作符命令如下(:h operator
):
- c:修改
- d:删除
- y:复制到寄存器
- g~:反转大小写
- gu:转换为小写
- gU:转换为大写
:增加缩进
- <:减少缩进
- =:自动缩进
操作符与动作命令的结合形成一种语法。这种语法的第一个规则很简单:即一个操作由一个操作符,后面跟一个动作命令组成。Vim 的语法还有一条额外规则:当一个操作符命令被连续调用两次时,它会作用于当前行,所以:
- cc:修改当前行
- dd:删除当前行
:缩进当前行
gugu
:将当前行转换为小写,这种情况也可以简化为guu
- ……
使用 Vim 内置的操作符和动作命令,我们可以执行的操作数目是非常巨大的。除此之外,我们还可以通过自定义动作命令及操作符来进一步扩充操作的数目:
- 可以使用
:h :map-operator
来查看自定义操作符的相关文档 - 可以使用
:h :omap-info
来查看自定义动作命令的相关文档。例如textobj-entire
插件新增了两种新的文本对象ie
和ae
,作用于整个文件
接下来再介绍 操作符待决模式
。如果把 Vim 想象成有限状态机,那么 操作符待决模式
就是一个只接受动作命令的状态。该模式在我们调用操作符时被激活,然后什么也不做,直到我们提供了一个动作命令,完成整个操作。当 操作符待决模式
被激活时,我们可以像平常一样按下 <ESC>
键终止该操作,回到普通模式。
很多命令由两个或更多的按键来调用(:h g
、:h z
、:h ctrl-w
可以看到一些示例),但在多数情况下,头一个按键只是第二个按键的前缀,这些命令不会激活 操作符待决模式
,相反可以把它们当成命名空间,用来扩充可用命令数目。只有操作符才会激活操作符待决模式。
操作符待决模式也是我们创建自定义操作符及动作命令的基础。