你好,我是葛俊。今天,我来和你聊聊VIM的使用技巧。

在“特别放送 | 每个开发人员都应该学一些VIM”这篇文章中,我和你详细介绍了VIM提高研发效能背后的原因。我推荐每个开发者都应该学一些VIM的原因,主要有两个:

VIM确实可以帮助我们提高效率,但面对这样一个学习曲线长而且陡的编辑器,我们很容易因为上手太难而放弃。所以,如何性价比高地学习VIM的使用技巧非常重要。

我推荐你按照以下三步,来高效地学习如何使用VIM:

  1. 学习VIM的命令模式和命令组合方式;
  2. 学习VIM最常用的命令;
  3. 在自己的工作环境中使用VIM,比如与命令行环境的集成使用。

接下来,我们分别看看这三步吧。

VIM的模式机制

VIM的基本模式是命令模式,在命令模式中,敲击主体键的效果不是直接插入字符,而是执行命令实现对文本的修改。

使用VIM的最佳工作流

在我看来,在命令模式下工作,效率高、按键少,所以我推荐你尽量让VIM处于命令模式,使用各种命令进行工作。进入编辑模式完成编辑工作之后也立即返回命令模式。

事实上,我们从命令模式进入编辑模式修改文件,之后再返回命令模式的全过程,就是一个编辑命令。它跟其他的命令,比如使用dd删除一行,并没有本质区别。接下来,我们一起看个具体的例子吧。

比如,我要在一行文字后面加上一个大括号,后面再写一行代码。开始编辑时,光标处于这一行的开头。

  config = 
^ 

修改的目标是这样:

  config = {
    timeout: 1000ms,
  }

在VIM中,我的操作是,首先敲击大写字母A,将光标移到这一行的末尾,并进入编辑模式:

 config = 
          ^

然后,输入“{ timeout: 1000ms,回车}”,在文件中插入内容。最后,敲击Esc键回到命令模式。编辑完成。

  config = {
    timeout: 1000ms,
  }
   ^

实际上,整个过程就是执行了一条“在本行末尾插入文字”的命令。整条命令的输入是“A{ timeout: 1000ms,回车}Esc”。虽然比较长,但仍然是一条文本编辑命令。

所以实际上,我们在VIM中的工作,正是在命令模式里执行一条条的命令完成的。理解了这一点,你就可以有意识地学习、设计命令来高效地完成工作了。

为了让命令更加高效,VIM还提供了强大的命令组合功能,使得命令的功能效果呈指数级增长。

命令的组合方式

在VIM中,有相当一部分命令可以扩展为3部分:

比如,命令3de中,3表示执行3次,d是删除命令,e表示从当前位置到单词的末尾。整条命令的意思就是,从当前位置向后,删除3个单词。类似的,命令3ce表示从当前位置向后,删除三个单词,然后进入编辑模式。

可以看到,命令组合的前两个部分比较简单,但第三个部分也就是命令对象,技巧就比较多了。所以接下来,我就与你详细介绍下到底有哪些命令对象可以使用。

其实,对命令对象并没有一个完整的分类。但我根据经验,将其总结为光标移动命令和文本对象两种。

第一种是光标移动命令。比如,$命令是移动光标到本行末尾,那么d$就表示删除到本行末尾;再比如,4}表示向下移动4个由空行隔开的段落,那么d4}就是删除这4个段落。

这一类命令功能很强大,我们也很熟悉了,但它有一个缺陷,就是选择的出发点始终是当前光标所在位置。而我们在处理文本的过程中,尤其是在编写程序的时候,光标所在位置常常是处于需要修改内容的中间,而不是开头。比如,我们常常在写一个注释字符串的时候,需要修改整个字符串:

    comment: 'this is standalone mode',
                          ^

如果使用上面的光标移动作为命令对象的话,我们需要执行bbbct’命令,也就是向左移动三个单词之后,再向右删除到第一个单引号的地方,操作起来很麻烦。

针对这种情况,VIM提供了第二种命令对象:文本对象。具体来说就是,用字符代表一定的文字单位。比如,i"代表两个双引号之间的字符串,aw表示当前的单词。使用这种文本对象很方便,比如上面的编辑例子,我们只需要命令ci"就可以实现,比bbbct’命令方便了很多。

具体来说,文本对象命令由两部分组成:

如果你要查看完整的文本对象,可以使用:help text-objects。

这一组文本对象选择功能很强大,而且是VIM自带功能,不需要安装任何插件。神奇的是,不知道为什么,很多使用VIM很久的人都不知道这个功能。我个人把它叫作vip功能,如果你以前没有用过,可以在文件中输入命令vip看看效果。相信不会让你失望。

可见,VIM的命令操作及其组合功能非常强大,要高效使用VIM,我们就必须使用命令模式以及命令组合。我看到有些开发者使用VIM时,一上来就使用i命令进入编辑模式,然后在编辑模式中工作。但是,编辑模式的功能很有限,完全发挥不出VIM提供的高效文本编辑功能。

接下来,我们进入第二步,也就是学习VIM最常用的命令。

命令模式中的基础命令

VIM有大量的命令可供我们使用,这里我主要与你介绍针对单个文件编辑的命令,因为这是编辑工作最基础的部分,也是各种跨平台场景,尤其在其他IDE中通过插件使用VIM的方式的共性部分。

至于更多的命令和细节,你可以参考我的“命令模式中的基础命令”这篇博客,或者自行搜索查询。

接下来,我会按照以下编辑文件的逻辑顺序,来与你介绍关键知识点和技巧:

  1. 打开文件,以及进行设置操作;
  2. 移动光标;
  3. 编辑文本;
  4. 查找、替换。

第一组常用命令是,打开文件以及对文件的设置

这些命令包括打开文件、退出、保存、设置等。关于设置,如果在远端的服务器上,我常常运行一条命令进行基本设置:

:set ic hls is hid nu

其中,ic表示搜索时忽略大小写,hls表示高亮显示搜索结果,is表示增量搜索,也就是在搜索输入的过程中,在按回车键之前就实时显示当前的匹配结果,hid表示让VIM支持多文件操作,nu表示显示行号。

如果要关闭其中某一个选项的话,在前面添加一个no即可。比如,关闭显示行号,使用

:set nonu

第二组常用命令是,移动光标

VIM提供了非常细粒度的光标移动命令,包括水平移动、上下移动、文字段落的移动。这些命令之间的差别很细微。比如,w和e都是向右边移动一个单词,只不过w是把光标放到下一个单词开头,而e是把光标放到这一个单词结尾。

虽然差别很小,但正是这样的细粒度,才使得VIM能够让我们使用最少的按键次数去完成编辑任务。

第三组常用命令是,编辑命令

编辑工作中我们应该大量使用命令组合来提高效率。关于组合命令的威力,我建议你看下“命令模式中的基础命令”那篇博客,我与你详细解释了一个ct"的例子。

这里,我重点与你分享一个设计命令的技巧。VIM的取消命令u、重复命令.,都是针对上一个完整的编辑命令而言。所以,我们可以设计一个完整的编辑命令,从而可以使用重复命令.来提高效率。比如,你要把一段文本中的几个时间都修改成20ms,这段文本原来是

    timeout: 1000000ms,
    waiting: 300000ms,
    starting: 40000ms,

希望改变成

    timeout: 20ms,
    waiting: 20ms,
    starting: 20ms,

在VIM中有多种实现方法。一种还不错的方法是,使用修改命令c和移动命令l的组合,在每一行用一个命令搞定。具体操作是,将光标挪到1000000ms里数字1的地方,用c7l20命令对第一行进行修改,然后在第二行、第三行的相同位置分别使用c6l20和c5l20进行修改。

这个命令可以完成工作,但每条命令都不一样,不能重复。实际上,还有一种更好的方法。

你可以设计一条命令,在每一行重复使用。在这个例子里,这个命令是ctm20,意思是从当前位置删除到下一个字母m的位置,进入编辑模式,插入20,然后返回命令模式。

使用这条命令对第一行进行修改之后,你可以把光标挪到第二、三行使用重复命令.来完成编辑任务,如下所示。

当然了,你还可以使用正则表达式进行查找替换,但是远比上面这个重复命令要复杂。这个设计命令并重复使用的方法非常好用,建议你上手实践下。

关于重复命令,VIM另外还有一个录制命令q和重复命令@,功能更强大,但也比较重。如果你想深入了解的话,可以参考这篇文章

第四组常用命令是,查找、替换

这组命令主要有/、*和s等,很常用,网络上也有很多描述,这里我就不再详细介绍了。比如,我在“命令模式中的基础命令”这篇博客中详细介绍了s命令,可供你参考。

除了上述4种常见命令外,VIM中还有一种很强大的可视模式(Visual Mode),可以提高编辑体验。接下来,我们再一起看看。

可视模式

可视模式一共有3种,包括基于字符的可视模式、基于行的可视模式和基于列的可视模式,详见“命令模式中的基础命令”这篇博客中的介绍。

接下来,我以基于列的可视模式为例,带你看看可视模式是如何提高编辑体验的吧。

我们先来看看第一个例子。有一段文本,每一行都用var声明变量(比如var gameMode),现在我希望改成使用const来声明。我们来看看具体的实现方式。

第一步,把光标挪到第一行开头的 v 字符处,使用组合键Ctrl-v进入列可视模式,然后使用7j下移动七行,再用e向右移动一个单词,从而选中一个包含所有var的矩形区域。

第二步是开始编辑。输入c,删除所有的var,并进入编辑模式。

第三步是输入const,并输入Esc完成编辑操作,回到命令模式,整个修改完成。

我们再来看看第二个例子。我希望在上面这段文字的每一行末尾都添加一个分号。具体的操作步骤如下所示:

首先,用$命令把光标挪到最后一行的末尾,然后Ctrl-v进入列模式,再用7j命令将光标挪到第一行,这时,每一行的末尾都被包含到了一个方块里。不过因为每一行的长度不同,所以方块没有显示完全。

然后,输入A命令,光标挪到每一行的末尾,并进入编辑模式。

最后,输入要插入的分号,输入Esc完成编辑。这样,每一行的末尾都添加了一个分号。

最后,关于可视模式我还有三个比较有用的小贴士:

以上就是命令模式中的常用基础命令了。理解了这些命令,再加上命令组合,你就可以比较高效地使用VIM进行文本编辑了。但,要更高效地使用VIM,我们还有一个问题没有解决:VIM虽然强大,但也只是整个工作环境中的一个工具,怎样才能在整个工作中高效地使用VIM技能呢?

所以,接下来我将带你探索如何寻找合适的VIM使用场景。

寻找合适的VIM使用场景

寻找合适的适用场景,常常包括两种情况,一是VIM不是主力IDE的情况,二是VIM与其他工具的集成。接下来,我们先看第一种情况。

我的工作环境中,VIM不是主力IDE怎么办?

这种情况下,我会建议你先看一下你的主力IDE是否支持VI模式。目前,绝大部分主流IDE都支持,而且大都做得不错。如果你的主力IDE中能使用VIM命令的话,那从VIM特有的命令模式就能让你收益颇丰。

从我的经验来看,我只有在Facebook那几年使用VIM作为主力IDE,其他时间使用的编辑器主要是Intellij的IDE系列和VS Code。在这些IDE里,我一直在使用VI模式,效果也都很好。

除了在主力IDE中使用VI模式外,你还可以选择把VIM只作为一个单纯的文本编辑器,需要强大的编辑功能时,临时用一下就可以。比如,我在写微信小程序的时候,一开始使用的是原生的微信开发工具,编辑功能不是太强。所以在遇到一些重量级的编辑工作时,我就打开VIM快速搞定,然后再回到微信原生开发工具IDE里继续工作。

VIM能在Linux命令行环境中与其他工具结合的很好

Unix/Linux系统的一个设计理念是,每个工具都只做一个功能,并且把这个功能做到极致,然后由操作系统把这些功能集成起来。VI当初就是作为Unix系统里的编辑器而存在的,到了40年后的今天,虽然VIM已经可以被配置成为一个强大的IDE,但很大程度上依然是Unix/Linux系统的基础编辑器,仍然可以很方便地和Unix/Linux的其他工具集成。

为了帮助你理解,我再和你分享3个适用场景。

第一个场景:使用VIM作为其他工具的编辑器。

在大部分Linux系统里,默认的编辑器就是VIM。如果不是的话,可以使用如下命令来设置:

// 全局使用VIM
export EDITOR='vim'

第二个适用场景:使用管道(Pipe)。

管道是Linux环境中最常用的工具之间的集成方式。VIM可以接收管道传过来的内容。比如,我们要查看GitHub上用户xxx的代码仓的情况,可以使用下面这条命令

curl -s https://api.github.com/users/xxx/repos | vim -

curl命令访问GitHub的API,把输出通过管道传给VIM,方便我直接在VIM中查看用户xxx的代码仓的细节。

这里需要注意的是,在VIM命令后面有一个-,表示VIM将使用管道作为输入。

第三个适用场景:在VIM中调用系统工具

除了系统调用VIM和使用管道进行集成之外,我们还可以在VIM中反过来调用系统的其他工具。这里,我与你介绍一个最实用的命令:在可视模式中使用!命令调用外部程序。

比如,我想给一个文件里的中间几行内容前加上从1000开始的数字序号。

在Linux系统里,我们可以使用nl这个命令行工具完成,具体的命令是nl -v 1000。所以,我们可以把这一部分文本单独保存为文件,使用nl处理后,再把处理结果拷贝回VIM中。

虽然可以达到目的,但过程繁琐。幸运的是,我们可以在VIM里面直接使用nl,并把处理结果插入VIM中,具体操作如下。

首先,使用命令V进入可视模式,并选择这一部分文字。

然后,输入命令!,VIM的最后一行会显示’<,’>! ,表示由外部程序操作选中的部分。这时,我们输入nl -v 1000并回车。

VIM就会把选择的文本传给nl,nl在每一行前面添加序号,再把处理结果传给VIM,从而完成了我们想要的编辑结果,也就是在所选的几行文字前面添加从1000到1006的序号。

同时,在VIM的底部,会显示7 lines filtered,意思是VIM里面的7行文本被使用外部工具处理过。是不是很方便?

我每次使用这样的功能时都觉得很爽,因为这可以让我聚焦在工作上,而不会把时间花在繁琐的操作上。

以上就是3个最常用的VIM和其他工具集成的例子。这种工具的集成工作方式,可以大大提高单个工具所能带来的效率提升,我还会在后面的文章中与你详细讨论。

总结

在今天这篇文章中,我与你介绍了高效学习VIM的三个步骤,即:第一,学习VIM的命令模式和命令组合方式;第二,学习文本编辑过程中各个环节最常用的命令;第三,在自己的工作环境中找到适用VIM的场景。

事实上,我今天与你讲述的VIM命令和使用技巧,只是最常见的跨平台使用中的共性部分,对效能提升带来的效果也最直接、最明显。

但,这些远没有覆盖VIM的强大功能。比如,我没有与你讨论多文件、多Tab、多窗口编辑的场景,以及插件的话题。如果你需要的话,可以把VIM配置成一个类似IDE的开发环境,进入沉浸式的VIM使用体验。关于插件方面的内容,我推荐3个插件:

其中,pathogen和fugitive的作者都是Tim Pope,一个VIM牛人。如果你想了解更多的插件的话,可以看看他的其他VIM插件。

不容置疑的是,VIM的学习曲线非常长,即使我已经使用了15年,仍然会时不时地学习到一些新东西。只要你愿意,就可以一直学习一直提高。

思考题

你想要分享的最炫酷的VIM技巧是什么呢?

感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

评论