Vim 实用小技巧系列——操作符 + 动作命令,让「操作」秀起来
简介
我们都知道,.
命令是vim
中最强大的命令之一,今天我们再来认识一个vim
中很酷炫的技巧 ——「操作」。
没错,你没有听错,就是「操作」,能秀起来的那种,就像王者荣耀里秀的飞起的「上官婉儿」一样。在vim
中,如果「操作」运用得当的话,是绝对能够秀飞天的。
何为「操作」呢?vim
中的「操作」由两部分组成:操作符 + 动作命令。
接下来,就跟我一起走进「操作」的世界,一起「秀」起来吧~
技能介绍
操作的组成
上面提到了,操作由操作符和动作命令组成。比如d{motion}
操作可以对一个字符(dl
)、一个完整单词(daw
)或一整个段落(dap
)进行删除操作,它作用的范围由motion
部分的动作命令决定。
那么常见的操作符有哪些呢?
看这里:
命令 | 用途 |
---|---|
c | 修改 |
d | 删除 |
y | 复制到寄存器 |
g~ | 反转大小写 |
gu | 转换为小写 |
gU | 转换为大写 |
> | 增加缩进 |
< | 减小缩进 |
= | 自动缩进 |
! | 使用外部程序过滤{motion}所跨越的行 |
这里就是我整理出来的最常见的操作符,后续的训练部分我们会用到这里边的大部分操作符。大家可以多看几遍,后面如果忘记了,也可以返回来查一下。
温馨提示:本教程的灵感及部分案例参考自《Vim 实用技巧(第二版)》,建议感兴趣的同学可以去学习一下,绝对是经典必读系列。
操作符待决模式
不知道大家考虑过没有,操作符和我们用到的x
,i
,a
这些普通的编辑命令有什么区别呢?
像x
,i
,a
这些命令执行完后,会立即改变vim
操作的内容或状态。而当我们输入操作符以后,并不会立即出现这样的变化(在没有选中内容的前提下)。这是因为当输入操作符以后,vim
会进入一种特殊的模式状态,即「操作符待决模式」。在该模式下,vim
会继续等待用户输入动作命令,直到接收到完整的动作命令以后,才会执行具体的动作。
当然,操作符除了可以在后边跟范围选中的动作命令,也可以在选中范围以后键入操作符直接执行,或者在命令行模式下,跟在范围选择的命令参数后执行。这几种运行方式大家都可以了解下。
训练营
好了,掌握了技能特点以后,现在就让我们进入「训练营」开始连招训练吧~
训练之前,我们先来准备一个练习用的「人偶」,模样如下:
<?php
class Test {
private $x;
private $name;
public function foo(int $a, int $b){
return $a + $b;
}
}
我们知道,在文本编辑中,最基本的操作就是「增删改查」,查的部分我们会专门拿一篇来讨论,这里我们先来一起看看如何借助「操作」来快速实现「增删改」的编辑动作。
温馨提示:我们学习的思路是由简到难,紧贴实际工作场景,所以,当你做完这些操作以后,哪怕你刚接触
vim
,相信你也一样可以秀起来。
字符操作
我们先从最简单的字符操作开始。
替换
现在,假设我们需要对x
字符进行如下替换,需要怎么操作呢?
变更前内容 | 变更后内容 |
---|---|
private $x; | private $y; |
通过观察发现,只是将变量x
变成了y
,属于单字符替换。你可能想到使用r
命令进行替换:ry
,并且仅需要两步即可完成:
修改
如果我们需要把变量x
换一个名字,应该怎么处理呢?
变更前内容 | 变更后内容 |
---|---|
private $x; | private $age; |
如果按普通操作,我们会先将光标移动到x
后面,键入i
进入插入模式,然后进行编辑:
或者将光标移动至x
上,键入a
从x
后进入插入模式,然后进行编辑:
两个操作差距不大,方式二比方式一少了一次光标移动,不过都需要先进入插入模式,然后进行编辑。
现在让我们来看看换成「操作」实现的话,效果是怎样的:
发现区别了没有?是不是光标一闪,x
就被删除了,而且进入了插入模式。
这是怎么实现的呢?
这里我们先键入cl
,修改当前字符,并进入插入模式,然后进行编辑。cl
就是一个操作,c
是操作符,而l
就是动作命令,即选中光标所在的字符。
相比较前两种方式,用「操作」完成看上去省了移动光标位置和删除原字符的动作,好像是快了一点点?别着急,这只是开胃小菜,好戏还在后头。
删除
如果我们仅是删除某个字符而不做修改操作的话,可以使用x
命令进行删除。
如果使用「操作」实现的话,可以使用dl
进行删除。看上去dl
比x
还多了一次按键操作,但是我还是建议你多练习用d{motion}
的方式执行删除操作,这对后面使用复杂的删除操作还是有帮助的。
大小写转换
如果我们想把变量x
转换成大写X
,应该如何操作呢?
当然,我们可以通过替换或者最普通的方式进行处理,同上面修改案例:
不过,你知道吗,vim
中还可以通过~
命令将光标所在的字符进行大小写反转。所以,对于此类场景,使用~
实现就简单多了,我们无需关心转换成大写还是小写,闭眼操作就可以了:
如果我们使用gU
操作符(这是一个组合操作符)来实现的话,可以使用gUl
进行转换:
好了,字符相关的「操作」我们就介绍这么多。常用的操作符我们都用到了,除了y
这个操作符——我实在想不到什么场景会去复制一个字符,而不是直接手动输入。
是不是还没有「秀起来」的感觉?莫慌,现在只是「小试牛刀」阶段,记住我们的「连招」,马上进入到第二阶段。
单词操作
如果说字符操作在工作中操作频率不高的话,那「单词操作」绝对要算得上高频操作了。
首先我们需要搞清楚vim
中「单词」的概念。什么是单词呢?
其实,当我们提到「单词」的时候,还需要提到一个概念,那就是「字串」,两者有什么区别呢?
单词(word):由字母、数字、下划线组成,或其他非空白字符的序列组成,单词间以空白字符(空格,制表符或者换行符)分隔
字串(WORD):由非空白字符序列组成,字串间以空白字符分隔
这样说可能还不够直观,让我们通过一组例子来感受一下:
命令 | 选中前 | 选中后 |
---|---|---|
viw | ||
viW | ||
viw | ||
viW | ||
viw | ||
viW | ||
viw | ||
viW |
这里我们使用了i
这个选择范围的命令,类似的命令还有a
,两者的不同在于:
i(inside):选中边界内部的内容,不包含边界
a(around):选中边界内部的内容,包含边界
通过这一系列对比,大家对「单词」和「字串」的区别有所了解了吗?没错,单词更倾向于选中符合我阅读习惯的词组,比如在$name
中,name
被界定为单词,而不包含特殊字符$
,这在我们修改变量名称(ciw
)的时候非常方便。
那「字串」就没有用武之地了吗?非也。
让我们来看下面的例子:
e.g. we're going too slow.
当我们使用w
或者b
在「单词」上进行移动的时候,碰上这种句式结构简直要费死劲:
而如果我们以W
或者B
在「字串」上进行移动,则要快捷许多:
这些特殊的应用场景,大家只有多加练习,形成肌肉记忆,才能应用自如。
到指定字符操作
我们在开发中还会经常遇到这样的场景,比如:
变更前内容 | 变更后内容 |
---|---|
public function foo(int $a, int $b){ | public function foo(int $a){ |
从变更描述上我们不难看出,这种属于从某个「起始位置」到「截止位置」的编辑操作。这种情况我们也可以借助「操作」完成。只不过这里的动作命令motion
是通过查找命令t
或者f
实现的,当然还包括反方向的命令T
和F
。几个命令的区别如下:
命令 | 用途 |
---|---|
f{char} | 正向移动到下一个 {char} 所在之处 |
F{char} | 反向移动到上一个 {char} 所在之处 |
t{char} | 正向移动到下一个 {char} 所在之处的前一个字符上 |
T{char} | 反向移动到上一个 {char} 所在之处的后一个字符上 |
说明:
f
和t
的区别在于,f
选中到指定的字符上,而t
选中到指定字符的前一个字符上。这两个命令具体应该怎么用呢?让我们通过具体操作来感受一下。
这里我们进行了两步操作(前提是光标已位于操作行):
- 使用
f
命令将光标移动到$a
后面的,
位置 - 使用
dt)
命令删除从,
位置(包含,
)到)
(不包含)
)之间的内容
感受到差别了吗?没错,我们一般先使用f
命令查找操作的起始位置,然后使用{操作符}t{char}
来执行命令。如果对这个动作对组成不好记忆的话,可以使用以下方法进行记忆:
dt{char}:删除(
d
)从当前光标位置(包含当前光标位置)到(t
)目标字符char
(不包含char
)所在位置之间的内容。操作符或者目标字符可自由组合。
这个操作算是vim
中使用频率较高的一个操作了,大家可以多加练习,形成肌肉记忆,收益绝对倍增。
前期如果对操作的范围不敏感的话,也可以拆成两步进行练习:
- 使用
vt{char}
选中目标范围 - 使用操作符(
d
,y
,c
等)进行操作
这样练习的目的是为了让我么对选中的范围更直观,练习的多了,感觉自然就有了。
行操作
在日常工作中,整行编辑操作也是不少见的。比如我们要做以下的变更:
变更前内容 | 变更后内容 |
---|---|
private $name; | private $name; private $age; |
看看我是怎么操作的:
在 vim
中,行操作的命令是比较好记的,连续按下两次命令即可:
dd:删除整行并停留在普通模式
cc:删除整行并进入插入模式
yy:复制整行到寄存器
g~~:反转整行大小写
guu:整行转换成小写
gUU:整行转换成大写
温馨提示:整行转换大小写也可以通过键入两次完整命令实现,比如
g~g~
,但是我们一般很少这么操作,毕竟多了一次按键操作,没有g~~
更加丝滑。
当然,整行操作还有其他实现的方式,比如先用V
进行整行选中(包含了空白字符),然后执行操作符进行操作。看上去没有重复操作符的方式简捷,但是当我们同时操作多行的时候,这种方式的用武之地就体现出来了。
比如我们要做以下变更:
变更前内容 | 变更后内容 |
---|---|
public function foo(int $a, int $b){ return $a + $b; } |
public function foo(int $a, int $b){ return $a + $b; } public function boo(int $a, int $b){ return $a - $b; } |
这种情况下,「V
多行选中 + 操作符」的作用就发挥出来了:
同样,删除修改也是同样的操作,大家可以自由发挥。
边界操作
在前面讲单词操作的时候,其实我们已经提到「边界操作」了。像iw
或者aw
,是以单词作为选中的操作单位。同样,支持边界范围选中的命令还有很多,比如:
文本对象 | 选择区域 | 文本对象 | 选中区域 |
---|---|---|---|
a) 或 ab |
一对圆括号 (parentheses) | i) 或 ib |
圆括号内部 (parentheses) |
a} 或 aB |
一对花括号 {braces} | i} 或 iB |
花括号内部 {braces} |
a] |
一对方括号 [brackets] | i] |
方括号内部 [brackets] |
a> |
一对尖括号 <brackets> | i> |
尖括号内部 <brackets> |
a' |
一对单引号 ‘single quotes’ | i' |
单引号内部 ‘single quotes‘ |
a" |
一对双引号 “double quotes” | i" |
双引号内部 “double quotes“ |
a` | 一对反引号 `backticks` | i` | 反引号内部 `backticks` |
at |
一对 XML 标签 <xml>tags</xml> | it |
一对 XML 标签内部<xml>tags</xml> |
当我们掌握了这个技巧以后,相信在很多场景下都可以秀起来了。比如下面这个场景,当我们对一个方法写的不满意,想删除方法内容重新写的时候,一个「连招」就够了:
是不是有点意思了。实际工作中,那些看似天花乱坠的操作,实际都是一点一点积累出来的经验。所以,当你学会连招以后,下一步就该找秀连招的地方了。
温馨提示:需要注意的是,边界操作遵循「就近原则」。所以,在使用的时候,需要将光标置于合适的位置。
段落操作
除了单词操作外,vim
还支持句子(s
)和段落(p
)的选中操作。句子是以句号(.
)作为分隔标识,而段落是以空白行作为分隔标识。
句子操作在实际写代码的过程中用的不多,除非像注释中会有句子的形式存在,代码中见的比较少。
而段落在我们删除整个方法的时候,看上去有一些用处:
但是,真的可以一直这么用吗?再来看看下面这种情况:
看到没,当方法中出现空白行分隔的时候,就不这么好用了。
全文操作
vim
中如果想删除或者修改全文的话,也是很简单的。我们有以下几种实现方式
方式一
gg
命令回到首行位置v
进入字符选中模式或者V
进入行选中模式G
选中全文c
或者d
删除全文操作
方式二
针对方式一,我们还可以再进行优化:
gg
命令回到首行位置dvG
或者cvG
进行删除或者修改全文
其实,与方式一相比,方式二就是将可视化选中的过程直接放到了操作的「动作命令」部分实现,操作更简捷了。
方式三
因为是全文删除,不需要计算位置,所以,我们还可以通过命令行模式实现:
:% d
搞定
如果是跨多行的删除或者修改操作,我们也可以采用类似方式实现,比如我们要删除除第一行以外的所有行:
方式一
通过操作的方式实现:
2gg
跳转至目标行位置dvG
删除第二行至末行之间的内容
方式二
命令行模式实现,一步搞定~
:1, $ delete
总结
怎么样,通过这一系列的「操作」,你的「婉儿」飞起来了吗?
跟玩游戏一样,要想人前秀的飞起,背后肯定要付出无数次的努力。只有不断练习,形成肌肉记忆,再加上多思考,才能嘎嘎乱杀,秀翻全场。
感谢大家的持续关注~
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: