Usr_10
Nvim :help
页面,生成 于 源,使用 tree-sitter-vimdoc 解析器。
VIM 用户手册 - Bram Moolenaar 著
进行大规模更改
在第 4 章中,解释了多种进行小幅更改的方法。本章将介绍如何进行重复或可能影响大量文本的更改。Visual 模式允许对文本块执行各种操作。使用外部程序来执行真正复杂的操作。
记录和回放命令
“.” 命令会重复前一次更改。但如果要执行的操作比单个更改更复杂怎么办?这就是命令记录发挥作用的地方。有三个步骤
1. "q{register}" 命令开始将按键记录到名为 {register}
的寄存器中。寄存器名称必须在 a 和 z 之间。2. 输入您的命令。3. 要完成记录,请按 q(不带任何额外字符)。
您现在可以通过键入命令 "@{register}" 来执行宏。
看一下如何在实践中使用这些命令。您有一个如下所示的文件名列表
stdio.h
fcntl.h
unistd.h
stdlib.h
您想要的是以下内容
#include "stdio.h"
#include "fcntl.h"
#include "unistd.h"
#include "stdlib.h"
您首先将光标移动到第一行的第一个字符。接下来执行以下命令
qa 开始在 a 寄存器中记录宏。^ 移动到行首。i#include "<Esc> 在行首插入字符串 #include "。$ 移动到行尾。a"<Esc> 在行尾追加双引号 (") 字符。j 转到下一行。q 停止记录宏。
现在您已经完成了一次操作,可以通过键入命令 "@a" 三次来重复更改。"@a" 命令前可以加上一个计数,这将导致宏执行该次数。在本例中,您将键入
3@a
移动和执行
您可能需要更改的行位于不同的地方。只需将光标移动到每个位置并使用 "@a" 命令即可。如果您已经执行过一次,可以使用 "@@" 再次执行。这样输入起来更方便一些。如果您现在使用 "@b" 执行寄存器 b,下一个 "@@" 将使用寄存器 b。如果您将回放方法与使用 "." 进行比较,会发现有几个区别。首先,"." 只能重复一次更改。如上例所示,"@a" 可以执行多次更改,并且还可以四处移动。其次,"." 只能记住最后一次更改。执行寄存器允许您进行任何更改,然后仍然使用 "@a" 来回放记录的命令。最后,您可以使用 26 个不同的寄存器。因此,您可以记住 26 个不同的命令序列来执行。
使用寄存器
用于记录的寄存器与您用于 yank 和 delete 命令的寄存器相同。这使您可以将记录与其他命令混合使用来操作寄存器。假设您在 n 寄存器中记录了一些命令。当您使用 "@n" 执行此操作时,您会发现自己做错了。您可以尝试再次记录,但也许您会再次出错。相反,请使用此技巧
G 转到文件末尾。o<Esc> 创建一个空行。"np 将 n 寄存器的文本粘贴到文件中。您现在会在文件中看到您输入的命令作为文本。{edits}
更改错误的命令。这就像编辑文本一样。0 转到行首。"ny$ 将更正后的命令 yank 到 n 寄存器中。dd 删除草稿行。
现在,您可以使用 "@n" 执行更正后的命令。(如果您的记录命令包含换行符,请调整示例中的最后两个项目以包含所有行。)
追加到寄存器
到目前为止,我们一直使用小写字母作为寄存器名称。要追加到寄存器,请使用大写字母。假设您已经记录了一个命令来将单词更改为 c 寄存器。它可以正常工作,但您希望添加一个搜索以更改下一个单词。可以使用以下方法实现
qC/word<Enter>q
您从 "qC" 开始,这会记录到 c 寄存器并追加。因此,写入大写寄存器名称意味着追加到具有相同字母但为小写的寄存器。
这适用于记录以及 yank 和 delete 命令。例如,您想将一系列行收集到 a 寄存器中。使用以下方法 yank 第一行
"ayy
现在移动到第二行,并键入
"Ayy
对所有行重复此命令。a 寄存器现在包含所有这些行,按您 yank 它们的顺序排列。
":substitute" 命令使您能够对整个行范围执行字符串替换。此命令的通用形式如下
:[range]substitute/from/to/[flags]
此命令将 [range] 指定的行的 "from" 字符串更改为 "to" 字符串。例如,可以使用以下命令将所有行中的 "Professor" 更改为 "Teacher"
:%substitute/Professor/Teacher/
注意:":substitute" 命令几乎从不完全拼写出来。大多数情况下,人们使用缩写版本 ":s"。从这里开始将使用缩写。
命令前的 "%" 指定命令对所有行有效。如果没有范围,":s" 仅对当前行有效。有关范围的更多信息,请参阅下一节
10.3。
默认情况下,":substitute" 命令仅更改每行上的第一个出现。例如,前面的命令将更改以下行
Professor Smith criticized Professor Johnson today.
为
Teacher Smith criticized Professor Johnson today.
要更改行上的每个出现,您需要添加 g(全局)标志。命令
:%s/Professor/Teacher/g
结果为(从原始行开始)
Teacher Smith criticized Teacher Johnson today.
其他标志包括 p(打印),它会导致 ":substitute" 命令打印出它更改的最后一行。c(确认)标志告诉 ":substitute" 在执行每次替换之前向您确认。输入以下内容
:%s/Professor/Teacher/c
Vim 找到 "Professor" 的第一个出现并显示它即将更改的文本。您会收到以下提示
replace with Teacher (y/n/a/q/l/^E/^Y)?
此时,您必须输入以下答案之一
y 是;进行此更改。n 否;跳过此匹配项。a 全部;进行此更改以及所有剩余的更改,不再需要进一步确认。q 退出;不再进行任何更改。l 最后;进行此更改然后退出。CTRL-E
将文本向上滚动一行。CTRL-Y
将文本向下滚动一行。
substitute 命令的 "from" 部分实际上是一个模式。与用于 search 命令的模式相同。例如,此命令仅在 "the" 出现在行首时才替换它
:s/^the/these/
如果要替换的 "from" 或 "to" 部分包含斜杠,则需要在它前面加上反斜杠。一个更简单的方法是使用另一个字符而不是斜杠。例如,一个加号
:s+one/two+one or two+
":substitute" 命令以及许多其他 : 命令可以应用于选定的行。这称为范围。范围的简单形式是
{number}
,
{number}
。例如
:1,5s/this/that/g
对第 1 到 5 行执行 substitute 命令。第 5 行包含在内。范围始终放在命令之前。
可以使用单个数字来指定特定行
:54s/President/Fool/
某些命令在您未指定范围时会在整个文件中执行。要使它们仅在当前行上执行,请使用 "." 地址。":write" 命令就是这样。没有范围,它会写入整个文件。要使其仅将当前行写入文件
:.write otherfile
第一行始终为 1。最后一行的编号是多少?"$" 字符用于此目的。例如,要替换从光标到末尾的行
:.,$s/yes/no/
我们之前使用的 "%" 范围实际上是 "1,$" 的简写形式,表示从第一行到最后一行。
在范围内使用模式
假设您正在编辑一本书中的一个章节,并且想将所有 "grey" 替换为 "gray"。但仅在本章中进行替换,而不是在下一章中进行替换。您知道只有章节边界在第一列包含 "Chapter" 一词。然后,此命令将起作用
:?^Chapter?,/^Chapter/s=grey=gray=g
您可以看到搜索模式被使用了两次。第一个 "?^Chapter?" 查找当前位置上方与该模式匹配的行。因此,?pattern? 范围用于向后搜索。类似地,"/^Chapter/" 用于向前搜索下一章的开头。为了避免与斜杠混淆,这里在 substitute 命令中使用了 "=" 字符。斜杠或其他字符也可以使用。
加和减
上面的命令中有一个小错误:如果下一章的标题包含 "grey",它也会被替换。也许这就是您想要的,但如果您不想要呢?那么,您可以指定偏移量。要搜索模式并使用其上方的行
/Chapter/-1
您可以使用任何数字代替 1。要指定匹配项下方的第二行
/Chapter/+2
偏移量也可以与范围中的其他项目一起使用。看看这个
:.+3,$-5
这指定了从光标下方三行开始、在文件最后一行上方五行结束的范围。
使用标记
您无需计算特定位置的行号,无需记住它们并在范围内输入它们,而是可以使用标记。放置标记,如第 3 章中所述。例如,使用 "mt" 标记区域顶部,使用 "mb" 标记底部。然后,您可以使用此范围指定标记之间的行(包括带有标记的行)
:'t,'b
Visual 模式和范围
您可以使用 Visual 模式选择文本。如果然后按 ":" 启动冒号命令,您将看到以下内容
:'<,'>
现在,您可以键入命令,它将应用于视觉选定的行范围。
注意:使用 Visual 模式选择行的一部分,或使用 CTRL-V
选择文本块时,冒号命令仍将应用于整个行。这可能在 Vim 的未来版本中发生变化。
“<” 和 “>” 实际上是标记,分别放置在可视选择区域的开头和结尾。这些标记将保留在它们的位置,直到进行另一个可视选择。因此,您可以使用 “’<’” 命令跳回到可视区域开始的位置。您也可以将这些标记与其他项目混合使用。
:'>,$
这将处理从可视区域结尾到文件结尾的行。
一定数量的行
当您知道要更改多少行时,可以键入数字,然后输入 “:”。例如,当您键入 “5:” 时,您将获得
:.,.+4
现在,您可以键入要使用的命令。它将使用范围 “.”(当前行)到 “.+4”(向下四行)。因此,它跨越五行。
“:global” 命令是 Vim 中更强大的功能之一。它允许您找到模式的匹配项并在那里执行命令。通用格式为
:[range]global/{pattern}/{command}
这类似于 “:substitute” 命令。但是,它不是用其他文本替换匹配的文本,而是执行命令
{command}
。
注意: 为 “:global” 执行的命令必须以冒号开头。普通模式命令不能直接使用。可以使用
:normal 命令来完成此操作。
假设您想将 “foobar” 更改为 “barfoo”,但仅在 C++ 风格的注释中。这些注释以 “//” 开头。使用此命令
:g+//+s/foobar/barfoo/g
它以 “:g” 开头。这代表 “:global”,就像 “:s” 代表 “:substitute”。然后是模式,用加号括起来。由于我们正在查找的模式包含一个斜杠,因此它使用加号字符来分隔模式。接下来是将 “foobar” 更改为 “barfoo” 的替换命令。全局命令的默认范围是整个文件。因此,本示例中没有指定范围。这与 “:substitute” 不同,后者在没有范围的情况下在一个行上工作。该命令并不完美,因为它也会匹配 “//” 出现在行中间的那些行,并且替换也会在 “//” 之前进行。
与 “:substitute” 一样,可以使用任何模式。当您学习更复杂的模式时,可以在这里使用它们。
使用 CTRL-V
可以开始选择矩形区域的文本。有一些命令对文本块执行特殊操作。
在可视块模式下使用 “$” 命令有一些特别之处。当最后使用的移动命令是 “$” 时,可视选择中的所有行都将扩展到行尾,即使光标所在的行更短也是如此。这将一直有效,直到您使用一个水平移动光标的移动命令为止。因此,使用 “j” 将保持此状态,而 “h” 将停止此状态。
插入文本
命令 “I{string}<Esc>” 会在每行中插入文本 {string}
,正好位于可视块的左侧。首先按 CTRL-V
进入可视块模式。现在,您可以移动光标来定义您的块。接下来,您键入 I 进入插入模式,然后键入要插入的文本。当您键入时,文本仅出现在第一行。在按 <Esc>
结束插入后,文本将神奇地插入到可视选择中包含的其他行。示例
include one
include two
include three
include four
将光标移至 “one” 的 “o” 上,然后按
CTRL-V
。使用 “3j” 向下移动到 “four”。现在,您有一个跨越四行的块选择。现在键入
Imain.<Esc>
结果
include main.one
include main.two
include main.three
include main.four
如果块跨越没有延伸到块中的短行,则不会在该行中插入文本。例如,进行一个可视块选择,其中包括此文本的第一行和最后一行中的单词 “long”,因此在第二行中没有选择任何文本
This is a long line
short
Any other long line
^^^^ 选择的块
现在使用命令 “Ivery <Esc>
”。结果是
This is a very long line
short
Any other very long line
在短行中没有插入任何文本。
如果要插入的字符串包含换行符,则 “I” 就像一个普通插入命令一样,只影响块的第一行。
“A” 命令的工作方式相同,只是它在块的右侧追加。并且它确实在短行中插入文本。因此,您可以选择是否要将文本追加到短行。对于 “A” 来说,有一种特殊情况:选择一个可视块,然后使用 “$” 使块扩展到每行的末尾。现在使用 “A” 将文本追加到每行的末尾。使用上面的相同示例,然后键入 “$A XXX<Esc>”,您将得到以下结果
This is a long line XXX
short XXX
Any other long line XXX
这实际上需要使用 “$” 命令。Vim 会记住它被使用过。通过使用其他移动命令将光标移动到最长行的末尾来进行相同的选择,将不会得到相同的结果。
更改文本
可视块 “c” 命令会删除块,然后将您带入插入模式,以使您能够键入一个字符串。该字符串将插入到块中的每一行中。从上面相同的 “long” 词的选择开始,然后键入 “c_LONG_<Esc>”,您将得到以下结果
This is a _LONG_ line
short
Any other _LONG_ line
与 “I” 一样,短行不会改变。此外,您不能在新文本中输入换行符。
“C” 命令会删除从块的左边缘到行尾的文本。然后它会将您置于插入模式,以便您能够键入一个字符串,该字符串将添加到每行的末尾。从相同的文本再次开始,然后键入 “Cnew text<Esc>”,您将得到以下结果
This is a new text
short
Any other new text
请注意,即使只选择了 “long” 这个词,它后面的文本也会被删除。因此,只有可视块左边缘的位置才是真正重要的。同样,没有延伸到块中的短行也被排除在外。
其他更改块中字符的命令
~ 改变大小写(a -> A 以及 A -> a) U 转换为大写(a -> A 以及 A -> A) u 转换为小写(a -> a 以及 A -> a)
用字符填充
要使用一个字符填充整个块,请使用 “r” 命令。再次从上面的相同示例文本开始,然后键入 “rx”
This is a xxxx line
short
Any other xxxx line
移位
命令 “>” 会将选定的文本向右移动一个移位量,插入空格。移位的起点是可视块的左边缘。使用相同的示例,">" 会产生以下结果
This is a long line
short
Any other long line
移位量由
'shiftwidth' 选项指定。要将其更改为使用 4 个空格
:set shiftwidth=4
“<” 命令会删除块左边缘的一个移位量的空格。此命令受现有文本量的限制;因此,如果可用的空格少于一个移位量,它将尽可能多地删除。
合并行
“J” 命令会将所有选定的行合并成一行。因此,它会删除换行符。实际上,换行符、前导空格和尾随空格将被一个空格替换。行结尾后使用两个空格(可以通过
'joinspaces' 选项更改)。让我们使用我们现在已经很熟悉的示例。使用 “J” 命令的结果
This is a long line short Any other long line
“J” 命令不需要块选择。它与 “v” 和 “V” 选择的工作方式完全相同。
如果您不想更改空格,请使用 “gJ” 命令。
当您正在编写电子邮件时,您可能想包含另一个文件。可以使用 “:read {filename}
” 命令来完成此操作。文件的文本将放在光标行下方。从以下文本开始
Hi John,
Here is the diff that fixes the bug
Bye, Pierre.
将光标移至第二行,然后键入
:read patch
名为 “patch” 的文件将被插入,结果如下
Hi John,
Here is the diff that fixes the bug
2c2
< for (i = 0; i <= length; ++i)
---
> for (i = 0; i < length; ++i)
Bye, Pierre.
“:read” 命令接受一个范围。文件将放在该范围的最后一个行号下方。因此,":$r patch" 会将 “patch” 文件追加到文件末尾。如果您想在第一行上方读取文件怎么办?可以使用行号零来实现。该行实际上并不存在,使用它执行大多数命令时,您会收到错误消息。但此命令是允许的
:0read patch
“patch” 文件将放在文件的首行之上。
写入一定数量的行
要将一定数量的行写入文件,可以使用 “:write” 命令。如果没有范围,它将写入整个文件。如果有范围,则只写入指定的行
:.,$write tempo
这会将从光标到文件结尾的行写入文件 “tempo”。如果该文件已存在,您将收到错误消息。Vim 会保护您免受意外覆盖现有文件的风险。如果您知道自己在做什么,并且想要覆盖该文件,请在后面附加 “!”
:.,$write! tempo
小心:”!” 必须紧跟在 “:write” 命令之后,中间没有空格。否则它将成为一个过滤器命令,这将在本章后面进行解释。
追加到文件
在本章的第一部分中,解释了如何将多行收集到寄存器中。也可以将多行收集到文件中。使用以下命令写入第一行
:.write collection
现在将光标移至您要收集的第二行,然后键入以下内容
:.write >>collection
“>>” 会告诉 Vim “收集” 文件不要作为新文件写入,而要将该行追加到末尾。您可以根据需要重复此操作。
当您键入纯文本时,如果每行的长度自动调整以适合窗口,这会很好。要在插入文本时使之生效,请设置
'textwidth' 选项
:set textwidth=78
您可能还记得,在示例 vimrc 文件中,此命令用于每个文本文件。因此,如果您正在使用该 vimrc 文件,那么您已经在使用它了。要检查
'textwidth' 的当前值
:set textwidth
现在,行将被截断,只占用最多 78 个字符。但是,当您在行中间插入文本,或者当您删除几个单词时,行会变得太长或太短。Vim 不会自动重新格式化文本。要告诉 Vim 格式化当前段落
gqap
它以 “gq” 命令开头,这是一个操作符。后面是 “ap”,文本对象代表 “一个段落”。一个段落用空行与下一个段落隔开。
注意: 包含空格的空白行不会分隔段落。这一点很难注意到!
您可以使用任何移动或文本对象来代替 “ap”。如果您的段落被正确地分隔开,可以使用以下命令来格式化整个文件
gggqG
“gg” 将您带到第一行,"gq" 是格式操作符,"G" 是跳转到最后行的移动操作。
如果您的段落没有清晰地定义,您可以只格式化手动选择的行。将光标移动到要格式化的第一行。从命令“gqj”开始。这将格式化当前行和下一行。如果第一行很短,下一行的单词将被追加。如果它太长,单词将被移到下一行。光标移动到第二行。现在可以使用“.”重复该命令。继续执行此操作,直到到达要格式化的文本的末尾。
您的文本中包含小写字母的节标题。您想将“section”一词全部大写。使用“gU”运算符来完成此操作。从光标位于第一列开始
gUw
section header ----> SECTION header
SECTION header ----> section header
您还可以使用“g~”来交换大小写。所有这些都是运算符,因此它们可以与任何移动命令、文本对象和可视模式一起使用。要使运算符作用于行,请将其加倍。删除运算符是“d”,因此要删除一行,请使用“dd”。类似地,“gugu”使整行小写。这可以缩写为“guu”。“gUgU”缩写为“gUU”,而“g~g~”缩写为“g~~”。示例
g~~
Some GIRLS have Fun ----> sOME girls HAVE fUN
Vim 有一组非常强大的命令,它可以做任何事情。但仍然可能有一些事情是外部命令可以做得更好或更快。命令“!{motion}{program}”获取一段文本并通过外部程序过滤它。换句话说,它运行由
{program}
表示的系统命令,并将由
{motion}
表示的文本块作为输入提供给它。此命令的输出然后替换选定的块。因为如果您不熟悉 Unix 过滤器,这将很难总结,所以请看一个例子。sort 命令对文件进行排序。如果执行以下命令,未排序的文件 input.txt 将被排序并写入 output.txt。这在 Unix 和 Windows 上都有效。
sort <input.txt >output.txt
现在在 Vim 中做同样的事情。您想对文件中的第 1 行到第 5 行进行排序。您首先将光标放在第 1 行。接下来,执行以下命令
!5G
“!”告诉 Vim 您正在执行过滤操作。Vim 编辑器期望接下来是一个移动命令,指示要过滤文件的哪一部分。命令“5G”告诉 Vim 转到第 5 行,所以它现在知道要过滤第 1 行(当前行)到第 5 行。为了预料过滤,光标会下降到屏幕底部,并显示一个“!”提示。现在可以输入过滤器程序的名称,在本例中为“sort”。因此,您的完整命令如下
!5Gsort<Enter>
结果是对前 5 行运行 sort 程序。程序的输出替换了这些行。
line 55 line 11 line 33 line 22 line 11 --> line 33 line 22 line 44 line 44 line 55 last line last line
“!!”命令通过过滤器过滤当前行。在 Unix 中,“date”命令打印当前时间和日期。“!!date<Enter>”用“date”的输出替换当前行。这对于在文件中添加时间戳很有用。
当它不起作用时
启动 shell、向它发送文本并捕获输出需要 Vim 确切地知道 shell 的工作原理。如果在过滤时遇到问题,请检查这些选项的值
在 Unix 上,这几乎永远不会成为问题,因为有两种类型的 shell:“sh”型和“csh”型。Vim 检查
'shell' 选项并根据它是否在
'shell' 中看到“csh”自动设置相关选项。然而,在 MS-Windows 上,有很多不同的 shell,您可能需要调整选项才能使过滤工作。检查帮助以获取有关选项的更多信息。
读取命令输出
要将当前目录的内容读入文件,请使用以下命令
在 Unix 上
:read !ls
在 MS-Windows 上
:read !dir
“ls”或“dir”命令的输出将被捕获并插入文本中,位于光标下方。这类似于读取文件,只是使用“!”来告诉 Vim 后面跟着一个命令。该命令可能包含参数。可以使用范围来告诉 Vim 应该将行放在哪里
:0read !date -u
这将在文件的顶部插入当前时间和日期的 UTC 格式。(当然,如果您有一个接受“-u”参数的 date 命令。)请注意与使用“!!date”的区别:后者替换了一行,而“:read !date”将插入一行。
将文本写入命令
Unix 命令“wc”计算单词。要计算当前文件中的单词
:write !wc
这与之前的写入命令相同,但不是文件名,而是使用“!”字符和外部命令的名称。写入的文本将作为标准输入传递给指定的命令。输出可能如下所示
“wc”命令不是详细的。这意味着您有 4 行、47 个单词和 249 个字符。
注意这个错误
:write! wc
这将强制写入当前目录中的文件“wc”。空格在这里很重要!
重新绘制屏幕
如果外部命令产生了错误消息,显示可能已乱七八糟。Vim 非常高效,它只重新绘制那些它知道需要重新绘制的屏幕部分。但它无法知道另一个程序写了什么。要告诉 Vim 重新绘制屏幕
CTRL-L