vim
一直是程序员之间比较有争议的一个话题。有人认为她是编辑器之神,有人则认为她古老过时,远远不如IDE,或是以当红小生vscode
为代表的图形化文本编辑器。无论爱恨,我们的开发工作,大到远程登录服务器coding
,修改config
文件,小到git commit message
,或多或少总要接触她。
为什么要写这篇blog呢,是因为我发现各大平台充斥的vim
教程类blog其实很不友好,大多数是命令的堆砌,很少有对思想的解读。由此造成的结果,很多工程师对vim
总是敬畏三分,或者就算部分人可以使用vim
,也只是以自己的固有思维,结合vim的命令操作,并没有真正掌握vim
的精髓。所以我尝试用自己的方式,试图帮助大家系统地建立起vim
的知识系统。
文章主要结构如下:
- 首先介绍
vim
编辑器最简单、基本的操作,让你快速入门,在遇到vim
时,可以不至于惊慌,从容完成任务。如果想到某些操作,比如常用的复制、粘贴之类,可以到第二部分查找对应高阶操作,循序渐进使用vim
。 - 接下来,将会介绍
vim
一系列高级操作,将我们的效率最大化。注:这部分信息量较大,建议您在阅读部分内容后,快速浏览不熟悉的命令,做到心中有数;随后可前进到第三部分;回过头来,再循序渐进,边学边练。 - 最后,试图讲述
vim
思想的精髓,既让我们真正对vim
的操作融会贯通,又让我们可以在使用其他IDE/编辑器时应用这些思想,甚至在我们自己设计、实现功能、组件时,都能进行应用。这才算真正掌握了神器vim
。
模式
vim有三个模式,分别为普通(正常)模式、插入模式以及命令模式。
- 普通模式:一般用于浏览文件,也包括一些复制、粘贴、除等操作。
- 插入模式:主要用来输入、修改、删除字符,此时的操作,除了不能用鼠标外,与我们日常在编辑器中操作无异。普通模式下,通过
i
等命令进入插入模式。 - 命令模式:用以执行一些输入并执行一些vim或插件提供的指令。在普通模式下通过输入
:
后,可以发现,屏幕的右下角会出现:
,此时便进入了命令模式。本文中使用:
开头的命令,便可视为输入:
进入命令模式后,输入后面的字符,执行命令。
很多人对于vim
的第一点疑惑,便来源于此。我们习惯了图形化编辑器下,始终处于插入状态。然而在vim
中,大多数情况下,我们会处于正常模式。只有当需要输入字符时,进入插入模式;当需要使用命令时,进入到命令模式。在插入和命令模式下,输入Esc
便可返回正常模式。一张图概括如下:
注:后文讲解,如无特殊说明,均为普通模式下操作。
基本操作
如果不追求效率,只想完成修改文件的任务,并保存退出,只需要掌握以下三个命令:
- 移动:
h j k l
最简单的移动,相当于键盘上面的方向键,分别对应左下上右。 - 进入插入模式:
i
- 保存退出:
ZZ
(注意区分大小写)
流程如下:
- 普通模式下,通过
h j k l
移动到想要修改的位置 - 输入
i
进入插入模式,此时通过输入(字母、数字、符号),删除(Backspace)等,完成基本修改操作 -
Esc
回到普通模式,ZZ
,保存修改并退出。
进阶:命令形式
普通模式下,vim
的命令主要分为以下三种:
-
动作,用以移动光标,或者定义操作的范围;比如:
-
h
:定义操作范围为一格,单独使用时,向左移动光标一格。 -
w
:定义操作范围为一个单词,移动光标到下一个单词首部。
-
-
操作,这种命令需要在后面接表示操作范围的指令;
-
d
,删除,后接表示一个单词操作范围的w
,即dw
时,表示删除到当前词尾。 -
c
,修改,后接表示一个单词操作范围的w
,即cw
时,表示修改当前单词。(编辑器行为表现为,删除到当前词尾,同时进入插入模式)。
-
-
命令,直接执行的命令,其中一部分,在执行命令后,直接进入编辑模式;比如:
-
D
,删除至行末。 -
I
,到行首进入插入模式。
我们的使用方式主要也是三种:命令、动作、操作+动作。
此外,在动作类的命令前,加上number
为可选项,可实现重复n次
的效果:
-
[number] + h/j/k/l
向左/下/上/右
移动number
个字符。比如,'2j',向下移动光标2个字符。依旧使用
d
和w
来举例,d
是删除,w
是单词,dw
代表删除一个单词,d2w
代表删除两个单词。后面的命令,大多都可应用此种形式组合使用,大家多注意,养成这种操作 + [次数] + 范围
的思维模式,举一反三,便可发挥最大功效。
移动进阶
单词级别的移动
这里有仅大小写不同的两组命令,两组命令的功能,是相同的:跳转光标到对应位置。但是对应的单位不同,分别为word
和string
。具体区别是:
-
string
仅以空格分开; -
word
以字母数字以外的字符分开。
以这个字符串为例:hello world-hehe111 abcde
-
word
有5个,分别为hello
world
,-
,hehe111
和abcde
。 -
string
有3个,分别为hello
world-hehe111
和abcde
。
两组命令如下:(跳转光标至)
-
w
下一个单词开头 -
e
当前或下一个单词结尾 -
b
当前或上一个单词开头 -
ge
上一个单词的结尾 -
W
下一个字符串的开头 -
E
当前或下一个字符串结尾 -
B
当前或上一个字符串的开头 -
GE
上一个字符串的结尾
举个例子,当光标位于hehe111
的第一个字符h
时,前后的单词/字符串信息如下:
前一个 | 当前 | 后一个 | |
---|---|---|---|
单词 | - | hehe111 | abcde |
字符串 | hello | world-hehe111 | abcde |
那么以上各个敲击以上各个命令的结果,便一目了然(加粗字表示命令运行后光标位置):
句子,段落级别的移动
-
0
移动到当前行行首 -
^
移动到当前行的第一个非空字符 -
$
移动到当前行尾 -
(
跳转到当前或前一个句子的开头 -
)
跳转到当前或下一个句子的结尾 -
{
跳转到当前或前一个段落的开头 -
}
跳转到当前或下一个段落的结尾
- 这里段落很容易理解,是以空行分隔开的。句子麻烦些,是按照句号来算的。
- 记得在这些命令前添加
d
试一下效果吧,掌握操作+范围
这种命令形式吧。
页面级别的移动
按行移动光标
-
gg
移动到文本第一行行首 -
G
移动到文本末行行首 -
[n] + %
:按百分比近似定位到某行,该行位于整个文件的n%
处 -
[n] + gg/G
跳转到第n
行,常用。
要想用好上述几个命令,有两个简单的建议:
- 结合命令:
ctrl-g
。该命令的作用是显示当前行的位置信息(第几行,相对整个文本行数的百分比)。 - 在命令模式下输入以下命令,或在
~/.vimrc
中添加如下代码片段
set nu " 显示行号
set cursorline " 高亮光标所在行</pre>
显示页面内移动光标
-
H
:屏幕顶部行首 -
M
:屏幕中央行首 -
L
:屏幕底部行首
滚动与翻页
-
ctrl-d/u
:前进/后退半页 -
ctrl-f/b
:前进/后退整页 -
ctrl+e
:上滚一行 -
ctrl+y
:下滚一行 -
zt
:使光标所在位置移动到屏幕的顶部(所有内容做位移) -
zz
:使光标所在位置移动到屏幕的中央(所有内容做位移) -
zb
:使光标所在位置移动到屏幕的底部(所有内容做位移)
匹配
-
f+单个字符
:在本行内向右移动到指定字符 -
F+单个字符
:在本行内向左移动到指定字符 -
t+单个字符
:在本行内向右移动到指定字符的前一个字符 -
T+单个字符
:在本行内向左移动到指定字符的前一个字符 -
%
: 在“( )”、“[ ]”、“{ }”类符号的首尾间切换 -
*
和#
: 匹配光标当前所在的单词,移动光标到下一个(或上一个)匹配单词(*是下一个,#是上一个)。
Mark
-
m+[a~z]
:在当前光标做标记,如ma -
'+[mark]
:光标返回指定标记所在的行,如'a
,则光标返回到标记a
所在行首 - "`"+[mark]:光标返回指定标记
-
ctrl+o
:跳转回光标前一个位置 -
ctrl+i
:跳转回较新的光标位置 - 建议结合命令模式下如下两个命令,可获得更好体验:
-
:marks
:显示全部mark -
:delmarks [mark]
:删除指定mark
-
编辑进阶
进入插入模式
在不同位置进入插入模式
-
i
:在光标前插入字符 -
I
:在行首插入字符 -
a
:在光标后插入字符 -
A
:在行尾插入字符 -
o
:在光标下发插入空行 -
O
:在光标上方插入空行
使用修改命令进入插入模式
-
c
:修改,后面需要接范围 -
c+w
:删除光标位置单词,并进入插入模式 -
c+l / s
:删除光标位置字符,并进入插入模式 -
c+c / S
:删除光标所在行,并进入插入模式 -
c+$ / C
:删除光标位置到行尾的字符,并进入插入模式
-
r
: 替换当前字符。 -
R
:(进入replace模式)持续替换光标所在字符,直到使用ESC退出替换模式。
删除
-
x
: 删除当前位置或下一个位置的字符。 -
d
:删除,属于动作指令,后面需要加操作类指令。比如如下命令:-
de
:删除到当前单词结尾。 -
dw
:删除到下一个单词开始。 - 注意,此处与
de
的区别在于,dw
会删除两个单词之间的空格。 -
daw
:删除一个单词,包含单词的边界(空格)。 -
d0
:删除至行首。 -
d$ / D
:删除至行尾。
-
-
da[
:删除[ ]整个块,包含符号本身; -
di[
:删除[ ]块,不包含符号本身; -
da/di +
' " { ( 等,也与接[类似,删除整个区块。唯一需要注意的,"和'仅仅在行内。 -
dt[x]
:在本行,删除到[x]。比如,dt"
删除到双引号,dtf
,删除到字母f
。d/foo
:在全文, 删除到 “foo” 。
剪切
剪切操作其实就是我们之前讲的删除。也就是d
。删除的内容,默认会存放到剪切板中。也就相当于进行了剪切。
进阶操作符
从这里大家可以看出,i
和a
的作用比较特殊,代表与区块相关的某种操作。区别就在于,i
不包含区块边界符号。a
包含。这两个操作符很重要,在后面的复制操作中还会用到。此外还有t
,/
。此外,i
和a
还可以接t
,此时t
表示一对xml标签。i
:区块,不包含边界。a
:区块,包含边界。t
:"to",本行到哪里。/
:接匹配,全文到哪里。
粘贴
-
p
:粘贴到光标后,或下一行。 -
P
:粘贴到光标前,或前一行。
为什么会有光标前后或上下一行两种情况呢?是因为我们复制或剪切的内容有可能是字符串或者整行:
- 当复制内容为字符串时,粘贴到光标前/后。
- 当复制内容为整行时,粘贴到上/下一行。
复制
-
y
,复制,属于操作,后面需要接动作来标识复制的范围。比如:-
yw
:复制到当前单词结尾。 -
ye
:从当前位置复制到本单词的最后一个字符。 -
y$
:复制到当前行尾。 -
yy
或Y
:复制当前行。 -
nyy
:复制从光标所在行起的n行,注意n在最前面。
-
0y$
: 命令意味着:
-
0
→ 先到行头 -
y
→ 从这里开始拷贝 -
$
→ 拷贝到本行最后一个字符
当然也可以结合我们刚刚介绍的进阶操作符来进行操作:
-
yi"
:复制两个引号之间 -
yit
:复制两个xml标签之间 -
y/[x]
:复制到x。
剪切板
vim 有 12 个剪切板,分别是 0、1、2、...、9、a、“、+。:reg
:查看各个剪切板里的内容。y
,p
默认使用 "剪切板中的内容。
"[n]y
:复制到剪切板n
中。"[n]p
:粘贴剪切板n
中的内容。
查看是否支持系统剪切板:
vim --version | grep "clipboard"
观看输出中,clipboard
前面是+
还是-
。若是-
,则说明不支持系统剪切板。
+
号剪切板比较特殊,是系统剪切板,用于与系统其他应用互动:
-
"+y
,将内容复制到系统剪切板,ctrl+v
将其粘贴到其他应用中,比如vs code
。 -
"+p
,将其他应用中复制的内容,粘贴到vim中。
可视模式
v
:进入可视模式。V
:进入行选择模式。Crtl + v
:进入块选择模式。
进入可视模式后,可以通过之前的移动操作,来进行选择。比如:hjkl
:前后左右选择。$
:选择到行尾。i"
:选择两个引号之间。
选择后,可以使用
-
d
进行删除/剪切, -
y
进行复制。 - 还可以使用以下很有意思的命令:
-
gU
:变大写。 -
gu
:变小写。 -
J
:把所有的行连接起来(变成一行)。 -
<
或>
:左右缩进。 -
=
:自动缩进 。
-
格式化
=
:调整格式化缩进。gg=G
:全文代码格式化。
-
gg
,到文章开头 -
=
,调整格式 -
G
,到文章结尾。
自动补全
编辑模式下Ctrl + n/p
出现提示,此时会出现补全的选项。按住Ctrl
不放,用n
和p
来遍历提示选项,到达期待的选项后,无需其他操作,继续输入即可。
撤销
-
u
:撤销前一个动作 -
U
:撤销当前行的一系列动作 -
CTRL-R
:Redo,意思就是我又不想撤销了。
查找替换
-
/
: 查找,此时Terminal左下角会出现/
,在后面输入想要查找的内容,回车即可。 -
?
:反向查找,同样道理,左下角会出现?
。 -
/[search]\c
:忽略大小写。比如:/test\c
,查找test,忽略大小写 -
n
: 下一个匹配 -
N
: 前一个匹配
命令模式下:
-
s/old/new/
:用new
替换old
-
s/old/new/g
:全局替换 -
set hlsearch
:高亮搜索结果
宏录制
qa
操作序列 q
, @a
, @@
-
qa
把你的操作记录在寄存器a。
- 于是
@a
会replay被录制的宏。 -
@@
是一个快捷键用来replay最新录制的宏。
命令
-
:w
:保存修改 -
:wq
:保存修改并退出 -
ZZ
:保存修改并退出 -
q!
:不保存修改,强制退出 -
e!
:不保存修改,强制重新打开当前文件
大家可以看到,
!
的作用便在于,强制。除此以外,他还有另一个很强势的功能,就是执行shell命令。具体信息,大家可以详细阅读下一节。
-
.
:重复执行前一个命令。这个命令很灵活、实用,建议多多尝试。 -
:help [command]
:查看某命令的help此外,在命令行中执行如下命令,便可进入vim的教程。
vimtutor
外部命令
这是vim
的一个很神奇的功能,在编辑的时候可以与外部文本互动,甚至执行一些shell
命令。
-
:w [file-name]
:将当前内容输出到指定文件中 -
:r [file-name]
:将另外一个文件的内容输出到当前位置 -
:e filename
:vim下打开指定文本
-
ctrl+w, s
:水平拆分窗口 -
ctrl+w, v
:垂直拆分窗口 -
ctrl+w, ARROW(h,j,k,l或方向键)
:在窗口间切换光标。 -
ctrl+w, w
:在窗口间切换光标。 -
:qa
:关闭所有窗口。
-
:saveas
:另存为。 -
:n/bn/bp
:在打开的多个文件间切换。
-
:![command]
:vim下执行某shell命令。 - 比如,
:!ls
,便会暂时切换到shell下,输出当前目录的文件名。此时输入回车,便可退回当前vim
编辑的文件中。
如果你觉得这种输入命令的方式还不够过瘾,vim
还提供了保留当前工作现场,直接进入shell的方式。这种命令一个典型的工作场景是,如我们编辑了一个文件,但是发现无法保存(没有写权限),此时可以先进入到shell下,执行类似chmod u+w [filename]
,的命令,为当前用户获取该文件的写权限,然后再回到 vim 保存刚刚的修改。 有如下两种方法:
-
:shell
或:sh
,当退出当前 shell 时(比如exit
),就会回到 vim。 -
ctr-z
进入 shell,fg
退回 vim。
Config
这部分主要是一些vim的config。可以直接命令模式输入,也可以保存到~/.vimrc
中,便可每次打开vim
自动应用。(其中一些命令是互相冲突的,请自行选择有用的命令)。
syntax on # 开启语法高亮
set nu[mber] # 显示行号
set nonu[mber] # 隐藏行号
set cursorline # 高亮当前行
set ruler # 显示光标位置信息
set noruler # 隐藏光标位置信息
set hlsearch # 高亮匹配
set nohlsearch # 取消高亮匹配
nohlsearch # 临时取消高亮(只取消一次查询的高亮)
set incsearch # 在输入字符串过程中显示匹配点
set nowrapscan # 找到文尾后停止查找
set wrapscan # 恢复为到文尾后自动从头开始
set ic/ignorecase) # 忽略大小写
set noic/noignorecase # 区分大小写</pre>
VIM思想
这部分主要是一些我在使用vim
过程中的一些思考和感悟,试图尽力阐述出来。如果大家能有一些思考和收获,说明我的思考是有意义的。如果大家有不同见解,十分欢迎拍砖交流。
Why Normal
- 为什么vim下,要放弃人们习惯的插入模式,使用命令模式呢?仔细想一想,其实原因很简单:在没有鼠标的年代,人们只能依靠键盘来移动光标,修改文本。
- 为什么现在有了鼠标,我们还要用正常模式呢?
- 工作内容覆盖。我们每个人都认为,工程师的工作,是写代码。然而,其实我们主要的工作,是读,或者说,理解代码。经调查,工程师日常工作中,读:写代码的比例,为10:1(参考《Clean Code》一书)。所以默认的普通模式,主要满足占比重更大的”读“;遇到需要修改的时候,再进入编辑模式。
- 大量快捷键。相信每个人,都最起码知道一组快捷键:
ctrl-c/v
,也就是我们熟悉的copy & paste. 如果你平时注重效率,养成了快捷键的习惯,还可能知道一些诸如ctrl-a/x/s/w
等。不知你注意到没有,如刚刚列举的很多快捷键,都由特殊的命令符+字母
构成。因为在此时,键盘上的大多数按键,都是可以输入到文本中的字符。而在vim
的正常模式下面,无法直接向文件中输入这些字符,相当于不用按ctrl
等特殊的命令符,直接可以把这些按键,用作命令的快捷键。
合理的快捷键
- vim中的快捷键,布局非常合理。根据使用频繁程度,调整距离手边的距离。比如,最基础的移动操作,放在手边的
HJKL
。虽然移动将手移动到键盘上的方向键,并未真正的浪费多少时间,但是其对思维的打断,其实非常影响效率。 - 快捷键的设置,也是非常合理,结合了单词的意义、读音,非常便于记忆。比如:
-
d
delete -
c
change -
w
word -
e
end -
b
back -
I
edit -
f
find -
r
replace
-
精细化,多维度命令
快捷键应有尽有,各个维度移动,都切合使用者思维,几乎可以做到”指哪儿打哪儿“。
比如,移动、删除、复制、等等操作,都可以结合精细化的位置,根据符合人类思维的不同维度,进行操作。
比如,字符,单词,行,文章,屏幕,匹配(位置、文字、符号),以及类似书签的Mark等。
原子、组合命令
vim
的大部分快捷键,都是原子操作,并通过与范围结合,排列组合,灵活多变,完成各种强大的功能。这也与unix
的主要思想契合:每个命令做好,且只做好同一件事。
与此同时,通过用数字和宏,代替无意义的重复。
此外,对一些常用操作,提供了现成的宏,方便操作。比如,dd
,是删除整行,同时也可以直接用D
来完成。I
,A
等,也是类似道理。
外部命令
类似栈的思路,可以放下当前操作,保存现场,然后进入另一个操作。当操作完成后,回到当前现场。
思维模式
vim
的快捷键,或者说命令,不仅很符合我们的思维,而且还能在很大程度上扩展我们的思维。
拿编辑代码时最多的操作,移动光标来说。原来我们的移动,基本就是通过键盘的方向键,上下左右,或者通过鼠标,移动到想要去的位置。而在vim中,你会发现,光标除了上下左右,还可以移动到词首,词尾,句首,句尾,行首,行尾,页面首部,页面中部,页面尾部,文档首部,文档尾部,文档任意一行,甚至还可以移动到某个指定字母,某个tag,匹配大、中、小括号。度过最初的不适应后,通过刻意练习和日常使用,肌肉形成记忆,便无需刻意回想是用什么命令,而是潜意识完成操作。掌握了这些命令后,当你使用原来的编辑器时,也会去寻找这些快捷键。这就不仅仅是使用vim时候提供效率了,而是通过提高编辑操作的意识、思想,提高了整体的工作效率。
使用vim一段时间后,我在其他工具中进行编辑时,编再也无法忍受,一个一个自读地移动光标。于是也会主动去找单词、行级别移动的快捷键。
- Mac系统
-
cmd + ←/→
移动到:当前行首/尾部 -
alt + ←/→
移动到:当前单词首/尾部
-
- iterm:
-
ctrl + f/b
前进/后退一个字符 -
Esc + f/b
前进/后退一个单词 -
ctrl + a/e
行首/行尾 -
ctrl + h/d
删除光标前/后一个字符 -
ctrl + w
删除光标前一个单词 -
ctrl + k/u
删除光标前/后所有内容 -
ctrl + y
粘贴之前删除的内容
-
后记
这篇文章到这里也就结束了,洋洋洒洒写了这么多,一次读下来就接受,很难;仅仅通过阅读就掌握,更难。想要真正用熟vim
,掌握思想,需要后续更多思考、实践。但是相信我,这些付出,一定是值得的。因为它不仅能让你掌握一个开发利器,更能带给你很有价值的思想。