Vim 的哲学第四篇姗姗来迟,狗血的缘由我就很少说了,好消息是我将为这个系列带来一些动态演示。本来我打算录视频的,可是文章都写了那么些篇了,如今再录视频彷佛晚了些,因此我研究了一下如何录制高质量的 GIF 动画(第三方软件都很差用,最后我仍是用 QuickTime 和一段脚原本完成录制,挺酷的~)。接下来先奉上第一弹:程序员
上一期的基础配置我遗漏了一个蛮重要的选项:shiftround
,这个选项真的很贴心很好用,遗憾的是官方文档对此语焉不详。我特地 Google 了一下才发现挺少有人解释这个选项的。如今我也忘记了当初我是怎么知道它的,而后我发现用文字也还挺难解释清楚,因此仍是看动画吧:正则表达式
round 在这里应该是取整的意思。当你的缩进不成倍时,开启这个选项将会让 Vim 自动帮你把周围的缩进化零为整,你就不须要手动去填/删空格了。顺便提早讲一下,缩进的指令是 <
或 >
键,它们支持移动指令(立刻讲到),也支持数字前缀。对当前行执行缩进是 <<
或 >>
,也就是连按两次。咱们在之后会详细介绍关于缩进的知识。编程
OK,加上它在你的基础配置里,咱们开始新的旅程!vim
如今咱们知道使用 Vim 的一个很重要的原则就是远离鼠标,远离可视化的定位装置(固然除了键盘之外),缘由我就再也不赘述了。在此原则之下,必然要有一些方式来帮助咱们选中目标而后作出咱们但愿的更改,也就是移动和编辑。segmentfault
在常规模式里有两个很重要的概念,一个叫作“动做”(Motions),另外一个叫作“操做”(Operators)。动做,是指你能让光标移动到哪里,而操做则须要结合动做来决定你能够对文本作什么。经过两者的结合,咱们能够逐渐体会 Vim 是如何贯彻保持简单这一原则的。编辑器
hjkl
虽然简单,可是在不少场合之下它们太没效率了,对么?从如今开始我但愿你记住:咱们一般不用 hjkl
作大量的光标移动。若是你发现你常常抽疯似的按这四个键在屏幕里来回移动,那你就已经错了!这四个键真正的主要用处是做为其余动做和操做的辅助键来使用的,随着咱们的深刻学习你会愈来愈理解这一点。工具
那么除此以外咱们还有什么选择呢?学习
:help left-right-motions
当咱们以行/段为单位来审视咱们的文本时,Vim 为咱们提供了一些横向移动的快捷动做。动画
移动指令 | 移动效果 |
---|---|
0 |
移动光标至行首 |
$ |
移动光标至行尾 |
^ |
移动光标至行首的第一个非空白符的字符 |
g_ |
移动光标至行尾的最后一个非空白符的字符 |
f{char} F{char} |
向前(右)或向后(左)移动光标至指定的字符({char} )处,光标停留在该字符之上 |
t{char} T{char} |
向前(右)或向后(左)移动光标至指定的字符({char} )处,光标停留在该字符前/后面 |
经过上面这个表格,咱们能够获取到如下信息:ui
这还没完,对于横向移动来讲,最麻烦的是当“回绕”(wrap)出现的时候。所谓回绕是指,当一行(段)的字符数目超过屏幕的可视宽度范围时,编辑器会将超出的部分自动转移到下一行来显示,可是这并不是换行,也就是说没有插入换行符,只是在显示上不让字符超出屏幕的最大宽度范围而已。
这个特性固然不是 Vim 特有的,几乎全部的编辑器都支持回绕,但是 Vim 对待回绕是和其余编辑器彻底不一样的,尤为是在编辑长文档时会让你以为有些古怪。我不会在本章详细介绍关于回绕的一切,由于本系列面向的读者主要是程序员。对于编写代码这样的工做,只要你遵循良好的编码规范(你应该这么作),那就不多会出现应对回绕的状况。因此目前为止你学会上述四个动做指令就足够了。将来的某一天我会专门写一篇如何打造专业的 Markdown 编辑功能,在那时候咱们再回过头来好好谈谈回绕。
如今我列出横向移动时若是遇到回绕行咱们能够作什么,这样你能够本身试一下:
移动指令 | 移动效果 |
---|---|
gh gj gk gl |
让光标在回绕行内作四方向移动 |
g0 |
移动光标至当前回绕行的行首 |
g$ |
移动光标至当前回绕行的行尾 |
g^ |
移动光标至当前回绕行的行首的第一个非空白符的字符 |
gm |
移动光标至当前回绕行的中间位置(或尽量接近中间的位置) |
g
键你或许已经注意到 g
键的屡次出现了,彷佛它和其余指令相互配合能够产生许多新指令。没错,在 Vim 中有那么几个“万能的”指令修饰键,g
是其中之一。若是你好奇还有多少指令是用 g
来修饰的,你能够键入 :help g
来查看一个列表。另外你应该知道这份列表实际上是索引文档的一部分,你能够时常打开索引(:help index.text
)来考察下本身对 Vim 到底有多熟悉。
:help up-down-motions
纵向移动的指令比较多,我仍是先介绍几个简单而且最有用的:
移动指令 | 移动效果 |
---|---|
gg |
让光标跳转到文档的最开始处 |
G |
让光标跳转到文档的最后一行 |
{count}G :{count} |
让光标跳转到指定的行号,即 {count} 所表明的行号 |
{count}% |
让光标跳转到指定的百分比位置,好比说第一行是 0%,最后一行是 100% 等 |
H |
让光标移动到当前屏幕的顶部(High Position),不滚屏 |
M |
让光标移动到当前屏幕的中部(Middle Position),不滚屏 |
L |
让光标移动到当前屏幕的底部(Low Position),不滚屏 |
{count}
是一个前缀标记,意思是这个指令前面能够追加数字,好比说若是你键入 25G
,那么光标就会移动到当前文档的第 25 行去。这个特性很是重要,Vim 的强大和灵活在很大程度上都仰仗相似的前缀特性。
G
和 {count}G
实际上是同一个指令,只是带上数字前缀与否会产生不一样的效果,因此我分开写了。这是第一次,一旦你了解了这个特色,之后我就不必分开了。
gg
和 G
是一对经典的搭档,许多很是有用的操做都是它们俩配合完成的。举个例子,《Vim 的哲学》全部的文字都是在Vim 里完成的,每一次到最后我都要把它们复制粘贴到 SegmentFault 博客的发表页面作最后的检查而且发布,如何所有复制过去?有不少办法,我通常选择以下两种:
command + a
,而后 "+y
或者 "*y
gg"+yG
,拆开看:gg
+ "+y
+ G
喂喂,command + a
是全选吧,这是 Vim 的指令吗?你别忽悠我喔~
我可没有说你只能用 Vim 的内置指令呀,了解你所处的环境,在不影响效率,不打乱节奏的前提下善于利用一切能够利用的工具,这不正是极客的特质之一吗?只不过我不是每次都有机会用到 MacVim 的,因此 gg
+ G
的经典组合仍是必需要掌握的。至于 "+y
,这个涉及到寄存器(:help registers
)的知识,咱们稍后就会讲到,别急。
:help word-motions
hjkl
是以字符为单位来移动的,横向和纵向移动基本上都是以句/段为单位来使用的,然而有些状况下咱们须要的是介于两者之间的移动单位,也就是以词为单位。以词为单位使得咱们能够更精确(也是更具语义化)的移动光标,而且要比逐个字符的移动要快得多。
这里所说的词特指的是像英语那样的以空格(有时候也会是别的符号)为分隔符的词——很遗憾,中文分词是很大的挑战,Vim 没有内置这一功能。像这样的词均可以分出词头和词尾,好比 word 这个词,词头是 w,词尾是 d。
因而在 Vim 中的词组移动也按照词分头尾的特性分红了两组:
移动指令 | 移动效果 |
---|---|
w b |
向前(右)或向后(左)移动光标至下一个词组的词头位置 |
e ge |
向前(右)或向后(左)移动光标至下一个词组的词尾位置 |
一些朋友喜欢寻找每个指令对应的含义,这样有助于形象记忆。事实上,这些含义也不是我编造出来的,内置的文档里都有很详细的描述,好比说上面这几个分别是:
w
:Words forwardb
:words Backwarde
:forward to the End of wordge
:backward to the End of word你可能在想:我干吗要关心词头和词尾,好麻烦啊!这个问题其实无关于 Vim 的哲学,而是语言的哲学。
你看,我们的汉语和英语是彻底不一样的两个语种。在汉语里词与词的界限是靠意义来划分,书写形式则不过重要。用汉语写一句话,你能够在词组之间添加空格或者不添加空格,基本上不会对读者产生影响(除了个别会产生歧义的特例)。而英文及其余相似语言则否则,它们加或不加空格的差异大了去了!一段英文若是没有空格,那几乎就是没法阅读的。所以,对于母语为相似语种的人群来讲,空格所划分出的词头与词尾是天然而然,司空见惯的事情,他们一点也不会以为奇怪。不幸的是,做为程序员的咱们也(被迫)得使用英语做为咱们的主要书写语言,所以你必须习惯去辨识和使用词头与词尾。一旦你习惯了,你会发现它们很是有用。
为了让你看到正确使用词头词尾的效果,我放一张图给你对比一下差异。在这张图里,我分别演示了四种操做:
w
)e
)b
)ge
)每一步操做以后,注意观察光标停留的位置和最终的效果:
这些结果或许是你指望的,也可能不是,但这没关系,没有哪种是绝对正确或错误的,重要的是你须要了解它们之间的差异,因而你能够在必要的时候选择正确的方式。
不过故事还没完,以上四个指令各自还有一个变体,分别是:W
,B
,E
,gE
。要了解它们的做用,咱们得先聊一下词的定界符。
对于词和词之间,空格是惟一的区隔标准吗?很显然不是。像这样的词:i_am_a_word
,Vim 会视为 1 个词,可是 i-am-a-word
,Vim 则会视为 7 个词!这是由于 Vim 容许你为其指定能够被视做词组定界符的字符,因而当 Vim 遇到这些字符的时候,就会认为是一个词的结束。默认状况下,_
不是定界符,因此它会被视做一个词的组成部分。
然而有些时候咱们但愿把这些定界符也看成词组的一部分,这样咱们能够移动的快速一点,这时大写版本的词组移动指令就派上用场了,它们永远都只把空白符(空格、TAB、EOL)视做词组的定界符。
你会以为本身定义定界符很酷吧?我会把它放在高级设置那一篇来说。
:help operator
Vim 内置了 15 个编辑指令(还有一些变体),可是通常来讲咱们用不到那么多。在本节咱们来学习其中的五种(共计 10 个):
:help d
若是你把光标对准某个字符,而后按下 d
(delete),你会发现什么都没有发生?不要惊讶,编辑操做是要配合移动指令来干活的,我以前花大力气介绍一堆移动指令不是漫无目的的不是?
OK,精彩的来了。当你按下 d
,Vim 会说:“好的伙计,你想要删除对吧?接下来请告诉我你要删什么?”
若是你要删一个词,按下 dw
,也就是 delete word。
若是你要删除两个词,按下 d2w
或者 2dw
,它们的效果是同样的,可是它们表明的含义略有差异:
d2w
意思是:删除 -> 2 个 -> 词2dw
意思是:2 次 -> 删除 -> 1 个词在这个例子里,两种操做的结果不会产生歧义,因此你能获得同样的效果。可是之后你会发如今某些特定的条件下,数字前缀在不同的地方会产生不同的效果(不仅局限于删除操做)。因此,正确的理解操做的含义是有必要的,请记住:
理解操做的含义,而不是背诵操做的顺序。就好像你说话说的是你想要表达的意思,而不是字词的某种排列组合。有些时候你颠倒字词的顺序不会影响你要表达的意思,由于不存在歧义,但有些时候则正好相反,切记切记!
若是我要删除一整行怎么办?简单:dd
。
那若是我想要从光标的位置开始一直删除到行结束呢?那还用我教你?d$
!不过 Vim 还有另一个版本等价于 d$
,它是:D
。
哦,那这么说若是我使用 0d$
或者 0D
,就是和 dd
等价的咯?
啊哈~聪明的童鞋,很抱歉你错了!可是不怪你,这是一个很重要的区别,咱们来单独看一下示范:
看明白了吗?其实差别是很是明显的,dd
是连同行尾的行结束符(EOL)一块儿删除的,因此粘贴的时候也会连着行结束符一块儿粘贴;而 0D
/0d$
则不会包含行结束符。
你可能还纳闷呢,不是演示删除的吗?为何删掉的东西还能再粘贴回来呢?嗯,可能删除这个词不太恰当,若是改叫剪切是否是突然就以为贴切起来了?
Vim 的删除操做和咱们常见的剪切很是类似,事实上 Vim 的删除指的是从你的眼前把目标文字移除到寄存器中,以后你还能够从寄存器里把删除的部分再粘贴回来。Vim 拥有一堆各式各样的寄存器,擅于使用寄存器可让你的编辑工做变得异常轻松。从此咱们会单独介绍寄存器的进阶使用。
让我来问你一个问题:若是要删除一个字符该怎么办?你或许已经了解到 x
能够删除光标所在的那个字符,X
能够删除光标左边的那个字符,可是你是否知道这两个功能也是从 d
演化出来的呢?试着找找答案吧。
:help c
在你执行完删除以后,紧接着按下 i
(insert),你就等于在改写以前删除的内容了。如何把这两步简化成一步?答案就是 c
(change)了。对于 c
,真的没什么好讲的,你学会了 d
就等于学会了 c
,由于 c
就等于 d
完了紧接着 i
而已。
并且其余相关联的操做也是相似的,好比说:
c2w
:改写两个词cl
:改写光标所在位置的字符,而且 s
等价于 cl
cc
:改写光标所在的一整行,而且 S
等价于 cc
c^
/c$
:从光标所在位置开始一直改写到行头/行尾,而且 C
等价于 c$
s
比较少用,由于一般改一个字符咱们会使用 r
,也就是替换(replace),可是 s
在编辑中文的时候有妙用,容我在这里卖个关子,等到打造专业 Markdown 编辑器的时候再说(实在是不能都说了,要否则这篇结束不了了)。
S
比 cc
多节省一次按键,并且也比较好按(在标准键位上),因此推荐用 S
来代替 cc
。
:help y
复制是几个基础编辑操做里怪癖略多的一个。首先是它的命名,y
是 yank 的首字母,可是 yank 又是什么?它和拷贝(复制)有什么关系呢?
这也和寄存器有关。你看,Vim 的复制/剪切(删除)/粘贴操做都是基于它底层的寄存器的,因为 c
已经被改写(change)占用了,而 Vim 的复制实质上是把目标文本拉拽(yank)到寄存器中备用,因此……好了你知道了就是了,咱不解释那么多,反正 y
就是复制了,爱咋咋地~
另一个怪癖出在 Y
身上,按照以前删除和改写的经验,你必定会认为 Y
就等同于 y$
呗,(Vim 乱入:“呵呵,图样图森破!你觉得我会让你这么轻易就掌握诀窍吗,少年?”)但是很不幸你又错了。这一回,Y
又和 yy
等价了……
我一直都没闹清楚为何到了复制这里就和删除/改写不同了,就连官方的帮助文档都是这么说的:
若是你但愿
Y
是从光标处复制到行尾(这样更合乎逻辑,不过不兼容 Vi),你可使用:map Y y$
看起来惟一的缘由就是为了和老 Vi 兼容,可是咱们彻底不在意这一点!后面的 :map Y y$
是键位映射,虽然咱们还没讲到,不过这一句你已经能够把它放到你的 .vimrc
里了,重启 Vim 以后你会发现 Y
的表现和 C
D
它们保持一致了,谢天谢地!
:help p
粘贴就单纯多了,只有两个指令:
操做指令 | 移动效果 |
---|---|
p |
自光标所在位置向右粘贴默认寄存器里的内容 |
P |
自光标所在位置向左粘贴默认寄存器里的内容 |
默认寄存器里的内容取决于你在粘贴前最后的编辑动做,有多是删除或复制的一段文本,也有多是其余的。因为咱们尚未详细介绍强大的寄存器功能,你或许会偶尔感到有些不便,在这里我先介绍一个最经常使用的技巧:
有时候,咱们须要完成以下操做:
你看,这其实是要用 A 处的文原本替换 B 处的文本,但因为默认寄存器只保留了最后一次的复制/删除(剪切)内容,因此当你完成第 2 步的时候,你在第 1 步准备好的文本已经没了……大多数人是这么作的:
实际上,咱们可让 Vim 不把指定的内容放入默认寄存器,这样就不会覆盖预先准备好的内容了,这等同于完全删除而不是剪切。Vim 的默认寄存器是 ""
(也叫匿名寄存器,:h quotequote
),它保存常规的复制/删除等操做的内容,Vim 还有一个名字很酷的寄存器叫作:黑洞寄存器(Blackhole Register,:h quote_
),它的按键是 "_
。若是你在键入任何操做以前先输入 "_
,操做的结果将不会被任何寄存器保留下来,就好像丢入了一个深渊黑洞,再也回不来了……(好伤感 T_T)
所以,咱们能够这么玩:
"_dd
我把这个过程也录了下来,你能够对照看看:
我真是爱死这玩意儿了!不过你要知道,就上例而言黑洞寄存器不是惟一的办法,说不定你更喜欢别的操做组合,好比下面这个:
这一套“组合拳”没有用黑洞寄存器,它的好处是若是我反悔了,我还能够撤销以前的操做把被替换的内容找回来。整个过程的按键顺序是这样的:y$
-> gt
-> gP
-> D
。
请容许我用更加具备语义的方式来重复一遍上面的操做:
y$
:从光标所在位置(行首)复制到行尾(不包括换行符)gt
:切换至下一个标签页gP
:自光标位置向左粘贴刚才复制的内容,结束以后把光标向右移动一个字符(这就是 g
的做用,为了把末尾的 .
保留住。你也可使用 Pl
实现同样的目标)D
:自光标位置删除到行尾我但愿你理解我这样重复一遍的缘由,它包含了体现 Vim 哲学的三个侧面:
好吧,第三点纯粹是我在胡扯,哈哈。
大小写转换其实不算什么大事,原本我也犹豫还要不要介绍一下,可是考虑到这个在编程的时候还挺有用的,因而索性一并说了吧,反正也很少……
操做指令 | 移动效果 |
---|---|
~ |
转换光标所在字符的大小写(严格来讲,这不是一个操做指令) |
g~ |
转换字符的大小写(这个才真的是) |
gu |
强制转换成小写 |
gU |
强制转换成大写 |
解释一下头两个,~
不是操做指令,是由于它没办法和移动指令结合,它就只会转换当前光标所在位置的那个字符。若是你有多个字符须要转换,你就只能一个一个按过去。g~
才是转换大小写的正式版,它能够结合移动指令。比方说按下 g~3j
会把往下 3 行的字符大小写都转换了(小写变大写,大写变小写)。不过 Vim 有一个选项叫作 tildeop
(:h tildeop),它默认是关闭的,若是你开启它,~
就会变成和 g~
同样了。这选项我记得很熟,由于我常常在团队里作重构工做,这种改写命名的活儿一再重复,我索性就把 ~
变成真正的操做指令了。
另外,毫无心外的,它们几个都有直接操做当前一整行的快捷版本,分别是:g~~
guu
gUU
。
趣味知识:你知道 ROT13 加密编码吗?这多是世界上最简单的加密手段了,有意思的是 Vim 也内置了 ROT13 编码/解码功能。闲来无事的时候能够拿来逗别人玩哦!切换 ROT13 编码/解码的操做指令是:
g?
(:help g?)
操做指令 | 移动效果 |
---|---|
gq |
自动应用排版规则 |
gw |
自动应用排版规则(光标位置不变) |
= |
自动应用缩进规则 |
< > |
手动应用缩进规则(左右两个方向) |
前面两个在编写代码时不太经常使用,却是在编写文档时能发挥做用,所以它们不是重点,请自行查阅文档并尝试。
后面两个就比较经常使用了,所谓“自动应用缩进规则”,前提是你得有可用的缩进规则。Vim 内置了很是多种语言的缩进规则,那些没有内置的也基本上均可以在网上找到合适的缩进规则插件。此前咱们也在基础设置里打开了 filetype indent on
,因此此时若是你打开一份源码文件,而后按下 gg=G
,“唰”的一下——整个世界清静了。
还记得吧?gg
是去文件的最开始处,G
则是去文件的最后一行,因此这条命令的含义是:“从文件的开始处应用自动缩进规则直到文件的最后一行”。固然你能够没必要老是对整个文件进行自动缩进,以前咱们提到过的移动指令均可以搭配使用,以后咱们还要介绍更增强大灵活的文本对象选择指令,搭配上自动缩进那叫一个如虎添翼~
至于 <
和 >
就没什么新鲜的了,手动缩进呗!缩进的宽度是由 shiftwidth
指定的,我们上次已经设置过了的有木有?另外它们也有针对当前行的快捷版本,你已经知道了,是吧?
想知道你的 Vim 内置了那些语言的缩进规则?键入这条命令:
:e $VIMRUNTIM/indent
你如今能够尝试把这些最经常使用的指令应用在你的平常工做里了,若是你能坚持去寻找最有效率的操做方式,你终将会明白其实根本用不着装太多的插件。我很乐意进一步帮助你,因此你若是在使用中有任何疑问请不要客气尽管询问我,我也喜欢看看有什么新的挑战,因此来吧~
其实,第四篇原本想直接讲文本对象的,可是我担忧新手会看不太懂,因而把文本对象一再日后挤。挤到如今才发现,天啊!这篇太长了,实在是不能再继续下去了。因而,咱们只好对文本对象说拜拜了~我们下期再见!