Practical Vim 学习笔记

自从半年前接触了Vim,我再也不想用其他的任何编辑器了。在学会了基本的操作后,我开始寻找Vim的高级操作,而相关的内容大家的博客上也都只是零零散散的记录。我一度以为我已经学会了Vim的全部,直到我见到了这本书:《Practical Vim》,我才知道原来自己仍然只是个小白。
这篇博客不是为了带Vim的小白入门,而是记录一些进阶的操作。想要入门的可以看看这篇文章:https://blog.csdn.net/sumword_/article/details/53011463 ,我就是看这篇文章入的Vim的坑,非常推荐。此外,本篇文章里我也只是记录我认为必要的知识,而不是完全的对原书的抄录,故没有原书详细。

目录列表

  • 前言:特殊按键的标示
  • 第零部分——Vim三种模式
    • 0.Vim解决问题的方式
    • 1.普通模式
        • 构造可重复的修改
        • 简单的算术运算
        • 操作符 + 动作范围 = 操作
  • ==前面的写的质量太差,抽空全部整改一下==
    • 2.插入模式
        • 在插入模式中修改文本
        • 只返回一次Normal模式
        • 在插入模式中粘贴寄存器中的文本
        • 随时随地做运算
    • 3.可视模式
    • 4.命令行模式
  • 第一部分——文件
    • 5.管理多个文件
        • 分屏操作
        • 用标签页将窗口分组
    • 6.打开及保存文件
  • 第二部分——更快的跳转
    • 7.文档中跳转
    • 8.文件间跳转
  • 第三部分——寄存器
    • 9.复制与粘贴
    • 10.宏
  • 第四部分——模式
    • 11.模式匹配、原义匹配
    • 12.查找
    • 13.替换
    • 14.global命令
  • 第五部分——Vim的工具
    • 15.通过ctags建立索引,用之浏览源代码
    • 16.编译代码,通过Quickfix列表浏览错误信息
    • 17.通过grep、vimgrep等查找
    • 18.自动补全
        • 自动补全目录
        • 从字典文档补全
    • 19.Vim的拼写检查器

前言:特殊按键的标示

标识 意义
按下esc
按下enter
同时按下ctrl和r
按下table
按下shift
按下shift和table
Down 按下光标上下键
<︹> 按下space

第零部分——Vim三种模式

0.Vim解决问题的方式

用聪明方法自我重复——不要进行无意义的劳动

  1. 用.来重复上一次的修改。
    何为上次的修改?有两种情况:从Normal模式进入到Insert模式开始,到按下Esc回到Normal模式,这是一次操作;或是不离开Normal模式所进行的最后一次修改。可重复的内容限于删除、插入的过程,当然,也包括上次执行的宏,不能重复的就是在Vim命令行中做出的修改。其实 .命令本身就算是一个微型的宏
    来举个例子:比如,我想删除这段代码里面的三个单词: printf("Hello world!!\n"); 那么可以:
    dw----->("Hello world!!\n");
    w.----->("world!!\n");
    .----->("!!\n");
  2. 用;重复上次的查找。
    比如,还是这段代码:printf("Hello world!!\n"); ,我想把光标移动到world,也就是第二个o上。第一次我没动脑子,按下fo(2fo就可以直接达到目的嘛),结果光标移动到了第一个o上,也就是:printf("Hello world!!\n");,可这不是我要的结果啊,怎么办,再输一次fo?,这样虽然可以达到结果,但却不是最优的解决方案,因为按下了两个按键。最优的补救方案是;,只需要按下一个键。假如说;按过头了咋办?,可以补救,回到上一个匹配的字符处。
  3. 重复上一次的命令行操作。
    前面说了,.可以重复上一次的修改,但是不能重复Vim命令行中的操作。这里暂时只记录重复上一次命令的方法:@:。这其实和宏的执行方法类似,也就可以把这当成一个宏,执行过上次命令之后,接下来只需要@@就可以重复执行了。如果要执行更远的旧命令,我的建议是:然后用上下键来选择。vim也提供了输入命令来遍历Ex命令(在命令行执行的命令)的方法,但我觉得太过复杂,不够直观,很容易就搞不清楚自己遍历到了哪里,所以掌握执行上次命令的快捷方法就够了。

1.普通模式

构造可重复的修改

现在假如说有一句话 The king,要删除掉king这个单词,此时光标位于字符g位置。

  1. 反向删除
    db-----> Theg
    x-----> The
  2. 正向删除
    b-----> Theking
    dw-----> The
  3. 删除整个单词——学会使用 ai范围限定词
    daw-----> The
    在这个删除单词的例子中,a限定词相比i会多选定单词边的一个空格。
    实际运用中,a或者i限定符用的还是很多的,比如删除一对括号()[]{}中的内容,di(会删除()内的内容,之后光标停留在(,di)则会停留在)。如果换成了a限定符,则会连同括号一块儿删掉。

现在比较比较吧,这三种解决方法那种得分更高?想想vim解决问题的方式:自我重复,就知道第三种方法更具有推广意义。只要光标在一个单词上,daw能最大发挥.命令的威力。

简单的算术运算

先介绍命令:
前者 的作用是:向右找到最近的数字,然后将这个数字 1;;后者 作用则是 1.
如果指定次数,就可以使一个数字按照自己的意愿增加或者减小。
举个例子:
for(int i = 0; i <n; i += 1);
如果执行10,那么会得到for(int i = 1 ; i < n ; i +=11);
如果执行10,那么会得到for(int i = 1 ; i < n ; i +=-9);
在Windows上的vim应使用,因为是Windows的全选快捷键。
另外注意:数字的格式
以0开头的数字,vim将其理解为八进制。如果对007进行,那么将可能不会得到009,而是010。如果你的vim将其理解为八进制,在vimrc中加入set nrformats=就能将所有数字看成十进制。

操作符 + 动作范围 = 操作

diwda{dj等等操作也能看出这个规律。vim的所有动作都是按照这个公式组合的。对vim的词汇掌握的多的话,就可以根据这个规则自己组合出自己想要的操作,当自己做到这一点的时候心里是非常有成就感的。
另外补充g的用法

  1. g就是一个前缀符,常用的默认作用是改变字母大小写的功能前缀。

g~将大小写相互转换。
gu将字符变成小写。
gU将字符变成大写。

这三个都还没有添加作用范围,可以是j(当前及下一行),G(从当前行到文件尾行)等等任何你愿意的范围限定。g~就相当于是一个操作符,只不过是g打头,使得后面的~进行了转义,有了别的意义。

  1. 此外,g这个前缀符和还有另一个用处:

当一行不能完整显示这一行内容时,vim会将内容折回显示在多行,但实际上这多行是一行。
如果用 jk移动光标的话,移动的是实际行,而不是当前行(在界面中看到的每一行)。
怎么移动当前行光标呢?加上 g这个前缀就好了。用gjgk就能很开心地移动当前行光标啦!

  1. 你还能自定义快捷操作,有时候为了不破坏默认符号的操作,需要加上一个前缀符来代表自己的操作即将进行。

比如我的一个快捷键设置: nnoremap ,sop aSystem.out.printf("");==f"a。作用是normal模式下,,sop四个键能在当前位置插入Java标准输出语句,并保持最佳缩进,最后将光标移动到双引号处。normal模式中,s本来已经被占用,所以就设置用,来当作前缀符完成功能设置。
http://learnvimscriptthehardway.stevelosh.com/ 这个链接是VimScripts的教学讲解,对自定义vim有爱好的可以学一下vim的脚本编写。

前面的写的质量太差,抽空全部整改一下

2.插入模式

插入模式中,我之前最多的技巧也就是自己定义的在插入模式中的一些快捷键,再没别的了。在Normal模式中待的太久,已经忘却了研究一下插入模式中的技巧。看了这本书,没想到插入模式也有这么多小技巧,哈哈哈太开心了。那么就一起来看看吧!

在插入模式中修改文本

我之前在插入模式中修改文本,要么是backspace或者delete按个不停,要么是进入Normal模式,diw或者ciw。现在找到了在插入模式中直接修改文本的方法:

按键 功能
删除前一个字符
删除前一个单词
删至行首

只返回一次Normal模式

返回Normal模式的经典方案是Esc键,当然也可以用。我是一开始学习用Vim的时候就是按的Esc,已经养成了习惯,改不回来了,所以按就特别不舒服。
在插入模式中有时候需要运行一次Normal模式中的命令。这种情况下当然可以按下Esc进入Normal模式,运行完再回到插入模式。可是有更好的解决方案,那就是Insert-Normal模式:。也就是只运行一次Normal模式的命令,随即自动返回Insert模式。

在插入模式中粘贴寄存器中的文本

在插入模式中,用{reg}来粘贴某个寄存器中的文本。{reg}则会更智能一些,按照原意插入寄存器中的文本,并且自动修改缩进。

随时随地做运算

在插入模式中,按下=之后就能在屏幕下方输入表达式,然后敲下enter就能将计算得到的值插入到光标之后了。当然,你也可以引用寄存器中的数值进行计算。一个寄存器中的值在表达式中是这样引用的:@{reg},比如@0,就是刚才复制过后得到的值。
这样很方便,如果文本中有一个你需要计算的数,只要将其复制一下到某个寄存器中,然后引用这个寄存器中的值就能方便的计算了。

3.可视模式

默认情况下,Ex命令都是作用于当前行。
在可视模式中,Ex命令会作用于选择的行。这些命令包括你在vim命令行输入的命令,也包括你执行宏的操作。

4.命令行模式

第一部分——文件

5.管理多个文件

分屏操作

当前窗口分屏

命令 操作
s 水平分割当前窗口,新窗口显示仍为当前缓冲区
v 垂直分割当前窗口 ,新窗口显示仍为当前缓冲区

水平、垂直分割这两个操作,初学的时候很容易混淆,用的多了就能分清楚两者的区别了。
分屏打开文件

命令 操作
:sp[lit] {file} 水平分割当前窗口,新窗口显示{file}
:vsp[lit] {file} 垂直分割当前窗口 ,新窗口显示{file}

这两个操作,没有指定文件的话,默认会载入当前的缓冲区。
在窗口切换

命令 用途
w 在窗口之间切换
h 切换到左边窗口
l 切换到右边窗口
j 切换到下边窗口
k 切换到上边窗口

完整列表参考:h[elp] window-move-cursor
实际上w效果相同,也就是可以一直按着Ctrl,然后输入ww。
关闭窗口

Ex命令 Normal命令 用途
:clo[se] c 关闭活动窗口
:on[ly] o 只保留活动窗口

改变窗口大小

命令 用途
= 使所有窗口等高等宽
_ 最大化活动窗口高度
| 最大化活动窗口宽度
- 窗口高度减小一行
+ 窗口高度增加一行
< 窗口宽度减小一行
> 窗口宽度增加一行

完整列表参考:h[elp] window-resize
如果终端支持鼠标,或者使用GVim时,可以用鼠标直接拖动分割线来调整分屏的比例,比较方便。

用标签页将窗口分组

打开以及关闭窗口

命令 用途
:tabe[dit] {filename} 在新标签中打开某个文件
T 将当前窗口(如果有多个窗口打开:vsp, :sp)移到一个新的标签页
:tabc[lose] 关闭当前标签页,以及里面的所有窗口
:tabo[nly] 只保留当前活动标签页,关闭所有其他标签页

标签页之间的切换

Ex命令 Normal模式命令 用途
:tabn[ext] {N} {N}gt 跳转到编号为{N}的标签页,默认为下一个标签页
:tabp[revious] gT 跳转到上一标签页

实际上gtgT 按起来并不方便,我选择用其他的快捷键来实现这两个操作:
nmap g[ gTnmap g] gt。这两个快捷键更形象,更好按。当然你也可以自己设置自己喜欢的快捷键。

6.打开及保存文件

第二部分——更快的跳转

7.文档中跳转

8.文件间跳转

第三部分——寄存器

9.复制与粘贴

都有哪些寄存器?

  • 无名寄存器""
    默认的寄存器。普通的删除、复制过程都会更新这个寄存器中的内容。
  • 有名寄存器"a - "z
  • 复制专用寄存器"0
    见名知意。没有指明寄存器的话,只有复制过程会更新这个寄存器。
  • 黑洞寄存器"_
    如果只是想删除一些字符,但不想破坏其它寄存器中的内容,就可以把要删除的东西扔到这个寄存器中。扔进去就没了。
  • 系统剪切板"+,选择专用寄存器"*
    在Windows和Mac上,没有区别。

还有一些其他寄存器:

寄存器 内容
"% 当前文件名
": 上次执行的命令行命令,@:可以再次执行它
"/ 上次查找的模式

那好了,有这么多寄存器,怎么使用它们呢?
如果要更新某个寄存器的内容,在剪切或复制等过程中,必须要明确指出他们。比如我经常使用的一个操作:gg"+yG ,将整个文本复制到系统剪切板中。其他操作也是类似,先指明寄存器,然后进行操作、限定范围。知道了寄存器是怎么一回事,vim似乎更强大了呢。
查看寄存器的内容,normal模式下:

  • :reg查看所有有内容的寄存器内容。
  • ·:reg {register},比如,:reg a查看a寄存器中的内容

寄存器内容的编辑:

  • 我的做法是将某个寄存器的内容复制出来,在编辑区编辑,然后再复制回相应的寄存器。

10.宏

宏和上一节的寄存器讲解密不可分。
所谓宏就是记录好的一串固定的操作。会使用宏,你在编写文档的过程中能更加得心应手,能够快速处理一些重复而又麻烦的操作,这时候就不由得惊叹Vim的强大了。接下来就来讲解宏的使用:
宏的录制:q{register}[motion]q。第一个q表示开始录制宏,最后的q表示宏录制的结束。中间你操作vim的过程会记录在你选择的寄存器中。
宏的执行: 第一次运行:@{reg},之后重复运行:@@
举个例子吧,写完.h头文件后,在.c文件中需要将在头文件中声明的函数展开,如果没有宏那将会痛苦不堪。
现在在.h文件中声明了一些函数:

void func1();
int func2();
char* func3();

将这些声明复制到.c文件中。假如说光标在func1()这一行,处于normal模式中,那么可以:
qqA{{jjq。我对{{定义了快捷键,会自动在当前行下方添加一对展开的大括号。这个快捷键是这样定义的:inoremap {{ o{}iO

void func1()
{
	
}
int func2();
char* func3();

宏录制完毕,现在光标已经到了func2()这一行了,刚才录制的宏,也就是你所按下的键的顺序,已经记录到了q寄存器了。接下来只需要@q@@就可以将剩下的两个函数展开了。如果下面还有更多函数,相信你已经知道这样会有多方便。
宏的编辑
有时候手贱,宏还没有录制完就按下了q键。不完整的宏可不能用。看看怎么修改吧:

  1. 追加: 还是刚才录制宏的套路,q{register}[motion]q只不过这次选择寄存器的时候按下的是大写字母。这次录制的宏就追加到了刚才的寄存器中。
  2. 在编辑区修改:直接将刚才录制的宏所在寄存器中的内容复制出来,在编辑区修改就可以了。
    但是怎么复制出来,怎么复制回去,也都是有讲究的!
    复制出来:假如说宏所在寄存器是q,将宏粘贴到文档可以::put q。为啥不用"qp呢?前者总是另起一行,然后粘贴。后者则是粘贴到当前光标之后。显然前者更方便。
    复制回去:0"qd$,这样会将这一行从开头到最后(不包括换行符)的所有内容剪切到q寄存器中。为啥不用"qdd呢?因为这个操作是面向行的,换行符也会被一块剪切过去,为了保险起见,还是原样剪切回去比较好。

第四部分——模式

11.模式匹配、原义匹配

太多模式了,不要纠结于各个模式的区别而不知道用哪个模式了,尤其是你太熟悉正则的情况下!
如果要用到正则表达式,那么就要用\v模式。
如果要用原义匹配,那么就用\V模式。
一旦习惯,这两种模式会非常顺手!
之前学正则表达式的时候,很痛苦,这么多符号怎么记得住!随着我越来越多用vim,我开始了正则之旅。在vim里用正则,所写即所匹配,所匹配即所见,十分友好,让我正则越用越熟练。不过vim的正则和Python还是有些区别的,我在Python里用正则的次数没有在vim里面多。

12.查找

13.替换

14.global命令

global命令很强大!可以在所有匹配到的位置进行某种操作。默认执行范围是整片文档,当然你也可以限定范围。

第五部分——Vim的工具

15.通过ctags建立索引,用之浏览源代码

需要用到ctags。我现在写的代码量还不是很多,所以觉得ctags这个程序有点鸡肋,因为还得运行vim外部的命令,屏幕的跳转让人看起来很不爽。如果要时时刻刻更新ctags文档的话,会很难受。

16.编译代码,通过Quickfix列表浏览错误信息

17.通过grep、vimgrep等查找

Windows上没有grep,所以可以用vimgrep了。
vim //g %这个命令可以统计上次匹配的词在本文档中出现了多少次。

18.自动补全

自动补全目录

按下vim会自动补全光标所在位置的文本的路径。如果光标当下没有任何和路径有关的字符,那么vim会给出当前文件夹中的内容的选择列表,也就是遍历当前的文件夹。当然,也可以自己设置vim所在的“工作目录”,也就是:cd path。这样的话遍历当前的目录就是遍历你设置的目录下的文档。

从字典文档补全

从你设定的字典文档里面查找单词,然后就显示补全目录了。

set dictionary = file
dictionary += anotherFile

19.Vim的拼写检查器


待更新

你可能感兴趣的:(vim)