自从半年前接触了Vim,我再也不想用其他的任何编辑器了。在学会了基本的操作后,我开始寻找Vim的高级操作,而相关的内容大家的博客上也都只是零零散散的记录。我一度以为我已经学会了Vim的全部,直到我见到了这本书:《Practical Vim》,我才知道原来自己仍然只是个小白。
这篇博客不是为了带Vim的小白入门,而是记录一些进阶的操作。想要入门的可以看看这篇文章:https://blog.csdn.net/sumword_/article/details/53011463 ,我就是看这篇文章入的Vim的坑,非常推荐。此外,本篇文章里我也只是记录我认为必要的知识,而不是完全的对原书的抄录,故没有原书详细。
标识 | 意义 |
---|---|
|
按下esc |
、
|
按下enter |
|
同时按下ctrl和r |
|
按下table |
|
按下shift |
|
按下shift和table |
、Down |
按下光标上下键 |
<︹> |
按下space |
用聪明方法自我重复——不要进行无意义的劳动
rintf("Hello world!!\n");
那么可以:"Hello world!!\n");
("
world!!\n");
("
!!\n");
rintf("Hello world!!\n");
,我想把光标移动到w
orld
,也就是第二个o
上。第一次我没动脑子,按下fo(2fo就可以直接达到目的嘛),结果光标移动到了第一个o
上,也就是:printf("Hell
o world!!\n");
,可这不是我要的结果啊,怎么办,再输一次fo?,这样虽然可以达到结果,但却不是最优的解决方案,因为按下了两个按键。最优的补救方案是;,只需要按下一个键。假如说;按过头了咋办?,可以补救,回到上一个匹配的字符处。现在假如说有一句话 The kin
g,要删除掉king这个单词,此时光标位于字符g位置。
The
gThe
The
king
The
a
、i
范围限定词The
a
限定词相比i
会多选定单词边的一个空格。a
或者i
限定符用的还是很多的,比如删除一对括号()[]{}
中的内容,di(会删除()
内的内容,之后光标停留在(
,di)则会停留在)
。如果换成了a
限定符,则会连同括号一块儿删掉。现在比较比较吧,这三种解决方法那种得分更高?想想vim解决问题的方式:自我重复,就知道第三种方法更具有推广意义。只要光标在一个单词上,daw能最大发挥.命令的威力。
先介绍命令:
前者 的作用是:向右找到最近的数字,然后将这个数字 减 1;;后者 作用则是 加 1.
如果指定次数,就可以使一个数字按照自己的意愿增加或者减小。
举个例子:
for(int i = 0; i <
n; i += 1);
如果执行10for(int i = 1 ; i < n ; i +=
11);
如果执行10for(int i = 1 ; i < n ; i +=
-9);
在Windows上的vim应使用
另外注意:数字的格式
以0开头的数字,vim将其理解为八进制。如果对007
进行009
,而是010
。如果你的vim将其理解为八进制,在vimrc中加入set nrformats=
就能将所有数字看成十进制。
从diw
、da{
、dj
等等操作也能看出这个规律。vim的所有动作都是按照这个公式组合的。对vim的词汇掌握的多的话,就可以根据这个规则自己组合出自己想要的操作,当自己做到这一点的时候心里是非常有成就感的。
另外补充g
的用法。
g
就是一个前缀符,常用的默认作用是改变字母大小写的功能前缀。
g~
将大小写相互转换。
gu
将字符变成小写。
gU
将字符变成大写。
这三个都还没有添加作用范围,可以是j
(当前及下一行),G
(从当前行到文件尾行)等等任何你愿意的范围限定。g~
就相当于是一个操作符,只不过是g
打头,使得后面的~
进行了转义,有了别的意义。
g
这个前缀符和还有另一个用处:当一行不能完整显示这一行内容时,vim会将内容折回显示在多行,但实际上这多行是一行。
如果用jk
移动光标的话,移动的是实际行,而不是当前行(在界面中看到的每一行)。
怎么移动当前行光标呢?加上g
这个前缀就好了。用gj
、gk
就能很开心地移动当前行光标啦!
比如我的一个快捷键设置: nnoremap ,sop aSystem.out.printf("");
。作用是normal模式下,,sop
四个键能在当前位置插入Java标准输出语句,并保持最佳缩进,最后将光标移动到双引号处。normal模式中,s
本来已经被占用,所以就设置用,
来当作前缀符完成功能设置。
http://learnvimscriptthehardway.stevelosh.com/ 这个链接是VimScripts的教学讲解,对自定义vim有爱好的可以学一下vim的脚本编写。
插入模式中,我之前最多的技巧也就是自己定义的在插入模式中的一些快捷键,再没别的了。在Normal模式中待的太久,已经忘却了研究一下插入模式中的技巧。看了这本书,没想到插入模式也有这么多小技巧,哈哈哈太开心了。那么就一起来看看吧!
我之前在插入模式中修改文本,要么是backspace或者delete按个不停,要么是进入Normal模式,diw或者ciw。现在找到了在插入模式中直接修改文本的方法:
按键 | 功能 |
---|---|
删除前一个字符 | |
删除前一个单词 | |
删至行首 |
返回Normal模式的经典方案是Esc键,当然也可以用
在插入模式中有时候需要运行一次Normal模式中的命令。这种情况下当然可以按下Esc进入Normal模式,运行完再回到插入模式。可是有更好的解决方案,那就是Insert-Normal
模式:
在插入模式中,用
在插入模式中,按下@{reg}
,比如@0
,就是刚才复制过后得到的值。
这样很方便,如果文本中有一个你需要计算的数,只要将其复制一下到某个寄存器中,然后引用这个寄存器中的值就能方便的计算了。
默认情况下,Ex命令都是作用于当前行。
在可视模式中,Ex命令会作用于选择的行。这些命令包括你在vim命令行输入的命令,也包括你执行宏的操作。
当前窗口分屏
命令 | 操作 |
---|---|
水平分割当前窗口,新窗口显示仍为当前缓冲区 | |
垂直分割当前窗口 ,新窗口显示仍为当前缓冲区 |
水平、垂直分割这两个操作,初学的时候很容易混淆,用的多了就能分清楚两者的区别了。
分屏打开文件
命令 | 操作 |
---|---|
:sp[lit] {file} | 水平分割当前窗口,新窗口显示{file} |
:vsp[lit] {file} | 垂直分割当前窗口 ,新窗口显示{file} |
这两个操作,没有指定文件的话,默认会载入当前的缓冲区。
在窗口切换
命令 | 用途 |
---|---|
在窗口之间切换 | |
切换到左边窗口 | |
切换到右边窗口 | |
切换到下边窗口 | |
切换到上边窗口 |
完整列表参考:h[elp] window-move-cursor
实际上
关闭窗口
Ex命令 | Normal命令 | 用途 |
---|---|---|
:clo[se] | 关闭活动窗口 | |
:on[ly] | 只保留活动窗口 |
改变窗口大小
命令 | 用途 |
---|---|
使所有窗口等高等宽 | |
最大化活动窗口高度 | |
最大化活动窗口宽度 | |
窗口高度减小一行 | |
窗口高度增加一行 | |
窗口宽度减小一行 | |
窗口宽度增加一行 |
完整列表参考:h[elp] window-resize
如果终端支持鼠标,或者使用GVim时,可以用鼠标直接拖动分割线来调整分屏的比例,比较方便。
打开以及关闭窗口
命令 | 用途 |
---|---|
:tabe[dit] {filename} | 在新标签中打开某个文件 |
将当前窗口(如果有多个窗口打开:vsp, :sp)移到一个新的标签页 | |
:tabc[lose] | 关闭当前标签页,以及里面的所有窗口 |
:tabo[nly] | 只保留当前活动标签页,关闭所有其他标签页 |
标签页之间的切换
Ex命令 | Normal模式命令 | 用途 |
---|---|---|
:tabn[ext] {N} | {N}gt | 跳转到编号为{N}的标签页,默认为下一个标签页 |
:tabp[revious] | gT | 跳转到上一标签页 |
实际上gtgT 按起来并不方便,我选择用其他的快捷键来实现这两个操作:
nmap g[ gT
,nmap g] gt
。这两个快捷键更形象,更好按。当然你也可以自己设置自己喜欢的快捷键。
都有哪些寄存器?
""
"a - "z
"0
"_
"+
,选择专用寄存器"*
还有一些其他寄存器:
寄存器 | 内容 |
---|---|
"% |
当前文件名 |
": |
上次执行的命令行命令,@: 可以再次执行它 |
"/ |
上次查找的模式 |
那好了,有这么多寄存器,怎么使用它们呢?
如果要更新某个寄存器的内容,在剪切或复制等过程中,必须要明确指出他们。比如我经常使用的一个操作:gg"+yG ,将整个文本复制到系统剪切板中。其他操作也是类似,先指明寄存器,然后进行操作、限定范围。知道了寄存器是怎么一回事,vim似乎更强大了呢。
查看寄存器的内容,normal模式下:
:reg
查看所有有内容的寄存器内容。:reg {register}
,比如,:reg a
查看a寄存器中的内容寄存器内容的编辑:
宏和上一节的寄存器讲解密不可分。
所谓宏就是记录好的一串固定的操作。会使用宏,你在编写文档的过程中能更加得心应手,能够快速处理一些重复而又麻烦的操作,这时候就不由得惊叹Vim的强大了。接下来就来讲解宏的使用:
宏的录制:q{register}[motion]q
。第一个q表示开始录制宏,最后的q表示宏录制的结束。中间你操作vim的过程会记录在你选择的寄存器中。
宏的执行: 第一次运行:@{reg}
,之后重复运行:@@
。
举个例子吧,写完.h
头文件后,在.c
文件中需要将在头文件中声明的函数展开,如果没有宏那将会痛苦不堪。
现在在.h
文件中声明了一些函数:
void func1();
int func2();
char* func3();
将这些声明复制到.c
文件中。假如说光标在func1()
这一行,处于normal模式中,那么可以:
qqA
。我对{{
定义了快捷键,会自动在当前行下方添加一对展开的大括号。这个快捷键是这样定义的:inoremap {{
void func1()
{
}
int func2();
char* func3();
宏录制完毕,现在光标已经到了func2()
这一行了,刚才录制的宏,也就是你所按下的键的顺序,已经记录到了q
寄存器了。接下来只需要@q
,@@
就可以将剩下的两个函数展开了。如果下面还有更多函数,相信你已经知道这样会有多方便。
宏的编辑
有时候手贱,宏还没有录制完就按下了q键。不完整的宏可不能用。看看怎么修改吧:
q{register}[motion]q
只不过这次选择寄存器的时候按下的是大写字母。这次录制的宏就追加到了刚才的寄存器中。:put q
。为啥不用"qp
呢?前者总是另起一行,然后粘贴。后者则是粘贴到当前光标之后。显然前者更方便。太多模式了,不要纠结于各个模式的区别而不知道用哪个模式了,尤其是你太熟悉正则的情况下!
如果要用到正则表达式,那么就要用\v
模式。
如果要用原义匹配,那么就用\V
模式。
一旦习惯,这两种模式会非常顺手!
之前学正则表达式的时候,很痛苦,这么多符号怎么记得住!随着我越来越多用vim,我开始了正则之旅。在vim里用正则,所写即所匹配,所匹配即所见,十分友好,让我正则越用越熟练。不过vim的正则和Python还是有些区别的,我在Python里用正则的次数没有在vim里面多。
global命令很强大!可以在所有匹配到的位置进行某种操作。默认执行范围是整片文档,当然你也可以限定范围。
需要用到ctags。我现在写的代码量还不是很多,所以觉得ctags这个程序有点鸡肋,因为还得运行vim外部的命令,屏幕的跳转让人看起来很不爽。如果要时时刻刻更新ctags文档的话,会很难受。
Windows上没有grep,所以可以用vimgrep了。
vim //g %
这个命令可以统计上次匹配的词在本文档中出现了多少次。
按下
set dictionary = file
dictionary += anotherFile
待更新