像编程一样使用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 的快捷键操作

代码编辑器[0] -> Vim/gVim[3] -> 像编程一样使用Vim的更多相关文章

  1. Github Atom开源文本代码编辑器- 由 Github 打造的下一代编程开发利器

    个人理解:Github 热度超凡的一个项目Atom,electron是整个atom的核心,对于electron可以理解成 electron =io.js + Chromium    通过 Electr ...

  2. 代码编辑器[0] -> Vim/gVim[0] -> 基于 Python 的 gVim 环境配置(Windows)

     环境配置 / Environment Setup 基于Python开发的 gVim 环境配置(Windows) 使用方式参考 Vim 的使用. 1 基于vundle进行配置 Vim有多个扩展管理器, ...

  3. 代码编辑器[0] -> Vim/gVim[1] -> Vim 的快捷键操作

    快捷键 / Shortcut Keys 1 基本操作 / Basic Operation Vim的基本操作主要可以参考以下几张图,参考链接, 命令行模式 i             从光标所在字符前插 ...

  4. 代码编辑器[0] -> Vim/gVim[2] -> Vim 的相关知识

    相关知识 / Relevant Knowledge 1 _vimrc编程 / _vimrc Program 1. 注释符", 用于注释 2. 关键词set, 用于设置功能等 3. 关键词im ...

  5. Linux中的代码编辑器vim

    Vim的三种工作模式 命令行模式 插入模式 底行模式 Vim 的命令行模式 命令行模式是进入vim后的初始模式,在该模式下主要是使用方向键来移动光标的位置,并通过相应的命令来进行文字的编辑. 切换方法 ...

  6. 〖Linux〗(2013.08.02)VIM74b+YouCompleteMe,VIM代码编辑器补全能手

    1. 编译和安装vim74b(参考:http://t.cn/zQa8R7h ) sudo apt-get install -y hgsvn libncurses5-dev libgnome2-dev ...

  7. 代码编辑器的最终选择Sublime Text 2

    对于程序员,不是每一种语言都有很好的代码编辑器,VS这样的编辑环境+编译器也不能适合所有的语言,同时VS占用内存量很大,开几个VS,计算机就开始有点吃不消了.所以简便的代码编辑器很重要. 再Windo ...

  8. 介绍linux下Source Insight强大代码编辑器sublime_text_3

    背景 1 一. 运行环境 1 二.安装环境配置 1 三.创建快捷方式 1 四.配置全局环境 2 五.操作界面 3 背景 在windows操作系统系统下,文本代码编辑器众多,各路英雄豪杰争相写了许多强大 ...

  9. 强大的vim配置文件,让编程更随意(转)

    欢迎来到小码哥的博客 博客搬家啦 blog.ma6174.com 强大的vim配置文件,让编程更随意 花了很长时间整理的,感觉用起来很方便,共享一下. 我的vim配置主要有以下优点: 1.按F5可以直 ...

随机推荐

  1. phaser常用API总结

    1. 游戏画布的尺寸 var width = game.width, height = game.height;   2. 中心点坐标 var game = new Phaser.Game(...); ...

  2. JUnit4.11 理论机制 @Theory 完整解读

    最近在研究JUnit4,大部分基础技术都是通过百度和JUnit的官方wiki学习的,目前最新的发布版本是4.11,结合代码实践,发现官方wiki的内容或多或少没有更新,Theory理论机制章节情况尤为 ...

  3. 解决Vue方法中setTimeout改变变量的值无效

    把data里的变量继承过来重新封装一下 let that = this; this.rightAnswer = false; setTimeout(function() { that.rightAns ...

  4. pmap用法小计

    By francis_hao    Aug 4,2017   pmap-报告进程的内存映射.   概要 pmap [options] pid [...]   描述 pmap命令用来报告一个或多个进程的 ...

  5. Equal Sums (map的基本应用) 多学骚操作

    C. Equal Sums time limit per test 2 seconds memory limit per test 256 megabytes input standard input ...

  6. barba.js 优化页面跳转用户体验

    barba.js 原理就是在a页面中显示b页面的内容,样式为刷新,给用户以页面跳转后无刷新体验,注意样式命名,ab页面引用的样式和js要相同 可以在页面之间创建良好的转换,增强用户的体验. 减少HTT ...

  7. eclipse配置文件内存设置

    1.-Xms64m -Xmx128m 2.配置文件的修改 http://wenku.baidu.com/link?url=spM-qCe0qHdhiykzwuzp-vBtcQrVtAzYiWe8uex ...

  8. Spring 学习笔记(一)

    一.Spring 是什么? •Spring 是一个开源框架. •Spring 为简化企业级应用开发而生. 使用 Spring 可以使简单的 JavaBean 实现以前只有 EJB 才能实现的功能. • ...

  9. mybatis基本流程、jdbc连接、ps:附mybatis(乐观锁)实现

    一.前言 Mybatis和Hibernate一样,是一个优秀的持久层框架.已经说过很多次了,原生的jdbc操作存在大量的重复性代码(如注册驱动,创建连接,创建statement,结果集检测等).框架的 ...

  10. php中的split函数

    字符串分割函数:split函数 <?php $email='microsoft@exam!ple.com'; $domain = split('\.|@|!',$email);//split分割 ...