You Don't Know Vim

2016-08-09 Jason Liao 更多博文 » 博客 » GitHub »

原文链接 http://jasonliao.me/posts/2016-08-09-you-dont-know-vim.html
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。


这一周看了一下 Practical Vim 这本书,作者说不要全部看,因为有很多操作你都已经知道了,这样会浪费时间。但是不看怎么知道到底知不知道了,所以还是都看了,把不知道的整理一下,记录了下来

操作那些被包含的文本

对文本的操作一般有

  • y -复制
  • d - 删除
  • c - 删除后转 insert mode
  • v - 区域选择

我们把这些操作统称为 op,下面就总结一下操作被 (){}[]''/""、HTML Tag 所包含的文本,用什么组合键,在包含文本的任意一个地方

  • ()
    • op + ib - 操作 () 内的文本
    • op + ab - 操作包含 () 在内的文本
  • {}
    • op + iB - 操作 {} 内的文本
    • op + aB - 操作包含 {} 在内的文本
  • []
    • op + i[i] - 操作 [] 内的文本
    • op + a[a] - 操作包含 [] 在内的文本
  • ''""
    • op + i'i" - 操作 '" 内的文本
    • op + a'a" - 操作包含 '" 在内的文本
  • <tagName></tagName>
    • op + it - 操作 <tagName></tagName> 内的文本
    • op + at - 操作包含 <tagName></tagName> 在内的文本

怎么快速地操作一个词,这里的一个词是指一串连续的中文或英文,或者用 _ 连接的多个词。我们可以直接在这个词中的任意位置使用 op + iw 操作整个词。换句话说,如果上述符号包括的是一个词,我们就可以直接使用 op + iw 操作符号内的文本,或者 op + aw 操作包含符号在内的文本

能一别二

很多要用两次按键完成的操作,其他都可以使用一次按键

可能大家都知道,跳到一行的最前方/最后方并变成 insert mode 可以使用 I / A,如果你还在使用 ^i / $a 就要停止了

如果你要从光标开始的地方,删除到末尾,可能会使用 c$d$,但你可以直接 CD。还在用 cc?快试试 S

使用 ;.

;. 都是用来帮助我们重复的操作,先来两个例子感受一下

var foo = 1
var bar = a
var foobar = foo + bar

怎么快速让每一行后面加个分号,首先假设我们光标在第一行。然后做如下操作

A; + Esc + j.j.,当然用 visual block 感觉更快(Ctrl + v + jj$A; + Esc)

再来看下一个例子

var hi = 'Hi! my name is '+name+', and I love '+hobby;

你有代码洁癖吗?我如果看到一个文件里的代码排版乱了,我就会 gg=G。但是这样上面那种情况并不会改变,那怎么可以快速的对类似上面的代码把 + 两边加个空格呢?那就要结合使用 ;: 了,假设我们我们光标在行首,做如下操作

f+s␣+␣ + Esc + ;.;.

第一组就是找到 +,然后删除输入空格加号空格,退回 normal mode,用 ; 重复查找工作,用 . 重复输入操作

但是如果想要把 . 的作用发挥好,就要把改变最大化,什么意思,我们看看下面这个例子

var obj = {
  name: 'jason',
  hobby: 'basketball'
};

现在假设光标在 backetball 的最后一个 l 上,我们要删除 basketball,我们有很多的方法

  • dbx - dbl 删除到词首,剩下 l,用 x 删掉
  • bdw - b 回到词首,用 dw 删掉
  • diw - 刚刚一开始说的,直接删掉一个词

都是三个按键,都可以达到同样的效果,但是 . 的作用却不一。对于第一种情况 . = x,第二种 . = dw,第三种则是 . = diw。当我们又想去删除 name 属性值的时候,使用 . 操作,第一种只能删除光标所在的字母,第二种只能删除光标所在字母到这个词结束的地方,意味着你在词首才能删除整个 jason,而最后一种,不管你光标在哪里,你都可以删掉整个词。这就是刚刚说的,要把改变做到最大化的原因

(当然向前删除我们可以先转成 insert mode,然后再 Ctrl + w 来向前删除一个单词)

搜索与替换

这个我们太常用了

:%s/search/replace/g

如果我只要换 5 ~ 12 行的 search 到的词呢

:5,12s/search/replace/g

但其实可能 5 ~ 12行 里要替换的这个词很少,打完那条命令的时间,可能我也可以同样用别的方法完成我的需要了,例如我们就在第 5 行,那个要替换的词上,然后

*cw + replace + Esc + n.n.n.

* 会 search 光标处在的那个词,相当于你用 /search?search 一样,然后删除输入要替换的词,然后退回 normal mode,n 是查找下一个,. 是重复输入操作

或者将命令和 * 结合起来

*cw + replace + Esc 然后输入命令

:5,12s//<C-r><C-w>/g

<C-r><C-w> 会把我们光标当前的词复制到命令里

<C-a><C-x>

<C-a><C-x> 我也是刚刚才发现,黑科技呀!<C-a> 可以为你光标处在的数字做加法,而 <C-x> 则做减法。如果光标所在的位置不是数字,则对光标后的第一个数字做操作,并跳到数字上。实际使用场景是什么呢?前端应用场景太多了,写 CSS 的时候常常会对一些长度单位进行操作。加 3px 那就太方便了。3 + Ctrl + a,当然在 window 下 <C-a> 会当成全选,解决方法看 这里

可以用 Ctrl + r0 来在 insert mode 完成粘贴,不用再退出 normal mode 下 p

多窗口

常常习惯多窗口一起编写代码,有些东西要对照或者参考,但屏幕小伤不起呀,可以使用 Ctrl + w| 来最大化当前窗口,用 Ctrl + w= 平均分所有窗口

寄存器

:reg

发现了什么?是不是发现很多你似曾相似的操作

你在外部 Ctrl + c 复制的东西会进到 "* 寄存器里

"" 是无名寄存器,任何没有指定寄存器的操作,都会进入这里

"0 称为抽取寄存器,是当你使用 y 复制东西的时候,被复制的就会进入到抽取寄存器中,但你使用 cdsx 操作的时候,复制的东西是永远不会进入抽取寄存器的。所以你可以使用 "0P 来确保把你上一次通用 y 复制的东西粘贴出来

有名的寄存器,就是 "a ~ "z,在操作之前加上有名的寄存器,就会把内容存到指定的寄存器里。例如 "adiw 就是把光标所在的词放在 a 寄存器里,就可以通过 "aP 把它给复制出来

了解了这些之后,就可以很好解决一个常见的场景

var collection = getCollection();
process(somethingElse, target);

我们想用 collection 替换 somethingElse,当我们用 yiwcollection 复制下来之后,去到 somethingElsedw 删掉它,再按 P 发现 somethingElse 又回来了,因为 dw 不仅仅会删除一个词,还会把它放在无名寄存器里,单纯的使用 P 就是把无名寄存器里的东西取出来。

根据我们刚刚了解的可以知道,使用 "0P 就可以把正解的东西拿出来,因为只有用 y 复制的东西才会进入 "0 寄存器。还可以不用 dw 删除这个词,用 ve 选中这个词,再 p,也是可以的,因为这时无名寄存器 "" 没有被替换。还有一种方式就是用 cw 删除这个词并转成 insert mode,然后 Ctrl + r0Ctrl + r 可以在 insert mode 下访问寄存器,所以再加上寄存器名字,就可以把寄存器里的东西取出来

寄存器不仅仅记录操作结果,还可以记录操作步骤。使用 q 加上寄存器名称,就可以开始记录操作步骤,再输入 q 就是停止记录,使用 @ 加寄存器名称,就可以重复步骤。当你有大量重复操作的时候,就可以使用啦

总结

本文只是总结了一些能够解决开发痛点的一些小技巧和我之前并不知道的操作,希望里面也有你们不知道的小操作,有更多更好的方法也可以在下面留言。Vim 一书一文怎能概括,更何况它的强大之处在于定制化,探索更多请猛戳下面这个链接,enjoy it :)