点命令
在编辑文本时,我们应该尽可能地避免重复的动作。在这一章节中,你将会学习如何使用点命令来重放上一个修改操作。点命令是最简单的命令,然而又是减少重复操作最为有用的命令。
用法
正如这个命令的名字一样,你可以通过按下.
键来使用点命令。
比如,如果你想将下面文本中的所有”let“替换为"const":
let one = "1";
let two = "2";
let three = "3";
- 首先,使用
/let
来进行匹配。 - 接着,使用
cwconst<esc>
来将"let"替换成"const"。 - 第三步,使用
n
来找到下一个匹配的位置。 - 最后,使用点命令(
.
)来重复之前的操作。 - 持续地使用
n . n .
直到每一个匹配的词都被替换。
在这个例子里面,点命令重复的是cwconst<esc>
这一串命令,它能够帮你将需要8次输入的命令简化到只需要敲击一次键盘。
什么才算是修改操作?
如果你查看点命令的定义的话(:h .
),文档中说点命令会重复上一个修改操作,那么什么才算是一个修改操作呢?
当你使用普通模式下的命令来更新(添加,修改或者删除)当前缓冲区中的内容时,你就是在执行一个修改操作了。其中的例外是使用命令行命令进行的修改(以:
开头的命令),这些命令不算作修改操作。
在第一个例子中,你看到的cwconst<esc>
就是一个修改操作。现在假设你有以下这么一个句子:
pancake, potatoes, fruit-juice,
我们来删除从这行开始的位置到第一个逗号出现的位置。你可以使用df,
来完成这个操作,使用.
来重复两次直到你将整个句子删除。
让我们再来试试另一个例子:
pancake, potatoes, fruit-juice,
这一次你只需要删除所有的逗号,不包括逗号前面的词。我们可以使用f,
来找到第一个逗号,再使用x
来删除光标下的字符。然后使用用.
来重复两次,很简单对不对?等等!这样做行不通(只会重复删除光标下的一个字符,而不是删除逗号)!为什么会这样呢?
在Vim里,修改操作是不包括移动(motions)的,因为移动(motions)不会更新缓冲区的内容。当你运行f,x
,你实际上是在执行两个独立的操作:f,
命令只移动光标,而x
更新缓冲区的内容,只有后者算作修改动作。和之前例子中的df,
进行一下对比的话,你会发现df,
中的f,
告诉删除操作d
哪里需要删除,是整个删除命令df,
的一部分。
让我们想想办法完成这个任务。在你运行f,
并执行x
来删除第一个逗号后,使用;
来继续匹配f
的下一个目标(下一个逗号)。之后再使用.
来重复修改操作,删除光标下的字符。重复; . ; .
直到所有的逗号都被删除。完整的命令即为f,x;.;.
。
再来试试下一个例子:
pancake
potatoes
fruit-juice
我们的目标是给每一行的结尾加上逗号。从第一行开始,我们执行命令A,<esc>j
来给结尾加上逗号并移动到下一行。现在我们知道了j
是不算作修改操作的,只有A,
算作修改操作。你可以使用j.j.
来移动并重复修改操作。完整的命令是A,<esc>j
。
从你按下输入命令(A
)开始到你退出输入模式(<esc
>)之间的所有输入都算作是一整个修改操作。
重复多行修改操作
假设你有如下的文本:
let one = "1";
let two = "2";
let three = "3";
const foo = "bar";
let four = "4";
let five = "5";
let six = "6";
let seven = "7";
let eight = "8";
let nine = "9";
你的目标是删除除了含有"foo"那一行以外的所有行。首先,使用d2j
删除前三行。之后跳过"foo"这一行,在其下一行使用点命令两次来删除剩下的六行。完整的命令是d2jj..
。
这里的修改操作是d2j
,2j
不是一个移动(motion)操作,而是整个删除命令的一部分。
我们再来看看下一个例子:
zlet zzone = "1";
zlet zztwo = "2";
zlet zzthree = "3";
let four = "4";
我们的目标是删除所有的'z'。从第一行第一个字符开始,首先,在块可视化模式下使用Ctrl-vjj
来选中前三行的第一个'z'字母。如果你对块可视化模式不熟悉的话也不用担心,我会在下一章节中进行介绍。在选中前三行的第一个'z'后,使用d
来删除它们。接着用w
移动到下一个z字母上,使用..
重复两次之前选中加删除的动作。完整的命令为Ctrl-vjjdw..
。
你删除一列上的三个’z‘的操作(Ctrl-vjjd
)被看做一整个修改操作。可视化模式中的选择操作可以用来选中多行,作为修改动作的一部分。
在修改中包含移动操作
让我们来重新回顾一下本章中的第一个例子。这个例子中我们使用了/letcwconst<esc>
紧接着n . n .
将下面的文本中的'let'都替换成了'const'。
let one = "1";
let two = "2";
let three = "3";
其实还有更快的方法来完成整个操作。当你使用/let
搜索后,执行cgnconst<Esc>
,然后. . .
。
gn
是一个移动并选择的动作,它向前搜索和上一个搜索的模式(本例中为/let
)匹配的位置,并且 自动对匹配的文本进行可视化模式下的选取。想要对下一个匹配的位置进行替换的话,你不再需要先移动在重复修改操作(n . n .
),而是简单地使用. .
就能完成。你不需要再进行移动操作了,因为找到下一个匹配的位置并进行选中成为了修改操作的一部分了。
当你在编辑文本时,应该时刻关注像gn
命令这种能一下子做好几件事的移动操作。
(译者在这里研究了一会,并做了不少实验,总结规律是:单独的motion(第4章中所说的名词)不算修改操作,而opeartor(动词)+motion(名词)时(请回顾第4章),motion被视为一个完整的修改操作中的一部分。再看一个例子,看看
/
命令是如何被包含在一个修改操作中的:
a
b
foo
c
d
foo
e
f
假设你的光标在第一行的a上,执行命令
d/foo<Esc>
,Vim会删除a,b。然后.
,Vim会删除foo, c, d,再按.
,Vim什么也不做,因为后面没有"foo"了。在这个例子中,/foo
是一个motion(名词),是Vim语法(动词+名词:operator + motion)的一部分,前面的d
则是动词。d/foo<Esc>
这条命令的功能是:从当前光标所在位置开始删除,直到遇到"foo"为止。后面的点命令就重复这个功能,第二次按.
之所以Vim什么也不做,是因为找不到下一个匹配了,所以这条命令就失效了。
聪明地学习点命令
点命令的强大之处在于使用仅仅1次键盘敲击代替好几次敲击。对于x
这种只需一次敲击键盘就能完成的修改操作来说,点命令或许不会带来什么收益。但是如果你的上一个修改操作是像cgnconst<esc>
这种复杂命令的话,使用点命令来替代就有非常可观的收益了。
在进行编辑时,思考一下你正将进行的操作是否是可以重复的。举个例子,如果我需要删除接下来的三个单词,是使用d3w
更划算,还是dw
再使用.
两次更划算?之后还会不会再进行删除操作?如果是这样的话,使用dw
好几次确实比d3w
更加合理,因为dw
更加有复用性。在编辑时应该养成“修改操作驱动”的观念。
点命令非常简单但又功能强大,帮助你开始自动化处理简单的任务。在后续的章节中,你将会学习到如何使用Vim的宏命令来自动化处理更多复杂的操作。但是首先,还是让我们来学习一下如何使用寄存器来存取文本吧。