跳到主要内容

点命令

在编辑文本时,我们应该尽可能地避免重复的动作。在这一章节中,你将会学习如何使用点命令来重放上一个修改操作。点命令是最简单的命令,然而又是减少重复操作最为有用的命令。

用法

正如这个命令的名字一样,你可以通过按下.键来使用点命令。

比如,如果你想将下面文本中的所有”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..

这里的修改操作是d2j2j不是一个移动(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的宏命令来自动化处理更多复杂的操作。但是首先,还是让我们来学习一下如何使用寄存器来存取文本吧。