• 代码编辑器[0] -> Vim/gVim[3] -> 像编程一样使用Vim


    像编程一样使用Vim


     目录

    1. 为什么是Vim / Why Vim
    2. 从hjkl开始上路 -- 使用基本按键进行移动和编辑 / Start from <hjkl>
    3. 一次超速和翻车的体验 -- 使用命令进行全局替换 / Global Replacement
    4. 开慢一点,重新出发 -- 善用搜索组合和重复 / Search and Repeat
    5. 宏和偷懒的程序员 -- 使用宏来存储命令 / Command Macro
    6. 可以编程的Vim -- 给Vim添加函数 / Functions for Vim
    7. Vim是张画板 -- 对一块代码进行操作 / Vim like a drawing board
    8. 最后的小结 / Summary

     

    1. 为什么是Vim / Why Vim

    Vim是一款古老的编辑器,可经过了这么多年依旧有许多忠实的使用者,也有许多新人不断加入使用Vim的行列,可以看出,这个具有年代感的编辑器一定拥有着某种魅力。

    使用Vim的理由有很多,最直接的就是Vim的无处不在,基本上只要有Linux,你就能找到Vi/Vim,而另外一个让许多人离不开Vim的理由就是高效的按键命令组合了,

    vimtutor的开头是这样的一段话,

      Vim is a very powerful editor that has many commands, too many to explain in a tutor such as this. This tutor is designed to describe enough of the commands that you will be able to easily use Vim as an all-purpose editor.

    Vim在文字编辑领域内的强大是毋庸置疑的,而作为一名仅有一年使用经验的Vim新手来说,Vim让我觉得有趣的地方,不仅仅是各种按键功能的组合重复,更多的是一种创造性,就像编程一样,我们可以用1000段不同的代码,去实现某一种功能。

    那么,在开始之前,首先假设你已经知道Vim的一些基本模式和功能按键,同时,这篇文章的目的更多的是为了记录和分享使用Vim时的一些思维和感想,而不是单纯的功能命令,最后,收拾好行李准备出发吧。

     

    2. 从hjkl开始上路 -- 使用基本按键进行移动和编辑 / Start from <hjkl>

    现在,假设我们在写一段Python代码,常常会遇到下面这样的问题,写好了一个元组x,里面包含了几个数字1,可现在由于某些需求,需要将元组里的数字对象改成字符串存储,就像下面这样,从左边变到右边:

     

    这时候该怎么办呢,最先想到的办法当然是用vim按顺序老老实实一个一个改了:

      (1)利用hjkl将光标移动到第一个1的位置;

      (2)按下i (insert),进入插入模式,输入单引号('),完成前向插入,Esc退出插入模式;

      (3)再将光标移动到第一个1的位置

      (4)按下a (append),进入插入模式,输入单引号('),完成后向插入,Esc退出插入模式;

      (5)重复前面(1)(2)(3)(4)的操作,利用hjklai完成剩余所有的修改

    可是,这样不是很慢吗,按了半天的hjklEsc,移光标,切模式,还不如我用 - >(方向键右)从行首移动到行尾挨个插入引号来得快呢。

    是的,这样的确快,可是你有没有发现,你的右手不停地在方向键和单引号之间来回移动(别告诉我你左手按引号右手按方向键,除非你的左手打算放弃左半边键盘,否则当你在输入引号后,如果还要接个字母a,你的左手同样需要移动回去)。

    所以,在这个例子里,推荐hjkl来代替方向键,并不是想证明这些按键能让这次修改变得多快,而是希望展示Vim的另外一种快速:

      忘掉Enter键往右的那些按键,让你无需移动手臂就能掌控整个键盘的功能,

      至于鼠标,不存在的,那种比数字小键盘还远的东西,在敲代码的时候能不碰就别碰。

    这其实也是Vim和程序员相契合的一个微妙的点:偷懒,懒得移动手臂,我只愿意用手指干活。

    好了,当你开始习惯性地使用hjklai和模式切换去完成所有的文字编辑时,那么说明你已经开始认可并接受Vim了(虽然这可能会让你前2个月的编码速度变得奇慢无比,但是当肌肉和思维习惯了之后,只会爱不释手)。

     

    3. 一次超速和翻车的体验 -- 使用命令进行全局替换 / Global Replacement

    当然了,前面的那个替换的例子,操作起来有点慢的过分了,这时候肯定会有一些Vim的老司机跳出来告诉你,Vim又不限速,你开这么慢干嘛,油门踩到底。于是你会看到这样一条命令:

       

    首先,这是一条替换命令,将行内所有的1替换为'1',介绍一下这条命令的原型,

      :[m,n | %]s/old/new/[gc]

    其中,方括号[]中的参数为可选参数,

      : -- 表示进入命令模式(许多vim的命令都是以先加一个冒号:开始的);

      m,n -- 表示替换的作用范围,从m行到n行,m和n可以是相对行号,如-3,+5;

      % -- 加%则表示作用范围是整个文件,不加则当前行(%与m,n不可同时使用);

      old -- 需要替换的字符串,支持正则表达式;

      new -- 替换后的新字符串;

      g -- 加g表示作用范围内所有符合的old都替换成new,否则只替换第一个;

      c -- 加c则进行替换前会进行询问,类似Linux中rm -r不加f时的情况;

    有了上面这条命令,基本上可以很快完成一个批量替换的操作,例如这样:

     

    只需要使用下面的命令,就可以完成一次性全局替换

     

    但是,开太快了肯定容易翻车的,比如这种情况,如果使用刚才的全局替换,那么不想被替换的变量y中隐含的1也被替换了,当文件里的内容非常多的时候,这就变得防不胜防。

     

    对于这种情况,为了避免翻车,有以下几种办法可以解决:

      (1)限道行驶,只在自己的地盘开:添加m,n参数或者使用Visual模式限制作用的范围;

      (2)限速行驶,开慢点:添加c参数,每次替换前都进行一次询问,只改自己需要的;

      (3)换个司机,找个技术好点的:old是支持正则表达式的,所以...换上正则精确匹配吧。

      Note: 虽然这种全局替换的方法容易出问题,但是在替换前多观察,做好特征的匹配,或当文件不复杂时,使用起来还是十分方便的。

     

    4. 开慢一点,重新出发 -- 善用搜索组合和重复 / Search and Repeat

    翻过一次车了,自然会心有余悸,那么咱换个慢点的车,利用Vim中的搜索和重复来试试如何修改。

    在这之前,介绍一下新车怎么操作:

    / -- 当前行向后全局查找(是向前);

    n -- next,跳转到下一个查找结果(N是上一个);

    c -- change,修改指定范围内的字符;

    . -- 点号(句号),重复上一次操作;

    然后有下面这样一段demo,一个奇怪的Python类里面包含了一些奇怪的属性,

     

    可有一天我们突然不想再让这个类这么没有意义,于是决定给它里面的属性换上一些有意义的名字,就叫something_meaningful吧,可是原来的属性那么多,something_meaningful这个单词又这么长,怎么办呢。

    当然是批量替换了,这个段代码很简单,完全可以利用前面介绍的批量替换来完成,但是这不是我们现在想要介绍的,这里想介绍的是Vim的一种组合和重复的方式。

      (1)首先,利用搜索符号 / ,输入 /foo_ 或 :/foo_ 来查找到所有foo_,就像图中那样;

      (2)然后按下n,光标跳转到第一个匹配的 foo_ 上面;

      (3)这时候,输入c3l(hjkl的那个l),也就是向右改变3个字符;

      (4)此时会删掉 foo_ 并进入insert模式下,输入something_meaningful,Esc退出;

      (5)这时候第一个修改已经完成,剩下的操作就很简单了,继续按下n,跳转到下一个,然后按下 . (点号/句号)键,重复上一次的操作,也就是操作(3)(4)所完成的删除修改。

      (6)不断地使用n . 的组合,就可以轻松地完成所有的修改。

    Note:

    1. 上面的操作(3)体现了Vim中的组合功能,这时候如果告诉你,d代表删除(delete),那么d3l(向右删除3个字符)和d3j(向下删除3行)的功能自然不言而喻,同样的还有y代表复制(yank),yiw (yank inside word) 则是复制一个单词,dap (delete around paragraph)或dip(delete inside paragraph)删除一个段落,甚至还可以da / di或者da( / di(,删除一对引号或者是括号之间的内容,此处的around和inside略有不同,区别在于inside的范围不包括边界(例如di(中,边界就是一对括号)。
    2. 而操作(5)就是Vim中常用的重复功能,利用好按键 . 将会让操作轻松许多。

     

    5. 宏和偷懒的程序员 -- 使用宏来存储命令 / Command Macro

    程序员是一种聪明而又偷懒的生物,所以才有了编程和函数,那些重复又无聊的操作?用代码搞定它们。编程的世界里到处都是重复,从使用变量替代具体的数值,到函数的封装调用,再到类的继承派生,虽然这些概念并非完全是为了应付重复而提出的,但至少在程序员的眼里,任何可能重复的操作,他们只愿意实现一次,一劳永逸的懒惰者。

    而Vim似乎也迎合了程序员的这种特质,比如上面提到的,使用按键 . 去重复上一次的操作。可如果想要重复的操作不止一个,该怎么办呢?

    写个宏或者函数吧,把那些烦人的操作都塞进去。是的,Vim给了你录制宏和写函数的机会!那么让我们先来看看这个录制宏的操作吧,使用宏可以一次将多个命令操作记录并存储下来,方便下次调用。

    录制宏

    首先介绍一下几个基本的功能键:

    q -- 录制命令宏到寄存器

    @ -- 调用寄存器的命令宏

    有了这两个按键,接着我们就一起来看看,宏的录制和使用过程,还是以刚才的那个修改为例,我们虽然可以用一个按键 . 就实现 foo_ 到 something_meaningful 的修改,可是却需要不停轮流按n . 来完成后面的操作,这可不是偷懒的程序员所乐意干的,那么就用宏来解决吧:

    命令原型:q + <register/寄存器> + <macro/命令宏> + q

      (1)首先,在普通模式下按下q键,这时Vim会等待你的下一个按键,并用这个按键所代表的寄存器来存储命令宏;

      (2)选择一个按键,比如a(或者b或者c都可以),此时Vim会进入record模式;

      (3)这时候,开始输入你想要录制的命令,比如按下n,再按下 . (注意,这些按键的功能还是会生效的);

      (4)完成了想要录制的命令后,再次按下q来保存宏并推出record模式,这样刚才录制的宏就保存在了a寄存器里;

      (5)当需要使用a背后存储的宏时,只需要输入命令 @a 就能够对宏进行调用了。

    Note:

    1. 还记得前面提到的组合这个概念么,是的,你可以输入 2@a 来对a所代表的宏进行2次调用,所以如果前面需要修改的地方有10处,那就用10@a可以一次搞定;
    2. Vim里有组合自然就有重复,用 @@ 来可以对上一次使用的宏进行再次调用;
    3. 当然,这里有一件重要的事不能忘了,那就是千万别在录制宏的时候,在宏命令里加入 @@ ,否则...你可以想象一下,一个无穷递归的函数会有什么样的下场;
    4. 如果不小心犯了c中提到的错,你就会发现你的vim变得不听使唤毫无响应了,别担心,这时候,ctrl + c结束这个万恶的递归吧。

    修改宏

    如果一不小心发现自己的宏录制错了,该怎么办呢,重新录制一遍?当然可以,但是如果这个宏很复杂,那么懒惰的程序员会乐意再来一次么?Certainly not,所以,调出刚才输错的宏,修改一下再存回去,不是很好么。

    先来介绍一下需要用到的功能按键,

    " -- 双引号,寄存器标识

    p -- 粘贴

    $ -- 行末

    0 -- 行首

    接着以寄存器a为例看看如何操作

      (1)先在文件里找到一块空地,用来存放待会需要编辑的宏;

      (2)使用命令 "ap 来读取寄存器a内的命令并粘贴到Vim,此时你会在刚才空白的文本处看到之前录制的宏命令,例如刚才存储的 n. ;

      (3)修改你的宏命令;

      (4)修改完后将光标移动到刚才那一行的行首,或者使用按键0回到行首;

      (5)在行首位置使用命令 "ay$ ,便可以将从当前位置到行末的命令复制进寄存器a中;

      (6)最后,别忘了删掉刚才粘贴上来的命令。

    Note:

    如果对于宏的修改只是需要继续增加命令的话,以a存储的宏命令为例,可以使用 qA 来重新进入record模式,然后继续添加操作。

    自动加载宏

    打开vim就想拥有一堆设置好的宏?配置文件了解一下,vimrc是Vim加载的一个配置文件,在Linux中,一般存放位置为 /etc/vimrc,有了这个文件,就可以提前写好Vim所需的宏,然后每次开启Vim的时候,这些宏便会自动加载好。

    例如,在vimrc中添加一行命令:

    Let @a="ohello, world!"

    这时如果调用宏a,Vim就会自动插入一行hello, word!

     

    6. 可以编程的Vim -- 给Vim添加函数 / Functions for Vim

    有宏自然就会想到函数,而对于Vim,你想得到的,都能找得到。是的,下面就用一个例子介绍一下如何编写一个Vim函数并映射到一个快捷键上:

    首先来看看这个函数,函数存放的位置就在Vim的配置文件中(/etc/vimrc),函数本身很简单,不过我还是为每行都添加了注释,方便阅读,其功能是目录递归向上查找一个tags文件,找到就更新文件,找不到就返回,

      (1)先看第二行,关键字function,然后接了个感叹号(!),这是因为Vim中如果需要定义已存在的函数,就需要加入一个!,而UpdateCtags是一个Vim默认已有的函数;

      (2)记录当前的路径信息,进入循环,判断tags可读文件是否存在,不存在则切换到上层目录,如果达到根目录,则说明文件不存在,退出查找;

      (3)如果找到文件,则判断是否具有写权限,若具有则执行命令更新文件;

      (4)最后返回原目录并结束函数;

      (5)第17行将按键 <F5> 和函数进行映射,按下 <F5> 便会调用这个函数。

    Note:

      可以在Vim中输入 :h function 来查看关于函数定义的帮助文件。

    函数的存在为Vim提供了无限的可能性,同时利用按键映射,就可以通过快捷键来完成许多自定义的操作。这种高度自由的特点,也是Vim的魅力之一。

     

    7. Vim是张画板 -- 对一块代码进行操作 / Vim like a drawing board

    当你碰到下面这样一段代码,并告诉你需要把每行后面的注释都删掉的时候,你是不是会祈祷自己的屏幕能变成一张白纸,然后用橡皮一次性把那块废弃的注释给擦个干净?别急,Vim又给了你这样操作的机会。在vim中,有一个叫做Visual Block的模式,在这个模式下,Vim就像是一张画板,你可以圈出某一块区域,进行移动或修改,

     

      (1)使用ctrl + v进入Visual Block模式;

      (2)利用hjkl来移动光标,完成代码块的选择;

      (3)最后用功能按键d(删除)/ x(删除)来完成想要操作。

    Note:

      上面的(3)中,除了进行删除外,还可以进行 c(修改)/ y(复制)/ I(前向插入)/ A(后向插入)等其他操作。

    那么,让我们回到刚才那个例子,如何用Visual Block来完成我们前面提到的把foo_ 替换成 something_meaningful 呢,最直接的做法就是擦掉所有的 foo ,然后插入所有的 something_meaningful,对不对,那么让我们用Vim来试一试:

      (1)首先光标落在第一个foo_的词首位置,ctrl + v 进入 Visual Block 模式;

      (2)使用hjkl移动光标选中代码块,当然可以,不过还记得Vim里的组合吗,这里或许可以试试另一种方法:先输入2l,然后输入5j,这样就拉出了一个3*6的矩形,正好选中了想要的区域。

     

      (3)这时候,按下按键c,就会发现所有的foo都被删除并进入了插入模式,接着就可以输入自己想要替换的字符,不过只有第一行会显示输入;

     

    (4)输入完成后按下Esc,Vim在退出插入模式后,会自动补全剩余的修改。

     

    8. 最后的小结 / Summary

    想写的内容暂时到此为止,这里所介绍的内容只是Vim中极其小的一部分,Vim所能提供的功能远比这些来得强大,再配合上外部的插件,Vim足以适应任何需要进行文字编辑的场合。而这篇文章的内容更主要的是想介绍一些使用Vim时的感想,主要有以下几点:

      (1)从适应hjkl开始,减少手臂的移动,提升操作的效率,纯粹的键盘操作可以让你更专注于思考编码问题;

      (2)熟悉Vim的基本按键,然后用自己的想法去尝试着进行组合和创造,会有意想不到的收获;

      (3)用好重复,无论是使用点号还是录制宏,又或者是定义函数,都是为了不重复;

      (4)Vim中的文字可以不按行看,有时候你把屏幕当成一张纸,按区域块操作起来或许会更容易;

      (5)在Vim中一个问题可以有无数种操作方式去实现,不一定要选择最快的,但要选择最合适的。

      

    相关阅读


    1. Vim 环境配置 

    2. Vim 的快捷键操作

  • 相关阅读:
    NeurIPS 2018 中的贝叶斯研究
    史上最全采样方法详细解读与代码实现
    采样方法(二)MCMC相关算法介绍及代码实现
    第七十四篇:机器学习优化方法及超参数设置综述
    论文阅读:《Bag of Tricks for Efficient Text Classification》
    训练技巧详解【含有部分代码】Bag of Tricks for Image Classification with Convolutional Neural Networks
    数值分析-Legendre正交多项式 实现函数逼近
    指针生成网络(Pointer-Generator-Network)原理与实战
    强化学习入门 第五讲 值函数逼近
    UniGUI的 TUniPageControl控件动态拖动tabsheet的实现方法
  • 原文地址:https://www.cnblogs.com/stacklike/p/9648459.html
Copyright © 2020-2023  润新知