Vim 实用小技巧系列——操作符 + 动作命令,让「操作」秀起来

简介

我们都知道,.命令是vim中最强大的命令之一,今天我们再来认识一个vim中很酷炫的技巧 ——「操作」。

没错,你没有听错,就是「操作」,能秀起来的那种,就像王者荣耀里秀的飞起的「上官婉儿」一样。在vim中,如果「操作」运用得当的话,是绝对能够秀飞天的。

何为「操作」呢?vim中的「操作」由两部分组成:操作符 + 动作命令

接下来,就跟我一起走进「操作」的世界,一起「秀」起来吧~

技能介绍

操作的组成

上面提到了,操作由操作符和动作命令组成。比如d{motion}操作可以对一个字符(dl)、一个完整单词(daw)或一整个段落(dap)进行删除操作,它作用的范围由motion部分的动作命令决定。

那么常见的操作符有哪些呢?

看这里:

命令 用途
c 修改
d 删除
y 复制到寄存器
g~ 反转大小写
gu 转换为小写
gU 转换为大写
> 增加缩进
< 减小缩进
= 自动缩进
! 使用外部程序过滤{motion}所跨越的行

这里就是我整理出来的最常见的操作符,后续的训练部分我们会用到这里边的大部分操作符。大家可以多看几遍,后面如果忘记了,也可以返回来查一下。

温馨提示:本教程的灵感及部分案例参考自《Vim 实用技巧(第二版)》,建议感兴趣的同学可以去学习一下,绝对是经典必读系列。

操作符待决模式

不知道大家考虑过没有,操作符和我们用到的xia这些普通的编辑命令有什么区别呢?

xia这些命令执行完后,会立即改变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,并且仅需要两步即可完成:

GtsZsL4enL.gif!large

修改

如果我们需要把变量x换一个名字,应该怎么处理呢?

变更前内容 变更后内容
private $x; private $age;

如果按普通操作,我们会先将光标移动到x后面,键入i进入插入模式,然后进行编辑:

kRUvnFAcZp.gif!large

或者将光标移动至x上,键入ax后进入插入模式,然后进行编辑:

b624EOx2bG.gif!large

两个操作差距不大,方式二比方式一少了一次光标移动,不过都需要先进入插入模式,然后进行编辑。

现在让我们来看看换成「操作」实现的话,效果是怎样的:

rsVyylaKpL.gif!large

发现区别了没有?是不是光标一闪,x就被删除了,而且进入了插入模式。

这是怎么实现的呢?

这里我们先键入cl,修改当前字符,并进入插入模式,然后进行编辑。cl就是一个操作,c是操作符,而l就是动作命令,即选中光标所在的字符。

相比较前两种方式,用「操作」完成看上去省了移动光标位置和删除原字符的动作,好像是快了一点点?别着急,这只是开胃小菜,好戏还在后头。

删除

如果我们仅是删除某个字符而不做修改操作的话,可以使用x命令进行删除。

如果使用「操作」实现的话,可以使用dl进行删除。看上去dlx还多了一次按键操作,但是我还是建议你多练习用d{motion}的方式执行删除操作,这对后面使用复杂的删除操作还是有帮助的。

TgKQq91eVU.gif!large

大小写转换

如果我们想把变量x转换成大写X,应该如何操作呢?

当然,我们可以通过替换或者最普通的方式进行处理,同上面修改案例:

lCmLdbliPX.gif!large

不过,你知道吗,vim中还可以通过~命令将光标所在的字符进行大小写反转。所以,对于此类场景,使用~实现就简单多了,我们无需关心转换成大写还是小写,闭眼操作就可以了:

6NMZxtB77x.gif!large

如果我们使用gU操作符(这是一个组合操作符)来实现的话,可以使用gUl进行转换:

lAbaOfwMc3.gif!large

好了,字符相关的「操作」我们就介绍这么多。常用的操作符我们都用到了,除了y这个操作符——我实在想不到什么场景会去复制一个字符,而不是直接手动输入。

是不是还没有「秀起来」的感觉?莫慌,现在只是「小试牛刀」阶段,记住我们的「连招」,马上进入到第二阶段。

单词操作

如果说字符操作在工作中操作频率不高的话,那「单词操作」绝对要算得上高频操作了。

首先我们需要搞清楚vim中「单词」的概念。什么是单词呢?

其实,当我们提到「单词」的时候,还需要提到一个概念,那就是「字串」,两者有什么区别呢?

单词(word):由字母、数字、下划线组成,或其他非空白字符的序列组成,单词间以空白字符(空格,制表符或者换行符)分隔
字串(WORD):由非空白字符序列组成,字串间以空白字符分隔

这样说可能还不够直观,让我们通过一组例子来感受一下:

命令 选中前 选中后
viw OPMDqYPqtI.png!large 7c4XiuHlQZ.png!large
viW OPMDqYPqtI.png!large uCjxSXe0ai.png!large
viw 5tG0tpw4ek.png!large mjGpAznBO2.png!large
viW 5tG0tpw4ek.png!large J4NgNKYuBV.png!large
viw Ix9rmtEiAk.png!large iyXf45aAiX.png!large
viW Ix9rmtEiAk.png!large MrkKBUPooC.png!large
viw S2qHmyEaD1.png!large 4nRT28aJAj.png!large
viW S2qHmyEaD1.png!large 4nRT28aJAj.png!large

这里我们使用了i这个选择范围的命令,类似的命令还有a,两者的不同在于:

i(inside):选中边界内部的内容,不包含边界
a(around):选中边界内部的内容,包含边界

通过这一系列对比,大家对「单词」和「字串」的区别有所了解了吗?没错,单词更倾向于选中符合我阅读习惯的词组,比如在$name中,name被界定为单词,而不包含特殊字符$,这在我们修改变量名称(ciw)的时候非常方便。

那「字串」就没有用武之地了吗?非也。

让我们来看下面的例子:

e.g. we're going too slow.

当我们使用w或者b在「单词」上进行移动的时候,碰上这种句式结构简直要费死劲:

AkEHQJq6Jl.gif!large

而如果我们以W或者B在「字串」上进行移动,则要快捷许多:

Nett46kWwl.gif!large

这些特殊的应用场景,大家只有多加练习,形成肌肉记忆,才能应用自如。

到指定字符操作

我们在开发中还会经常遇到这样的场景,比如:

变更前内容 变更后内容
public function foo(int $a, int $b){ public function foo(int $a){

从变更描述上我们不难看出,这种属于从某个「起始位置」到「截止位置」的编辑操作。这种情况我们也可以借助「操作」完成。只不过这里的动作命令motion是通过查找命令t或者f实现的,当然还包括反方向的命令TF。几个命令的区别如下:

命令 用途
f{char} 正向移动到下一个 {char} 所在之处
F{char} 反向移动到上一个 {char} 所在之处
t{char} 正向移动到下一个 {char} 所在之处的前一个字符上
T{char} 反向移动到上一个 {char} 所在之处的后一个字符上

说明: ft的区别在于,f选中到指定的字符上,而t选中到指定字符的前一个字符上。这两个命令具体应该怎么用呢?让我们通过具体操作来感受一下。

mH2LxJX6N6.gif!large

这里我们进行了两步操作(前提是光标已位于操作行):

  • 使用f命令将光标移动到$a后面的,位置
  • 使用dt)命令删除从,位置(包含,)到)(不包含))之间的内容

感受到差别了吗?没错,我们一般先使用f命令查找操作的起始位置,然后使用{操作符}t{char}来执行命令。如果对这个动作对组成不好记忆的话,可以使用以下方法进行记忆:

dt{char}:删除(d)从当前光标位置(包含当前光标位置)到(t)目标字符char(不包含char)所在位置之间的内容。操作符或者目标字符可自由组合。

这个操作算是vim中使用频率较高的一个操作了,大家可以多加练习,形成肌肉记忆,收益绝对倍增。

前期如果对操作的范围不敏感的话,也可以拆成两步进行练习:

  • 使用vt{char}选中目标范围
  • 使用操作符(dyc等)进行操作

这样练习的目的是为了让我么对选中的范围更直观,练习的多了,感觉自然就有了。

行操作

在日常工作中,整行编辑操作也是不少见的。比如我们要做以下的变更:

变更前内容 变更后内容
private $name; private $name;
private $age;

看看我是怎么操作的:

hLsZCpzzQ8.gif!large

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多行选中 + 操作符」的作用就发挥出来了:

5iV6TTBYAq.gif!large

同样,删除修改也是同样的操作,大家可以自由发挥。

边界操作

在前面讲单词操作的时候,其实我们已经提到「边界操作」了。像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>

当我们掌握了这个技巧以后,相信在很多场景下都可以秀起来了。比如下面这个场景,当我们对一个方法写的不满意,想删除方法内容重新写的时候,一个「连招」就够了:

TqCE91x6Rv.gif!large

是不是有点意思了。实际工作中,那些看似天花乱坠的操作,实际都是一点一点积累出来的经验。所以,当你学会连招以后,下一步就该找秀连招的地方了。

温馨提示:需要注意的是,边界操作遵循「就近原则」。所以,在使用的时候,需要将光标置于合适的位置。

段落操作

除了单词操作外,vim还支持句子(s)和段落(p)的选中操作。句子是以句号(.)作为分隔标识,而段落是以空白行作为分隔标识。

句子操作在实际写代码的过程中用的不多,除非像注释中会有句子的形式存在,代码中见的比较少。

而段落在我们删除整个方法的时候,看上去有一些用处:

JGcYyYLWDb.gif!large

但是,真的可以一直这么用吗?再来看看下面这种情况:

W8Gptibz4x.gif!large

看到没,当方法中出现空白行分隔的时候,就不这么好用了。

全文操作

vim中如果想删除或者修改全文的话,也是很简单的。我们有以下几种实现方式

方式一

RWDFujU0n9.gif!large

  • gg命令回到首行位置
  • v进入字符选中模式或者V进入行选中模式
  • G选中全文
  • c或者d删除全文操作

方式二

针对方式一,我们还可以再进行优化:

t9rWYcRJxp.gif!large

  • gg命令回到首行位置
  • dvG或者cvG进行删除或者修改全文

其实,与方式一相比,方式二就是将可视化选中的过程直接放到了操作的「动作命令」部分实现,操作更简捷了。

方式三

因为是全文删除,不需要计算位置,所以,我们还可以通过命令行模式实现:

  • :% d搞定

0NZfUS9jVc.gif!large

如果是跨多行的删除或者修改操作,我们也可以采用类似方式实现,比如我们要删除除第一行以外的所有行:

方式一

通过操作的方式实现:

a7fLPJGdCf.gif!large

  • 2gg跳转至目标行位置
  • dvG删除第二行至末行之间的内容

方式二

命令行模式实现,一步搞定~

GOiNGjDxHN.gif!large

  • :1, $ delete

总结

怎么样,通过这一系列的「操作」,你的「婉儿」飞起来了吗?

跟玩游戏一样,要想人前秀的飞起,背后肯定要付出无数次的努力。只有不断练习,形成肌肉记忆,再加上多思考,才能嘎嘎乱杀,秀翻全场。

感谢大家的持续关注~

本作品采用《CC 协议》,转载必须注明作者和本文链接
你应该了解真相,真相会让你自由。
本帖由系统于 9个月前 自动加精
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 5
laravel_peng

Nice :kissing_heart:

10个月前 评论

看来得擦擦我的hhkb了,vim大法好

9个月前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
文章
40
粉丝
117
喜欢
699
收藏
751
排名:255
访问:3.8 万
私信
所有博文
社区赞助商