vim产生的背景
ed 是最初的 Unix 文本编辑器,它编写于图形显示器很稀有的年代,那时源代码通常是打印在纸带上,并在电传终端机1上进行编辑。在终端上输入的命令被送到大型机上进行处理,每条命令的输出会被打印出来。在那个年代,从终端到大型机之间的连接很慢,以至于一个快速打字员比网络还快,他们输入命令的速度要比命令被发出去处理更快。在这种情况下,ed 能够提供一个简洁的语法变得异常重要,不过ed 是一个行编辑器,每次只能处理一行,使用起来很不方面。可是在70年代,因为传输速率慢,所以这种简洁的行模式编辑就很有意义。
ed 历经了几代的改进,包括 em (意为“editor for mortals”,即“人类的编辑器”)、 en,最终到 ex2。此时图形显示器已经比较普及了,ex 增加了一个把终端屏幕设置成交互窗口的功能,并在窗口内显示文件的内容。这样,在做修改时实时看到变化成为了可能。 此屏幕编辑模式由 :visual 命令激活,其简写为 :vi,这即是 vi 这个名字的由来。
vim 代表改进版的 vi (vi improved),然而这只是一种谦虚的说法。通过查阅 :h vi-differences ,我们可以看到 Vim 支持而 vi 不支持的功能列表。Vim 对功能的增强是必要的,但另一方面它却仍继承了大量的遗产。这些指导 Vim 先祖们设计的约束,给我们提供了一个非常高效的命令集,这在今天依然很有价值。
vim与vi相比,增加了更多的特性,如彩色与高亮显示,可以使我们的编辑工作更轻松。通过设置,vim会自动检测文件中内容的类型,并以不同的颜色进行高亮显示,如注释变成蓝色,关键字变成褐色,而字符串变成红色等,与vi传统的黑白显示模式相比,vim更易读易用。在vim中,我们还可以使用多窗口显示,在一个屏幕中同时对多个文件进行操作。我们还可以通过.vimrc文件定制的方法,来使用户在打开vi的时候获得自己熟悉的和适用于自己特殊目的的环境
为什么要学习vim
Vim是一种有30年历史的古老编辑器,为什么现在还收到如此的青睐?现在各种编辑语言都有自己各种各样编辑环境,为什么还是有很多人在使用vim?针对对这些问题的思考,能让我们不断理解vim的核心竞争力,主要如下:
Vim经过了半世纪的积累,详情可搜索:「How Did Vim Become So Popular」,这个几乎集成了近半世纪智慧与努力的Vim,将大量的精力都放在了兼容性上,兼容性强到几乎兼容了所有你能想到的东西。
强大的键位设计,自行设定快捷键等这些都让Vim成为提升工作效率的绝对利器呀!Vim诞生的时候,鼠标还不是电脑标配,所以Vim尽量为纯键盘操作而优化。而后来图形化界面、鼠标的普及,让更过代码编辑器脱颖而出,配合鼠标操作学习门槛低,但效率不及Vim老手。这也是Vim让人又爱又恨的原因吧。
Vim 是一个 高度可定制、可扩展的编辑器。在定制 Vim 时,你大多不用什么特殊工具,用 Vim 本身就可以。如果需要更强大的扩展能力,还可以用 Python、Perl、Ruby、Tcl 等其他通用脚本语言,或直接调用外部命令,很容易打造一个专属的开发环境,或把这个环境从一台机器转移到另一台机器上。
Vim有着良好的生态环境。网上可以找到大量现成脚本和插件,帮你打造一个顺手的开发环境。像语法检查、自动补全等程序员常用功能,全都可以在 Vim 里实现。不需要离开 Vim,就能完成从写代码、编译到运行的大部分工作。
Vim 这些特性可以让你快速搭建一个高效/跨平台/易扩展的开发环境,随让vim命令众多,掌握需要花一段时间,但绝对不会浪费,熟练到一定程度,就是一件高效的利器,拿来即用,快意编程。
vim使用技巧和心得
一键简化重复
用“. ”命令可以让我们重复上次的修改,它是Vim中最为强大的多面手. 要理解 . 命令的强大,我们需要意识到这一点:“上次修改”可以指很多东西,一次修改的单位可以是字符、整行,甚至整个文件。比如:
x 命令会删除光标下的字符,在这种情况下使用 . 命令“重复上次修改”时,就 会让 Vim 删除光标下的字符 。d 命令也做删除操作,但它会把整行一起删掉。如果在 dd 后使用 . 命令,那么“重复上次修改”会让 Vim 删除当前。 >G 命令会增加从当前行到文档末尾处的缩进层级。如果我们在此命令后使 用 . 命令,那么“重复上次修改”会让 Vim 增加从当前行到文档末尾的缩进层级
x、dd 以及 > 命令都是在普通模式中执行的命令,不过,每次我们进入插入模式 时,也会形成一次修改。从进入插入模式的那一刻起(例如,输入 i),直到返回普通 模式时为止(输入),Vim 会记录每一个按键操作。做出这样一个修改后再用 . 命 令的话,它将会重新执行所有这些按键操作。
重置ESC键位
重置ESC,在使用vim过程中,需要经常在不同的模式之间切换,不停的用到ESC键,ESC键在键盘的最左上角,距离常用字符较远,但为什么设计使用ESC键呢,原因是原来的键盘按键和我们现在的布局不同,ESC键就在TAB键的位置,距常用按键很近,所以很方便操作。对于目前的键盘ESC键距常用字符较远,不容易快速操作,而且容易出错,为提高操作的效率,需要重置ESC键。由于中英文切换键,对作者来说不太经常使用,所以重置成了ESC键,把中文切换映射成ctrl+space,按键的重置要考虑个人使用习惯,尽量不要映射经常使用的按键。刚开始可能不太适应,一段时间后,你会发现操作命令的效率将会成本的提高。
快速移动和跳转
hjkl移动光标
vim使用 h、j、k、l 键来移动光标。这些键的用途为:
h 左移一列
l 右移一列
j 下移一行
k 上移一行
如果你发现自己很难改掉使用光标键的习惯,那么可以试着把以下几行加到 你的 vimrc 里:
noremap <Up> <Nop>
noremap <Down> <Nop>
noremap <Left> <Nop>
noremap <Right> <Nop>
这几行会把光标键映射为什么都不做。这样,你每次移动手去够光标键时, 就会受到提醒:你应该让手停留在本位行上。如此一来,用不了多久你就会进入 状态,开始用 h、j、k 及 l 了。
hjkl每次只能移动一次光标,速度有点慢,能不能一次移动多个光标呢?当然可以,我们可以映射hjkl按键,配置如下:
noremap h 5h
noremap j 5j
noremap k 5k
noremap l 5l
noremap H 10h
noremap J 10j
noremap K 10k
noremap L 10l
hjkl按钮分别映射成原来操作的5倍,也就是说j原来向下移动1行,映射后一次移动5行,l原来右移1列,映射后一次右移5列。同时我把HJKL键映射成了原来的10倍,这样不同的场景使用不同的按键,加快移动效率。
基于单词/字串的移动
理解单词与字串
一个单词由字母、数字、下划线,或其他非空白字符的序列组成,单词间以空白 字符分隔(参见 :h word )。而字串的定义则更简单,它由非空白字符序列组成, 字串间以空白字符分隔。如:e.g. we're going too slow。上例中包含了 5 个字串 及 10 个单词。句号及单引号都被当成了单词。
对于单词的操作
w 正向移动到下一单词的开头
b 反向移动到当前单词/上一单词的开头
e 正向移动到当前单词/下一单词的结尾
ge 反向移动到上一单词的结尾
对于字串的操作
W 正向移动到下一字串的开头
B 反向移动到当前字串/上一字串的开头
E 正向移动到当前字串/下一字串的结尾
gE 反向移动到上一字串的结尾
单词移动
例如:we're going _&too $slow,
要想移动到slow,如果依靠原生的hjkl,需要很多次移动,而如果用w命令,仅需要8次w命令即可。
还能再快么?能,就是字串移动
字串移动
执行W命令只需要3次即可移动到$,再执行一次l命令,也就是仅需要执行4次命令。
对于单词和字串的理解,不仅能提高移动光标的效率,而且也能解决对单词和字串的操作上的疑惑,例如:$test = new array();当我们光标停在$上面,本想删除$test,执行ciw命令,结果只是把$给干掉了,而如果我们理解的了单词和字串的区别,就不会对ciw命令的结果感到疑惑。要想删除$test ,直接执行ciW。
查找
f{char} 命令是在 Vim 中移动的最快方式之一。它会在光标位置与当前行行尾 之间查找指定的字符,如果找到了,就会把光标移到此字符上;如果未找到,则保持光标不动(参见:hf )。听起来似乎比较复杂,但在实际操作中非常简单。相关的命令操作如下:
f{char} 正向移动到下一个 {char} 所在之处
F{char} 反向移动到上一个 {char} 所在之处
t{char} 正向移动到下一个 {char} 所在之处的前一个字符上
T{char} 反向移动到上一个 {char} 所在之处的后一个字符上; ,
; 重复上次的字符查找命令
, 反转方向查找上次的字符查找命令
可以把 t{char} 及 T{char} 命令当成“直到查找到指定的字符为止的命令,它们使光标停留在{char} 前面的那个字符上,而 f{char} 和 F{char} 命令则把光标移动到指定字符上,在业务中 f{char} 和 F{char}一般就可以满足需求了,为什么还需要 t{char} 及 T{char}呢,请看下面的使用场景:
$base = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
我想删除等号后面的数据,先执行f',再删除从此处到句尾的所有文本,但又不想删除分号,此时可以用 dt; 命令完成这项工作。
构造可撤销的单元
u 键会触发撤销命令,它会撤销最新的修改。一次修改可以是改变文档内文本的 任意操作,其中包括在普通模式、可视模式以及命令行模式中所触发的命令,而且一 次修改也包括了在插入模式中输入(或删除)的文本,因此我们也可以说,i{insert some text} 是一次修改。
在不区分模式的文本编辑器中,输入一些单词后使用撤销命令,有两种可能。一 种是它可能会撤销最后输入的字符;另一种做得更好点,它可能会把字符分成块,使 每次撤销操作删除一个单词而不是一个字符。在 Vim 中,我们自己可以控制撤销命令的粒度。从进入插入模式开始,直到返回普通模式为止,在此期间输入或删除的任何内容都被当成一次修改。因此,只要我们控制好对键的使用,就可使撤销命令作用于单词、句子或段落。
操作符+动作命令
操作符命令如下:
c c change
d d delete
y y yank into register (does not change the text)
~ ~ swap case (only if'tildeop' is set)
g~ g~ swap case
gu gu make lowercase
gU gU make uppercase
! ! filter through an external program
= = filter through'equalprg' or C-indenting if empty
gq gq textformatting
|gw| gw text formatting with no cursor movement
g? g? ROT13 encoding
> > shift right
< < shift left
zf zf define a fold
g@ g@ call function set with the'operatorfunc' option
Vim 的强大很大程度上源自操作符与动作命令相结合,操作符与动作命令的结合形成了一种语法。这种语法的第一条规则很简单,即一 个操作由一个操作符,后面跟一个动作命令组成。学习新的动作命令及操作符,就像 是在学习 Vim 的词汇一样。如果掌握了这一简单的语法规则,在词汇量增长时,就能 表达更多的想法。假如我们已经知道如何用 daw 删除一个单词,然后又学到 gU 命令(参见 :h gU )。 它也是个操作符,所以我们可以用 gUaw 把当前单词转换成大写形式。如果我们的词汇进一步扩充,学会了作用于段落的 ap 动作命令,就会发现我们可以进行两个新的操作:用 dap 删除整个段落,或者用 gUap 把整段文字转换为大写。
关于操作符+动作命令的具体使用,我会在介绍其他技巧时,贯彻其中,在实战中感受其的巨大魅力。
a/i
Vim 的文本对象由两个字符组成,第一个字符永远是 i 或是 a。我们一般说, 以 i 开头的文本对象会选择分隔符内部的文本,而以 a 开头的文本对象则会选择包括分隔符在内的整个文本。为了便于记忆,可以把 i 想成“inside”,而把 a 想成“around” 或“all”。比如文本,光标在双引号内,执行vi"可全部选中双引号内部文本,执行va",可全部选中双引号+内部文本。
i或a产生的魅力是无论你的光标在操作范围内的任何位置,都可以完全覆盖。这个特点使我们没必要移动到文本的开头或结尾,就可执行操作,非常便利。
部分 Vim 内置的分割符文本对象:
a) 或ab 一对圆括号 (parentheses)
i) 或ib 圆括号 (parentheses) 内部
a} 或aB 一对花括号 {braces}
i} 或iB 花括号 {braces} 内部
a] 一对方括号 [brackets]
i] 方括号 [brackets] 内部
a> 一对尖括号 <angle brackets>
i> 尖括号 <angle brackets> 内部
a' 一对单引号 'single quotes'
i' 单引号 'single quotes' 内部
a" 一对双引号 "double quotes"
i" 双引号 "double quotes" 内部
a` 一对反引号 `backticks`
i` 反引号 `backticks` 内部
at 一对 XML 标签 <xml>tags</xml>
it XML 标签内部
范围文本对象
iw 当前单词
aw 当前单词及一个空格
iW 当前字串
aW 当前字串及一个空格
is 当前句子
as 当前句子及一个空格
ip 当前段落
ap 当前段落及一个空行
在我们实际编程过程中,经常会出现删除分隔符内的内容,比如
public function AA($a,$b,$c){...}
如果要删除()中的所有参数,可以执行f(,查找到(,再执行ci)命令即可,非常方便。
选中批量操作
Vim 的可视模式允许我们选中一块文本区域并在其上进行操作,表面看这应该很 容易理解,因为大多数编辑软件都沿用此模式。然而,Vim的可视模式和其他软件的 做法却截然不同.Vim 具有 3 种不同的可视模式,分别用于操作字符文本、行文本或块文本。
激活可视模式
v 键是通往可视模式的大门。在普通模式下,按 v 可激活面向字符的可视模式, 按 V(v 和 Shift 键一起按)可激活面向行的可视模式,而按(v 和 Ctrl 键一起 按)则可激活面向列块的可视模式,请参见下表中的汇总:
v 激活面向字符的可视模式
V 激活面向行的可视模式
<C-v> 激活面向列块的可视模式
gv 重选上次的高亮选区
gv 命令是个有用的快捷键,它用来重选上一次由可视模式所选择的文本范围。不 管上个选区是面向字符的、面向行的,或是面向列块的,gv 命令都能够正确地工作。
批量修改文本
批量修改文本
比如,打开一个文本,我需要将30-35行快速加注释符号#,先按Esc 再Ctrl+v 进入可视块模式
定位到30行开始的位置使用方向键向下一直到35行
这时选中的地方会有阴影,然后按大写的 I (读 i)进入插入模式
在光标位置插入#符号
然后按ESC,30-35就全部加上注释符号了。
在一行或多个行上执行命令
有时使用Ex 命令,能比用普通模式命令更快地完成同样的工作。举个例子,普 通模式命令一般操作当前字符或当前行,而 Ex 命令却可以在任意位置执行,这意味 着我们无需移动光标就可以使用 Ex 命令做出修改。但使 Ex 命令脱颖而出的最让人 赞叹的功能,是它们拥有能够在多行上同时执行的能力。
一般地说,Ex 命令操作范围更大,并且能够在一次执行中修改多行;或者可以 进一步精炼为 Ex 命令影响范围广且距离远。
批量替换
:%s/aaa/bbb/g 表示把整个文档的内容中的aaa替换成bbb
回溯历史命令
Vim 会记录命令行模式下的命令历史,并且可以很容易回溯之前的命令,因此对于比较长的 Ex 命令来说,我们用不着在命令行中多次输入它.我们先按 : 键切换到命令行模式,在保持提示符为空的情况下按 <Up> 键,此 时最后执行的那条 Ex 命令就会被填充到命令行上。再接着按 <Up> 键的话,就可以回到更早的 Ex 历史命令;按 <Down> 键的话,则会沿相反方向滚动。<Up>和<Down> 键距离常用键较远,我们可以用快捷键 <c-p> <c-n>代替,效率更高。
总结
vim的命令较多,而正因为此,其提供的功能十分强大。随着在开发中不断应用,能够总结出一些实用技巧,提高我们的开发效率。vim的命令虽然繁杂,但是我们没必要全部掌握(当然越多越好),根据二八原则和实战经验,我们只需要熟悉掌握20%的关键命令,即可完成80%的功能,感觉还是熟练应用和掌握其命令的规律。
vim内置命令只是基础,但十分重要。要想实现更加复杂的功能,我们要善用插件,vim提供了很多丰富的插件机制,安装教程网上资料众多,就不在此赘述。插件极大的提供了vim的扩展能力,使用插件可以把vim打造成一款个性化的超级编辑器。
如果你是一个追求极致的人,想要个性化打造属于自己的vim编辑器,建议学习vimscrpt语言,实现vim定制化开发。