管理文件
vim允许同时在多个文件上工作。
用缓冲区列表管理打开的文件
在一次编辑会话中,可以打开多个文件。用Vim的缓冲区列表可以对这些文件进行管理。
此章内容基本只能用于原生vim,编辑器已经提供了很好的文件管理功能,所以略过此处。
用动作命令在文档中移动
让手指保持在本位行上
使用hjkl
可以进行左下上右的移动
区分实际行与屏幕行
有时编辑器为了展示一行的完整内容会在屏幕上换行展示,而上面提到的命令是换实际行下面的表格对相关命令进行了总结
命令 | 光标动作 |
---|---|
j |
向下移动一个实际行 |
gj |
向下移动一个屏幕行 |
k |
向上移动一个实际行 |
gk |
向上移动一个屏幕行 |
0 |
移动到实际行的行首 |
g0 |
移动到屏幕行的行首 |
^ |
移动到实际行的第一个非空白字符 |
g^ |
移动到屏幕行的第一个非空白字符 |
$ |
移动到实际行的行尾 |
g$ |
移动到屏幕行的行尾 |
基于单词移动
命令 | 动作 |
---|---|
w |
正向移动到下一单词开头 |
b |
正向移动到当前单词/上一单词开头 |
e |
正向移动到当前单词/下一单词的结尾 |
ge |
正向移动到上一单词结尾 |
理解单词和字符串
我们经常提到单词,但迄今为止,我们都一直未定义过究竟什么是一个单词。Vim 对此有两种不同的定义,并且分别用“单词”(word)和“字串”(WORD)对其进行区分。我们之前遇到过的每个面向单词的动作命令,都有一个面向字串的命令与其对应,这当中包括 W 、B 、E 和 gE 。
一个单词由字母、数字、下画线,或其他非空白字符的序列组成,单词间以空白字符分隔(参见 :h word )。字串的定义则更简单,它由非空白字符序列组成,字串间以空白字符分隔
对字符进行查找
命令 | 用途 |
---|---|
f{char} |
正向移动到下一个{char}所在处 |
F{char} |
反向移动到下一个{char}所在处 |
t{char} |
正向移动到下一个{char}所在处所在的前一个字符上 |
T{char} |
反向移动到下一个{char}所在处所在的后一个字符上 |
; |
重复上次的字符查找命令 |
, |
反转方向查找上次的字符查找命令 |
通过查找进行移动
可以通过/{char}
去跳转到其他行的字符,比如:
1 | search for your target |
可以直接使用d/ge
完成
用精确的文本对象选择选区
文本对象允许操作括号、被引用的文本、XML标签以及其他文本中的常见结构。
Vim 的文本对象由两个字符组成,第一个字符永远是i
或是a
。一般以i
开头的文本对象会选择分隔符内部的文本,而以a
开头的文本对象会选择包括分隔符在内的整个文本。为了便于记忆,可以把i
想成“inside”,而把a
想成“around”或“all”。
文本对象 | 区域 | 文本对象 | 区域 |
---|---|---|---|
a)或ab |
一对圆括号 | i)或ib |
圆括号内部 |
a}或aB |
- | i}或iB |
- |
a] |
- | i] |
- |
a> |
- | i] |
- |
a' |
- | i] |
- |
a |
- | i |
- |
a |
- | i |
- |
at |
- | it |
- |
用文本对象执行操作
文本对象自身并不是动作命令,不能用它们在文档中移动。但是却可以在可视模式及操作符待决模式中使用文本对象。记住:每当在命令语法里看到{motion}
时,也可以在这个地方使用文本对象,常见的例子包括d{motion} 、c{motion} 和 y{motion}
,更多命令看第二章表。
删除周边,修改内部
范围文本对象
文本对象 | 选择范围 |
---|---|
iw | 当前单词 |
aw | 当前单词及一个空格 |
iW | 当前字符串 |
aW | 当当前字符串及一个空格 |
is | 当前句子 |
as | 当前句子及一个空格 |
ip | 当前段落 |
ap | 当前段落及一个空行 |
上面这些命令对中文的支持都非常不好,因为vim评判单词,字符串,等的标准是英文的符号和空格
一般来说,d{motion}
命令和aw 、as 和 ap
配合起来使用比较好,而c{motion}
命令和iw
及类似的文本对象一起用效果会更好。
设置位置标记,以便快速跳回
Vim 的位置标记允许我们快速跳转到文档中感兴趣的地方。可以手动设置位置标记,不过 Vim 也会自动记录某些感兴趣的位置点。
m{a-zA-z}
命令会用选定的字母标记当前光标所在的位置。小写位置标记只在每个缓冲区局部可见,大写标记则全局可见。
普通模式提供了两个命令来跳转到一个位置标记上:'{mark}
命令跳到位置标记所在行,`{mark}命令则把光标移动到设置此位置标记时光标所在之处。
自动位置标记
位置标记 | 跳转到 |
---|---|
`` | 当前文件中上次跳转动作之前的位置 |
`. | 上次修改的地方 |
`^ | 上次插入的地方 |
`[ | 上次修改或复制的起始位置 |
`] | 上次修改或复制的结束位置 |
`< | 上次高亮选区的起始位置 |
`> | 上次高亮选区的结束位置 |
在匹配括号间跳转
%
命令允许在一组开、闭括号间跳转,它可作用于()、{}以及[]
。
如果想把cities = %w{London Berlin New\ York}
改为cities = [London Berlin New\ York]
,可以这么做,dt{
-> %
-> r]
-> ``
-> r[
在文件间跳转
本章在编译器中无法使用,故不过多介绍
复制与粘贴
用无名寄存器实现删除、复制与粘贴操作
通常情况下,在讨论剪切、复制与粘贴这3组操作时,指的都是操作系统剪贴板。不过,在Vim的术语里,我们操作的是寄存器,而并非剪贴板。
调换字符
在想调换的一个字符上使用xp
命令,x
可以将第一个字符剪切下来,并放到无名寄存器中,p
命令可以把无名寄存器中的内容粘贴到光标后面。
调换文本行
ddp
可以调换文本行,原理同上
创建文本行的副本
yyp
可以实现
深入理解Vim寄存器
Vim不使用单一的剪贴板进行剪切、复制与粘贴操作,而是为这些操作提供了多组寄存器。当使用删除、复制与粘贴命令时,可以明确指定它们中的某一个进行操作。
引用一个寄存器
Vim的删除、复制与粘贴命令都会用到众多寄存器中的某一个。可以通过给命令加"{register}
前缀的方法指定要用的寄存器。若不指名,Vim将默认使用无名寄存器。
Vim术语对照表
剪切(cut)、复制(copy)与粘贴(paste)这些都是众所周知的术语,而且大多数桌面软件和操作系统都支持这3类操作。Vim当然也提供这些功能,只不过使用的是另外的术语delete、yank与put 。
Vim的put命令与粘贴操作完全相同。幸运的是,两词均以字母p开头,因此即使术语不同,也不会影响记忆。
Vim的yank命令也等同于复制操作。但由于历史原因,当时c命令已经被用于修改(change)操作了,因此Vi的作者们被迫选择了另一个名字yank。由于那时y键还可用,因此它就成了复制操作的命令。
Vim的delete命令也与标准剪切操作的作用一致。也就是说,该命令会先把指定文本复制到寄存器后再从文档中删掉。能够理解这一点,是避开类似 糟糕!我弄丢了复制内容 所遇到的常见陷阱的关键。
你也许好奇,Vim中真正删除文本的操作是什么。也就是说,我们怎样才能删除文本而不把其内容复制到任何寄存器?答案是使用名为“黑洞”的特殊寄存器,顾名思义,放到这里的文本真的是有去无回了。用下画线符号可以引用黑洞寄存器。因此,"_d{motion}
会执行真正的删除操作。
无名寄存器(“”)
""
可以指定使用无名寄存器,例如""p
完全等于p
。
复制专用寄存器(“0)
当使用y{motion}
命令时,要复制的文本也会被拷贝到复制专用寄存器中,可用0引用,它不会被x,s,c,d
覆盖
有名寄存器(“a - “z)
遇到将一段或多段文本粘贴到多处的情况,有名寄存器就会大显神通。
黑洞寄存器
当想彻底删除的时候可以使用"_
来引用黑洞寄存器,这时不会覆盖掉无名寄存器的内容。
系统剪切板("+
)与选择专用寄存器("*
)
如果想要把vim外部的内容粘贴到vim内,或者想将vim中的内容粘贴到外部可以使用
表达式寄存器(“=)
Vim的寄存器通常被认为是保存一段文本的容器。然而,通过=
号引用的表达式寄存器却是个例外。当从表达式寄存器获取内容时,Vim将跳到命令行模式,并显示提示符“=”。这时,可以输入一段Vim脚本表达式并按
其他寄存器
寄存器 | 内容 |
---|---|
“% | 当前文件名 |
“# | 轮换文件名 |
“. | 上次插入的文本 |
“: | 上次执行的Ex命令 |
“/ | 上次查找的模式 |
用寄存器中的内容替换高亮选区的文本
选区之后使用p会将被替代的内容置入无名寄存器。
交换两个词
de
删除单词mm
标记位置ww
移动到被置换的单词ve
选中单词p
替换`m
返回之前的单词位置- ·
p
将前面的单词置换
把寄存器的内容粘贴出来
普通模式下的粘贴命令,根据要插入文本的性质不同,执行结果也不同。确定要粘贴的文本区域是面向行的还是面向字符的,将有助于制定不同的策略。
粘贴面向字符的区域
p
在光标后粘贴P
在光标前粘贴<C-r>
在插入模式下粘贴
粘贴面向行的区域
当要粘贴的内容来自于面向行的寄存器时,p
会在当前行下方粘贴,·P
在当前行上方粘贴。
与系统剪贴板进行交互
可能会带来许多问题,鉴于我们不是在vim中进行操作,所以建议直接使用系统粘贴快捷键。
### 宏 #### 宏的读取执行
把命令序列录制成宏
q
可以开启录制,也可以控制进行停止录制。
qa
将宏录制到寄存器a- 一系列操作
q
结束录制
可以通过:reg a
查询a中的内容
通过执行宏来回放命令序列
通过@a
可以重新执行之前的宏
规范光标位置、直达目标以及中止宏
黄金法则:在录制一个宏时,要确保每条命令都可被重复执行。
规范光标位置
应该把光标移到下一处查找匹配项(n
),或者当前行的行首(0
),又或是当前文件的首行(gg
)。如果每次总是从确定的位置开始执行的话,那么命中正确的目标会变得更容易。
用可重复的动作命令直达目标
面向单词的动作命令,如 w 、b 、e 和 ge ,与面向字符的动作命令 h 和 l 相比,更具灵活性。如果录制“动作命令 0 ,后跟 e ”,当每次执行该宏时,我们都能预料得到一致的结果,光标会移到当前行第一个词的最后一个字符上。只要该行包含至少一个词,无论该词包含多少个字符,都能够到达目标。
我推荐用查找命令定位,或者用文本对象。总之,请用好Vim提供的所有动作命令,尽量使你的宏兼具灵活性与可重复性。还有一点别忘了,在录制宏的过程中,禁止使用鼠标。
当动作命令失败时,宏将中止执行
Vim的动作命令可能会执行失败。例如,如果光标位于文件的首行,运行 k 命令将什么也不会发生。若光标位于文件的末行,按下 j 也会出现同样的情况。
如果宏执行动作命令失败了,Vim将中止执行宏的其余命令。
加次数回放宏
对于重复次数不多的工作,点范式是一种高效的编辑策略,但它不能指定执行的次数。为了克服该限制,可以录制一个廉价的、一次性的宏,然后再加次数进行回放。
在连续的文本行上重复修改
对于多行范围内的重复性改动,可以先录制一个宏,然后再在每一行上回放,这将会极大减轻我们的工作量。该功能可用串行或者并行两种执行宏的方式实现。
例如把下面的文本
1 | 1. one |
变成
1 | 1) One |
步骤如下:
qa
将宏录制到寄存器a0f.
这样可以保证无论从哪行开始都可以准确的移动到第一个.上,增强复用性r)w~
将.换成)移动到首单词首字母并将其转换为大写j
移动到下一行,这样多次执行宏就可以一直向下一行进行q
结束录制
以串行方式执行宏
宏录制好了以后我们使用3@a
就可以完成任务了,但是如果文件中包含注释就会带来新的问题
1 | 1. one |
我们可以用并行的方式解决这个问题
以并行方式执行宏
之前说过一种在连续文本上执行.
命令的方法,此处也可以使用相同的技术。
qa
开始录制0f.r)w~
q
jVG
选中要执行宏的文本:normal @a
执行宏
给宏追加命令
如果发现录制完宏后漏了步骤可以使用qA
来在上次录制后追加命令
在一组文件中执行宏
略
用迭代求值的方式给列表编号
如果宏在每次执行时都能插入一个可变的数值,这将会很有用处。在本节中,将学习一种技术,它会在录制宏时使某个数字递增,这样一来,就可以在连续的文本上插入数字1到5。
使用let i = 1
可以创建一个名为i的变量,如果想把它插入到文档只需在插入模式下运行<C-r>=i<CR>
,例如想把下面的文档
1 | artridge in a pear tree |
转换为
1 | 1) partridge in a pear tree |
可以按照如下步骤进行:
:let i = 1
定义变量iqa
开始记录宏i<C-r>=i<CR>) <C-[>
插入变量,添加文本:let i += 1<CR>q
增加变量值,结束录制VG:normal@a
执行宏
编辑宏的内容
假设我们将宏存在了寄存器a中,我们可以使用:put a
或者ap
命令来将其粘贴到文档中,然后就可以像编辑文档一样编辑宏了,编辑结束后将宏放回寄存器最简单的方法就是"add
但是这样会导致宏多一个^j
,所以最安全的方法还是将宏复制进寄存器后删除。