Vim技巧
一、管理多个文件
1.1、用缓冲区列表管理打开的文件
Vim 允许我们同时在多个文件上工作。缓冲区列表记录了一次编辑会话中打开的所有文件。
命令 | 效果 |
---|---|
:e[dit] {file} |
打开文件到新缓冲区(若文件已打开则切换到对应缓冲区 |
:ene[w] |
创建新的无名缓冲区 |
:new |
水平分割窗口并创建新缓冲区 |
:vnew |
垂直分割窗口并创建新缓冲区 |
:bn[ext] |
切换到下一个缓冲区 |
:bp[revious] |
切换到上一个缓冲区 |
:bfirst |
跳转到列表开头 |
:blast |
跳转到列表结尾 |
:b {number} 或:b {name} |
通过编号或文件名(支持 Tab 补全)切换缓冲区 |
:ls 或:buffers |
列出所有缓冲区(显示编号、状态、文件名) |
状态标记 | |
% |
当前窗口显示的缓冲区 |
# |
轮换文件(可通过 Ctrl+^ 切换) |
+ |
已修改未保存 |
a |
活动缓冲区(已加载且显示在某个窗口) |
:bd[elete] |
关闭当前缓冲区(若未保存会提示) |
:bd {number} |
关闭指定编号的缓冲区 |
:bw[ipeout] |
强制关闭缓冲区(丢弃未保存的修改) |
:1,3bd |
关闭编号 1 到 3 的缓冲区(范围操作) |
:w |
保存当前缓冲区 |
:w {file} |
另存为新文件(不改变当前缓冲区关联的文件) |
:wa[ll] |
保存所有已修改的缓冲区 |
:b# |
切换到最近访问的缓冲区(相当于 Ctrl+^) |
Ctrl+^ |
快速切换当前缓冲区和轮换缓冲区(# 标记的缓冲区) |
:set hidden |
允许隐藏已修改的缓冲区(切换时不强制保存) |
缓冲区列表:
bash
vim *.txt # 打开多个.txt文件
:ls # 查看缓冲区列表
# 结果如下
1 %a "a.txt" line 1
2 "b.txt" line 0
:bn # 切换到列表中的下一个缓冲区
:ls
# 结果如下
1 # "a.txt" line 1
2 %a "b.txt" line 1
# % 符号指明哪个缓冲区在当前窗口中可见, 而 # 符号则代表轮换文件。
使用缓冲区列表:
命令 | 效果 |
---|---|
:bn[ext] |
切换到下一个缓冲区 |
:bp[revious] |
切换到上一个缓冲区 |
:bfirst |
跳转到列表开头 |
:blast |
跳转到列表结尾 |
:b {number} 或:b {name} |
通过编号或文件名(支持 Tab 补全)切换缓冲区 |
:bufdo |
在:ls列出的所有缓冲区上执行Ex命令 |
删除缓冲区:
bash
# 语法
:bdelete N1 N2 N3
:N,M bdelete
删除一个缓冲区并不会影响缓冲区所关联的文件,而只是简单地把该文件在内存中的映像删掉。如果我们想删除编号 5~10(包含 5 和 10)的缓冲区,可以执行 :5,10bd; 然而, 如果想要保留编号为 8 的缓冲区的话, 那么就只能用 :bd 5 6 7 9 10 了。
1.2、用参数列表将缓冲区分组
- 参数列表(args)
- 通过命令行或 :args 命令指定的一组文件,Vim 会将这些文件加载到缓冲区列表,但不会立即全部打开(按需加载)。
- 与缓冲区列表的区别:
- 缓冲区列表(:buffers):包含所有已打开的文件(包括通过 :e 手动打开的)。
- 参数列表(:args):仅包含启动时或显式指定的文件集合,适合批量操作。
初始化参数列表:
bash
# 启动时指定
vim file1.txt file2.txt # 启动时传入参数列表
# 在Vim中修改参数列表
:args *.txt " 将当前目录下所有 .txt 文件加入参数列表
:args file1.js file2.js " 手动指定文件
:args `find . -name "*.md"` " 通过反引号命令动态生成列表(需支持 shell)
查看参数列表:
bash
:args " 显示当前参数列表
:arglist " 同上
# 输出示例 [] 表示当前正在编辑的参数文件
[file1.txt] file2.txt file3.js
切换参数文件:
bash
:next (:n) " 切换到下一个参数文件
:prev (:prev) " 切换到上一个参数文件
:first (:first) " 跳转到第一个参数文件
:last (:last) " 跳转到最后一个参数文件
:argadd file4.txt " 向参数列表追加文件
:argdel file2.txt " 从参数列表中删除文件
批量操作:
bash
:argdo %s/foo/bar/g " 在所有参数文件中替换 'foo' 为 'bar'
:argdo update " 保存所有修改过的参数文件
:argdo set ft=python " 对所有参数文件设置文件类型为 Python
用Glob模式指定文件:
Glob模式 | 所匹配的文件 |
---|---|
:args *.* |
index.html app.js |
:args **/*.js |
app.js lib/framework.js app/controllers/Mailer.js ...etc |
:args **/*.* |
app.js index.html lib/framework.js lib/theme.css app/controllers/Mailer.js ...etc |
管理隐藏缓冲区:
Vim 对被修改过的缓冲区会给予特殊对待,以防未加保存就意外退出。
bash
vim *.txt # 打开a.txt b.txt
:ls # 查看缓冲区列表
# 结果
1 %a "a.txt" line 1
2 "b.txt" line 0
首先,我们对 a.txt 做点修改,按 Go
在缓冲区的结尾增加一个空行。先不要保存修改,让我们查看一下当前的缓冲区列表:
bash
1 %a + "a.txt" line 17
2 "b.txt" line 0
缓冲区 a.txt 前有一个 +
号,表示这个缓冲区被修改过了。如果现在保存文件的话, 缓冲区的内容就会被写入磁盘里, 而 + 号也会消失了。 但是我们先不急着保存,而是试着切换一下缓冲区:
bash
:bn
# 结果
E37: No write since last change (add ! to override)
此时,Vim 会弹出一条错误信息,说当前缓冲区中有未保存的改动。让我们试一下括号中的建议,在上述命令的结尾加一个叹号:
bash
:bn!
:ls
# 结果
1 #h + "a.txt" line 17
2 %a "b.txt" line 1
叹号会强制 Vim 切换缓冲区,即使当前缓冲区中有未保存的修改,也会继续切换。如果现在再运行 :ls 命令,就会发现 b.txt 被标记为 a,表示它当前是活动缓冲区(active) ,而 a.txt 则被标记为 h,表示它是一个隐藏缓冲区(hidden)。
在退出时处理隐藏缓冲区:
当一个缓冲区被隐藏后,Vim 允许我们像往常一样工作。我们可以打开其他缓冲区,对其进行修改、保存等,没有任何不同。也就是说,一直到尝试退出编辑会话前,一切如常。然而,当我们想关闭编辑会话时,Vim 就会提醒我们某个缓冲区中有未保存的修改:
bash
:q
E37: No write since last change
E162: No write since last change for buffer "a.txt"
Vim 会把第一个有改动的隐藏缓冲区载入当前窗口,这样我们就可以决定如何处理它。如果要保留修改,可以执行 :write
命令把缓冲区保存到文件;如果想摒弃此修改,可以执行 :edit!
,重新从磁盘读取此文件,这会用文件的内容覆盖缓冲区中的内容。当缓冲区内容与磁盘文件一致后,我们就可以再次尝试执行 :quit
命令了。
如果会话里有不止一个被修改过的隐藏缓冲区,那么每次执行 :quit
命令时,Vim 都会激活下一个未保存的缓冲区。同样的,我们可以用 :write
及 :edit!
来保存或摒弃此修改。当没有其他窗口和隐藏缓冲区时,:q
命令就会关闭 Vim。
如果想退出 Vim 而不想对未保存的修改进行检查,可以执行 :qall!
命令;如果想保存所有有改动的缓冲区而无需逐个检查,可以用 :wall
命令。
命令 | 用途 |
---|---|
:w[rite] |
把缓冲区内容写入磁盘 |
:e[dit]! |
把磁盘文件内容读入缓冲区(即回滚所做修改) |
:qa[ll]! |
关闭所有窗口,摒弃修改而无需警告 |
:wa[ll]! |
把所有改变的缓冲区写入磁盘 |
1.3、将工作区切分成窗口
Vim 允许我们将工作区切分成若干窗口,在这些窗口里并排显示多个缓冲区。
创建分割窗口:
Vim 在启动时只会打开单个窗口。用 <C-w>s
命令可以水平切分此窗口,使之成为两个高度相同的窗口;或者可以用 <C-w>v
命令对其进行垂直切分,这样会产生两个宽度相同的窗口。这两条命令可以重复任意多次,结果就会把工作区一次次地切分为更小的窗口,就像细胞分裂那样。

每次执行完 <C-w>s
和 <C-w>v
命令后,新生成的两个窗口都会显示与原窗口相同的缓冲区。把同一缓冲区显示在不同窗口里会很有用,特别是在编辑长文件时。举个例子,我们可以滚动其中一个窗口,使之显示缓冲区的一部分,这样,在修改第二个窗口中缓冲区的另外一部分时,就可以参考第一个窗口中的内容。
我 们 可 以 用 :edit 命 令 把 另 外 一 个 缓 冲 区 载 入 活 动 窗 口 中 。 如 果 先 执 行<C-w>s
,再执行 :edit {filename}
,就会把工作区分成两个窗口,并在其中一个窗口中打开新缓冲区,而另一个窗口则继续显示原有的缓冲区。另外一种做法是使用 :split {filename}
命令,它把上述两步合并成为了一步。下表总结了把工作区切分为窗口的几种方式:
命令 | 用途 |
---|---|
<C-w>s |
水平切分当前窗口,新窗口仍显示当前缓冲区 |
<C-w>v |
垂直切分当前窗口,新窗口仍显示当前缓冲区 |
:sp[lit] {file} |
水平切分当前窗口,并在新窗口中载入{file} |
:vsp[lit] {file} |
垂直切分当前窗口,并在新窗口中载入{file} |
在窗口间切换:
命令 | 用途 |
---|---|
<C-w>w |
在窗口间循环切换 |
<C-w>h |
切换到左边的窗口 |
<C-w>j |
切换到下边的窗口 |
<C-w>k |
切换到上边的窗口 |
<C-w>l |
切换到右边的窗口 |
关闭窗口:
Ex命令 | 普通模式命令 | 用途 |
---|---|---|
:clo[se] |
c` | 关闭活动窗口 |
:on[ly] |
o` | 只保留活动窗口,关闭其他所有窗口 |
改变窗口大小及重新排列窗口:
命令 | 用途 |
---|---|
<C-w>= |
使所有窗口等宽、等高 |
<C-w>_ |
最大化活动窗口的高度 |
` | ` |
[N]<C-w>_ |
把活动窗口的高度设为[N]行 |
`[N] | ` |
1.4、用标签页将窗口分组
打开及关闭标签页:
命令 | 用途 |
---|---|
:tabe[dit] {filename} |
在新标签页中打开 {filename} |
<C-w>T |
把当前窗口移到一个新标签页 |
:tabc[lose] |
关闭当前标签页及其中的所有窗口 |
:tabo[nly] |
只保留活动标签页, 关闭所有其他标签页 |
在标签页间切换:
标签页的编号从 1 开始, 我们可以用 {N}gt
命令在标签页间切换,可以把此命令记成"跳到标签页{N}" 。当此命令带一个数字前缀时,Vim 会跳到指定编号的标签页;如果省略了数字前缀,则会跳到下一个标签页。 gT 命令的功能与此相同,只是跳转方向相反。
Ex 命令 | 普通模式命令 | 用途 |
---|---|---|
:tabn[ext] {N} |
{N}gt |
切换到编号为 {N} 的标签页 |
:tabn[ext] |
gt |
切换到下一标签页 |
:tabp[revious] |
gT |
切换到上一标签页 |
重拍标签页:
用 :tabmove [N]
命令可以重新排列标签页。当 [N] 为 0 时,当前标签页会被移到开头;如果省略了 [N],当前标签页会被移到结尾。如果你的终端支持鼠标,或是你正在使用 GVim,那么你也可以通过鼠标拖曳来进行重排操作。
1.5、用:edit命令打开文件
在 Vim 中,:edit
命令允许通过文件的绝对路径或相对路径来打开文件。
相对于当前工作目录打开一个文件:
:edit {file}
命令可以接受相对于工作目录的文件路径。
bash
:edit lib/framework.js
:edit app/controllers/Navigation.js
相对于活动文件目录打开一个文件:
假设我们正在编辑 app/controllers/Navigation.js,紧接着我们要编辑同一目录下的 Main.js。一种做法是输入从工作目录开始的路径,直到抵达该文件,然而这似乎有点儿舍近求远。
bash
:edit %<Tab>
# % 符号代表活动缓冲区的完整文件路径
# 按 <Tab>键会将其展开,使之显示为活动缓冲区的完整文件路径。
# 虽然这不是我们想要的结果,但是已经很接近了。
:edit %:h<Tab>
# :h 修饰符会去除文件名,但保留路径中的其他部分
# 在此例中,输入的 %:h<Tab> 会被展开为当前文件所在目录的路径,即 app/controllers/
:edit %:h<Tab>M<Tab>
# 后续输入M,再<Tab>键补全全文件名
1.6、使用:find打开文件
:find
命令允许我们通过文件名打开一个文件,但无需输入该文件的完整路径。要想利用此功能,我们首先要配置 'path' 选项。
配置path选项:
'path'
选项允许我们指定一些目录,当调用 :find
命令时,Vim 会在这些目录中进行查找 。在本例中,我们想让 app/controllers
及app/views
目录下的文件更容易被找到,因此可以执行下面的命令,把这些目录加入到查找路径中:
bash
# ** 通配符会匹配 app/ 目录下的所有子目录。
:set path+=app/**
使用:find命令,通过文件名查找文件:
如果想打开 app/controllers/Navigation.js文件,我们可以输入这条命令:
bash
:find Navigation.js
1.7、把文件保存到不存在的目录中
即使缓冲区的路径中包含了不存在的目录, Vim 仍允许我们对该缓冲区进行编辑,只是在我们试图将缓冲区写入文件时,Vim 会报错。
:edit {file}
命令一般用于打开一个已存在的文件。然而,如果我们指定了一个不存在的文件路径,那么 Vim 就会创建一个新的空白缓冲区。如果我们此时按<C-g>
,就会看到该缓冲区被标识为"新文件" ( 命令用于显示当前文件的文件名及状态 ) 。随后,当我们执行:write
命令时,Vim 就会尝试将此缓冲区的内容写到创建该缓冲区时所指定的文件路径中。
如果在执行 :edit {file}
时所指定的文件路径中包含尚未存在的目录,事情就会变得有点棘手。这种情况下,我们可以调用外部的 mkdir 程序对此做出补救:
bash
:!mkdir -p %:h
:write
二、动作命令在文档中移动
2.1、区分实际行与屏幕行
与许多文本编辑器不同, Vim 会区分实际行与屏幕行。当 'wrap'
设置被启用时(缺省启用) ,每个超出窗口宽度的文本行都会被回绕显示,以保证没有文本显示不出来。这样一来,文件中的一行也许会被显示为屏幕上的若干行。
要想知道实际行与屏幕行之间的不同,最简单的方法是启用 'number'
设置。启用后,以行号开头的行对应着一个实际行,它们会占据着屏幕上的一行或几行;当一行文本为适应窗口宽度而回绕时,回绕行的前面则不会显示行号。
命令 | 光标动作 |
---|---|
j |
向下移动一个实际行 |
gj |
向下移动一个屏幕行 |
k |
向上移动一个实际行 |
gk |
向上移动一个屏幕行 |
0 |
移动到实际行的行首 |
g0 |
移动到屏幕行的行首 |
^ |
移动到实际行的第一个非空白字符 |
g^ |
移动到屏幕行的第一个非空白字符 |
$ |
移动到实际行的行尾 |
g$ |
移动到屏幕行的行尾 |
2.2、基于单词移动
命令 | 光标动作 |
---|---|
w |
正向移动到下一单词的开头 |
b |
反向移动到当前单词/上一单词的开头 |
e |
正向移动到当前单词/下一单词的结尾 |
ge |
反向移动到上一单词的结尾 |
ea |
在当前单词结尾后添加 |
2.3、对字符进行查找
命令 | 作用 |
---|---|
f{char} |
正向移动到下一个 {char} 所在之处 |
F{char} |
反向移动到上一个 {char} 所在之处 |
t{char} |
正向移动到下一个 {char} 所在之处的前一个字符上 |
T{char} |
反向移动到上一个 {char} 所在之处的后一个字符上 |
; |
正向跳转到下一个匹配的字符 |
, |
反向跳转到上一个匹配的字符 |
示例:删除,到.之间的文本
按键操作 | 缓冲区内容 |
---|---|
{start} |
I 've been expecting you, Mister Bond. |
f, |
I've been expecting you, Mister Bond. |
dt. |
I've been expecting you. |
2.4、操作分隔符的文本对象
文本对象允许我们操作括号、 被引用的文本、 XML 标签以及其他文本中的常见结构。
Vim 的文本对象由两个字符组成,第一个字符永远是 i 或是 a。我们一般说,以 i
开头的文本对象会选择分隔符内部的文本,而以 a
开头的文本对象则会选择包括分隔符在内的整个文本。为了便于记忆,可以把 i 想成"inside" ,而把 a 想成
"around" 或"all"。
文本对象 | 选择区域 |
---|---|
a) 或 ab |
一对圆括号 (parentheses) |
i) 或 ib |
圆括号 (parentheses) 内部 |
a} 或 aB |
一对花括号 {braces} |
i} 或 iB |
花括号 {braces} 内部 |
a] |
一对方括号 [brackets] |
i] |
方括号 [brackets] 内部 |
a> |
一对尖括号 <angle brackets> |
i> |
尖括号 <angle brackets> 内部 |
a' |
一对单引号 'single quotes' |
i' |
单引号 'single quotes' 内部 |
a" |
一对双引号 "double quotes" |
i" |
双引号 "double quotes" 内部 |
a` | 一对反引号 backticks |
i` | 反引号 backticks 内部 |
at |
一对 XML 标签 <xml>tags</xml> |
it |
XML 标签内部 |
示例:
按键操作 | 缓冲区内容 |
---|---|
vi} |
![]() |
va" |
![]() |
vi> |
![]() |
vit |
![]() |
vat |
![]() |
va] |
![]() |
用文本对象执行操作:
每当在命令语法里看到{motion}
时,你也可以在这个地方使用文本对象,常见的例子包括 d{motion}
、c{motion}
和 y{motion}
。
示例:将下面文本中的{url}替换为#,然后再用一个
文本标记把{title}替换掉
按键操作 | 缓冲区内容 |
---|---|
{start} |
![]() |
ci"#<Esc> |
![]() |
citclick here<Esc> |
![]() |
2.5、操作文本块的文本对象
文本对象 | 选择范围 |
---|---|
iw |
当前单词 |
aw |
当前单词及一个空格 |
iW |
当前字串 |
aW |
当前字串及一个空格 |
is |
当前句子 |
as |
当前句子及一个空格 |
ip |
当前段落 |
ap |
当前段落及一个空行 |
示例:删除下句中的单词"excellent" ,此时可以用 daw 命令
按键操作 | 缓冲区内容 |
---|---|
{start} |
![]() |
daw |
![]() |
示例:把此单词改成另外一个单词,这次可以用 ciw 命令
按键操作 | 缓冲区内容 |
---|---|
{start} |
![]() |
ciwmost<Esc> |
![]() |
2.6、标记位置跳转
m{a-zA-Z}
命令会用选定的字母标记当前光标所在位置。小写位置标记只在每个缓冲区里局部可见,而大写位置标记则全局可见。
`{a-zA-Z} 命令则把光标移动到设置此位置标记时光标所在之处。
自动位置标记:
位置标记 | 跳转到 |
---|---|
`` | 当前文件中上次跳转动作之前的位置 |
`. | 上次修改的地方 |
`^ | 上次插入的地方 |
`[ | 上次修改或复制的起始位置 |
`] | 上次修改或复制的结束位置 |
`< | 上次高亮选区的起始位置 |
`> | 上次高亮选区的结束位置 |
2.7、在匹配括号间跳转
Vim 提供了一个动作命令, 让我们可以在开、 闭括号间跳转。 在激活了 matchit.vim插件后,此命令也可以用于成对的 XML 标签,以及某些编程语言中的关键字上。
按键操作 | 缓冲区内容 |
---|---|
{start} |
![]() |
% |
![]() |
h |
![]() |
% |
![]() |
l |
![]() |
% |
![]() |
示例:把 %w{London Berlin New\ York} 改 成 普 通 的 列 表 定 义 ["London","Berlin", "New York"],因此要把大括号改成方括号
按键操作 | 缓冲区内容 |
---|---|
{start} |
![]() |
dt{ |
![]() |
% |
![]() |
r] |
![]() |
`` | ![]() |
r[ |
![]() |
2.8、在匹配的关键字间跳转
Vim 在发布时带了一个名为 matchit 的插件,它增强了 % 命令的功能。激活此插件后, % 命令就可以在配对的关键字间跳转。例如,在一个 HTML 文件里,用 % 命令 可 以 在 开 标 签 和 闭 标 签 间 跳 转 ; 在 一 个 Ruby 文 件 里 , 可 以 用 它 在 配 对 的class/end、def/end,以及 if/end 间跳转。
bash
# 启动时自动加载matchit插件
set nocompatible
filetype plugin on
runtime macros/matchit.vim
示例:把New York放在双引号里
按键操作 | 缓冲区内容 |
---|---|
{start} |
![]() |
vee |
![]() |
S" |
![]() |
三、在文件间跳转
3.1、跳转列表
Vim 会记录跳转前后的位置,并提供了一些命令让我们能够沿原路返回。
命令 | 作用 |
---|---|
<C-o> |
后退 |
<C-i> |
前进 |
:jumps |
查看那跳转列表 |
任何改变当前窗口中活动文件的命令,都可以被称为跳转命令。Vim 会把执行跳转命令之前和之后的光标位置,记录到跳转列表中。
命令 | 用途 |
---|---|
[count]G |
跳转到指定的行号 |
/pattern<CR>/?pattern<CR>/n/N |
跳转到下一个/上一个模式出现之处 |
% |
跳转到匹配的括号所在之处 |
(/) |
跳转到上一句/下一句的开头 |
{/} |
跳转到上一段/下一段的开头 |
H/M/L |
跳到屏幕最上方/正中间/最下方 |
gf |
跳转到光标下的文件名 |
<C-]> |
跳转到光标下关键字的定义之处 |
'{mark}/`{mark} | 跳转到一个位置标记 |
3.2、改变列表
每当对文档做出修改后,Vim 都会记录当时光标所在的位置。
命令 | 作用 |
---|---|
g; |
方向跳转 |
g, |
正向跳转 |
:changes |
查看改变列表 |
四、寄存器
4.1、用无名寄存器实现删除、复制与粘贴操作
调换字符:
按键操作 | 缓冲区内容 |
---|---|
{start} |
Practical lvim |
F空格 |
Practica lvim |
x |
Practical vim |
p |
Practical vim |
将最后两条命令组合在一起,即 xp
,可被用于"调换光标之后的两个字符" 。
调换文本行:
按键操作 | 缓冲区内容 |
---|---|
{start} |
![]() |
dd |
![]() |
p |
![]() |
把以上命令序列连起来,即ddp
,可被用于"调换当前行和它的下一行" 。
4.2、引用寄存器
Vim 的删除、复制与粘贴命令都会用到众多寄存器中的某一个。我们可以通过给命令加 "{register}
前缀的方式指定要用的寄存器。若不指明,Vim 将缺省使用无名寄存器。
普通模式中,如果我们想把当前单词复制到寄存器 a中,可执行 "ayiw
,或者,可以用 "bdd
,把当前整行文本剪切至寄存器 b 中。在此之后,我们既可以输入 "ap
粘贴来自寄存器 a的单词,也可使用 "bp
命令粘贴来自寄存器 b的一整行文本,两者互不干扰。
命令模式中,可以执行 :delete c
,把当前行剪切到寄存器 c,然后再执行 :put c
命令将其粘贴至当前光标所在行之下。
4.3、无名寄存器("")
倘若我们没有指定要使用的寄存器,Vim 将缺省使用无名寄存器,它可以用双引号表示。为了显式地引用该寄存器,我们得使用两个双引号。例如,""p
,它完全等同于 p
命令。
x
、s
、d{motion}
、c{motion}
与 y{motion}
命令(以及它们对应的大写命令)都会覆盖无名寄存器中的内容。
4.4、复制专用寄存器("0)
当我们使用 y{motion}
命令时,要复制的文本不仅会被拷贝到无名寄存器中,而且也被拷贝到了复制专用寄存器中,后者可用数字 0加以引用。
按键操作 | 缓冲区内容 |
---|---|
yiw |
![]() |
jww |
![]() |
diw |
![]() |
"0P |
![]() |
4.5、有名寄存器("a-"z)
Vim 提供了一组以26 个英文字母命名的有名寄存器。这意
味着我们可以剪切( "ad{motion}
) 、复制("ay{motion}
)或者粘贴("ap
)多达26 段文本。
用小写字母引用有名寄存器,会覆盖该寄存器的原有内容,而换用大写字母的话,则会将新内容添加到该寄存器的原有内容之后。
4.6、黑洞寄存器("_)
黑洞寄存器是个有去无回的地方,可用下划线引用它。如
果我们运行 "_d{motion}
命令,Vim 将删除该文本且不保存任何副本。
当我们只想删除文本却不想覆盖无名寄存器中的内容时,此命令很管用。
4.7、系统剪贴板("+)与选择专用寄存器("*)
Vim 的加号寄存器与系统剪贴板等效,可用 + 号引用。
如果我们在外部程序中用剪切或复制命令获取了文本,就可以通过 "+p
命令(或在插入模式下用 <C-r>+
)将其粘贴到 Vim 内部。相反地,如果在 Vim 的复制或删除命令之前加入 "+
,相应的文本将被捕获至系统剪贴板。
4.8、表达式寄存器("=)
Vim 的寄存器通常被认为是保存一段文本的容器。 然而, 通过 = 号引用的表达式寄存器却是个例外。当我们从表达式寄存器获取内容时,Vim 将跳到命令行模式,并显示提示符"=" 。这时,我们可以输入一段 Vim 脚本表达式并按 <CR>
执行,如果返回的是字符串(或者可被强制转换成字符串的数据) ,Vim 将会使用它。
4.9、其他寄存器
Vim 还提供了几组可被隐式赋值的寄存器。它们被称作只读寄存器:
寄存器 | 内容 |
---|---|
"% |
当前文件名 |
"# |
轮换文件名 |
". |
上次插入的文本 |
": |
上次执行的Ex命令 |
"/ |
上次查找的模式 |
4.10、用寄存器中的内容替换高亮选区的文本
在可视模式下使用 p命令时,Vim 将用我们指定的寄存器内容来替换高亮选区中的文本。
按键操作 | 缓冲区内容 |
---|---|
yiw |
![]() |
jww |
![]() |
ve |
![]() |
p |
![]() |
交换两个词:改为"fish and chips
按键操作 | 缓冲区内容 |
---|---|
{start} |
![]() |
fc |
![]() |
de |
![]() |
mm |
![]() |
ww |
![]() |
ve |
![]() |
p |
![]() |
`m | ![]() |
P |
![]() |
4.11、把寄存器的内容粘贴出来
普通模式下的粘贴命令,根据要插入文本的性质不同,执行结果也不同。确定要粘贴的文本区域是面向行的还是面向字符的,将有助于我们制定不同的策略。
p
命令旨在将寄存器中的文本粘贴到光标之后P
命令用于将文本插入到光标之前<C-r>{register}
总会被插入至光标之前
粘贴面向字符的区域:
按键操作 | 缓冲区内容 |
---|---|
yiw |
![]() |
jww |
![]() |
ciw<C-r>0<Esc> |
![]() |
使 用 ciw 命 令 带 来 的 额 外 好 处 是 , 此 时 用. 命 令 可 以 把 当 前 单 词 替 换 为"collection"。 |
粘贴面向行的区域:
按键操作 | 缓冲区内容 |
---|---|
yap |
![]() |
gP |
![]() |
五、宏
5.1、宏的读取与执行
宏允许我们把一段修改序列录制下来,用于之后的回放。
把命令序列录制成宏:
q
键既是"录制"按钮,也是"停止"按钮。为了录制我们的按键操作,一开始需要按 q{register}
,从而指定一个用于保存宏的寄存器。当状态栏中出现"记录中"时,表示录制已经开始。此后,我们执行的每一条命令都将被宏捕获,直到我们再次按下 q键停下为止。
按键操作 | 缓冲区内容 |
---|---|
qa |
![]() |
A;<Esc> |
![]() |
Ivar空格<Esc> |
![]() |
q |
![]() |
可以通过以下命令查看寄存器 a 中的内容:
bash
:reg a
# 结果
Type Name Content
c "a A;^[<80><fd>5Ivar ^[<80><fd>5
通过执行宏来回放命令序列:
我们可以用@{register}
命令执行指定寄存器的内容,也可以用@@
来重复最近调用过的宏。
按键操作 | 缓冲区内容 |
---|---|
{start} |
![]() |
j |
![]() |
@a |
![]() |
j@@ |
![]() |
通过执行这个刚刚录制好的宏,Vim 对随后的每一行也重复了这两处相同的修改。 |
5.2、加次数回放宏
例如,有文本如下:
js
var foo = "method("+argument1+","+argument2+")";
准备把它变成这样:
js
var foo = "method(" + argument1 + "," + argument2 + ")";
按键操作 | 缓冲区内容 |
---|---|
{start} |
![]() |
f+ |
![]() |
s空格+空格<Esc> |
![]() |
qq;.q |
![]() |
22@q |
![]() |
在本例中,我们打算将这个宏执行 10 次。但如果回放 11 次的话,最后一次执行将会被中止。换句话说,只要我们用大于或等于 10 的次数调用该宏就可以完成任务了。
5.3、在连续的文本行上重复修改
对于多行范围内的重复性改动,可以先录制一个宏,然后再在每一行上回放,这将会极大减轻我们的工作量。该功能可用两种执行宏的方式实现,串行或者并行。
bash
1. one
2. two
3. three
4. four
# 转换为
1) One
2) Two
3) Three
4) Four
录制工作单元:
按键操作 | 缓冲区内容 |
---|---|
qa |
![]() |
0f. |
![]() |
r) |
![]() |
w~ |
![]() |
j |
![]() |
q |
![]() |
以串行方式执行宏:
可以调用 3 次 @a
命令完成这次任务,但运行 3@a
会更快:
按键操作 | 缓冲区内容 |
---|---|
{start} |
![]() |
3@a |
![]() |
但是串行执行可能会遇到问题,假设即将要处理的文本中,会时不时地被注释行所隔断:
1. one
2. two
// break up the monotony
3. three
4. four
在这种情况下,宏在执行到第 3 行时停了下来,所以需要采用并行执行方式。
以并行方式执行宏:
按键操作 | 缓冲区内容 |
---|---|
qa |
![]() |
0f.r)w~ |
![]() |
q |
![]() |
jVG |
![]() |
:'<,'>normal @a |
![]() |
5.4、给宏追加命令
有时候,我们在录制宏的过程中会漏掉某个至关重要的步骤。在这种情况下,我们没必要从头开始重录所有的步骤,而是可以在现有宏的结尾附加额外的命令。
在我们输入 qa
时,Vim 将开始录制接下来的按键操作,并将它们保存到寄存器 a
中,这会覆盖该寄存器原有的内容。如果我们输入的是 qA
的话,Vim 也会录制按键操作,但会把它们附加到寄存器 a
原有的内容之后。我们可以用这种方式更正该错误。
5.5、用迭代求值的方式给列表编号
假设我们要为一些连续的项目进行编号,以下列文本作为演示:
bash
partridge in a pear tree
turtle doves
French hens
calling birds
golden rings
# 转换为
1) partridge in a pear tree
2) turtle doves
3) French hens
4) calling birds
5) golden rings
:let
命令可以创建变量:echo
命令可以查看变量<C-r>-i<CR>
可以插入变量i的值
录制宏:
按键操作 | 缓冲区内容 |
---|---|
:let i=1 |
![]() |
qa |
![]() |
I<C-r>=i<CR>)空格<Esc> |
![]() |
:let i += 1 |
![]() |
q |
![]() |
执行宏:
:normal @a
命令将指示 Vim 在高亮选中的每一行上执行这个宏。i 的初始值是 2,但它在每次宏执行完后都会递增。最终,每行都以连续的数字开头了。
按键操作 | 缓冲区内容 |
---|---|
{start} |
![]() |
jVG |
![]() |
:'<,'>normal @a |
![]() |