一个完美的输入场景应该是这样:
- 打开一个编辑器
- 行云流水般的输入,没有删除没有修改,每一步都完美、风骚!
- 保存关闭
显然这样的人用Gedit/Emacs/Vim效果都是一样(Vim还要慢一些,因为初始状态为Normal模式)。所以,我想说的是软件有它的应用场景,Vim也不例外。这样完美的输入场景应该不存在,还记得看到过这样一句话:
The best writing is rewriting. ― E.B. White
所以编辑时就 一定 会涉及到修改行为。Vim就是让你能快速的进行移动/删除/替换等编辑行为。
同一个键盘要做两样事情,行为与输入正文。怎样区分你想输入的是告诉Vim的行为还是输入的正文? Vim引入了模式之分, 简言之,在一个模式下你的键盘输入会当作命令,这个模式官方名称为Normal模式,对应的就是Insert模式,在这个模式之中的所有键盘输入都会被当作文本的追加。怎样在两者之间进行切换? ESC
进入Normal模式, 进入Insert模式有很多,最基本是 i
。下文会详细叙述。你也可以做这样一种想象,Normal模式让好像是在编辑内容上盖了一层 透明薄膜 ,你的输入都是最先被这层薄膜截获,并对应某种行为,这种行为的结果要么是改变当前光标的位置,为你的输入作准备,要么是改变了你所编辑的文本内容。在这层薄膜上最大的好处就是可以方便的在文档中游走。 Emacs的做法是通过 Ctrl
Meta
辅助作为快捷键操作。这是另外一种思路。
- 接口与实现 这是IT里不厌其繁所讲到的,这里不涉及到编程语言的API,我想说的是不论VIM也好Emacs也好其实都只是实现不同而已,那接口在哪里?在你的大脑中,你的意念就是接口,说白了不论你是VIMer也好Emacser也好,首先你下手之前已经在你的大脑里有某种意念,只是下手之时键盘映射不一样而已。当然这里我忽略了局限性,即软件的差异固然会造成你思考的某种方式的差异。你在用某种软件时相辅相成的相互作用的过程会 极大 地影响你的思考模式。好比手里拿个锤子看什么就像钉子一样。所以下文对Vim的介绍都是以Intent和Vim键映射这样的列表展开。
- 编辑文本的组成: 从小到大,字符/单词/句子/段落/文件。
编辑过程中不可避免的要移动光标,这个时候,各个编辑器的差距就全出来了。一般Vim的介绍文章上来就是 hjkl
,我决定按移动步伐从小到大的顺序来介绍。见下表:
Intent | Vim key map |
---|---|
左移/右移一个字符 | h l |
以单词顺序移动向后/向前/一个单词 | w b e |
以空格为区分的下一个element | W B E |
上一行/下一行 | j k |
上一句/下一句 | ( ) |
上一段/下一段 | { } |
当前显示内容的head middle bottom | H M L |
当前显示内容从顶端数第三行 | 3H |
当前显示内容从底端倒数第三行 | 3L |
翻页,文件尾部方向头部方向翻页 | C-f C-b |
到文件顶端 | gg |
到文件末端 | G |
直到你编辑的是另外一个文件,打开另一个文件 | :e anotherfile.txt |
顺便唠叨一句,不要用方向键来使用Vim或者Emacs,因为我始终认为这样做的人没有领略到任何一个编辑器的精髓:让你手不离开主键盘区去做任何编辑的事情(Emacs要把编辑两字去掉更适合)。翻页我只列出了两个,其实还有翻半页,翻一行。这些应用的不是很多,所以就没有列出来。 精确定位,除了上表的移动外,Vim还可以精确的定位到某一行,甚至行内定位到某一个字符,下表详细说明
Intent | Vim Key Map |
---|---|
到文本的50%处 | 50% |
到文本的30行 | 30G |
从当前光标出开始往行尾方向找到第一个字符a处 | fa |
继续? | ; |
从当前光标开始往行首方向找到第一个字符a处 | Fa |
继续? | , |
至此,移动光标的步伐介绍大致如此,其实许多时候我都是用 f
, <c-f><c-b>
, HML
配合 jk
来实现文档内的快速定位。虽然看起来两个表格列在这里看过跟没看过没多大差别,但真正你完完全全领略到上面所有键组合的威力你就会领略到Vim的强大。这里再提一个插件: easymotion,可以理解为 f
的增强版。
这是我认为Vim最为强大的地方,我认为Vim在这方面丝毫不逊于Emacs。所谓编辑上下文,就是你处在编辑的一个过程当中,你所需要做的下一个步骤Vim都有帮你做键盘映射。没有任何动鼠标的意念。
时空局部性原理–计算机程序从时间和空间都表现出“局部性”:
时间的局部性(Temporal Locality):最近被访问的内存内容(指令或数据)很快还会被访问;
空间的局部性(Spatial Locality):靠近当前正在被访问内存的内存内容很快也会被访问。
–《计算机体系结构量化研究方法》
同样我觉得编辑过程也存在这种局部性原理。下面的列表就是从局部性出发,设想你在一个编辑环境中所能做的事情。
Intent | Vim Key Map |
---|---|
到行首 | 0 |
到行尾 | $ |
在行首添加内容 | I |
在行尾添加内容 | A |
删除光标所在的行 | dd |
删除光标到行尾的内容 | d$ D |
删除光标所在的行并且在这行位置添加新内容 | S |
删除光标所在位置到行尾内容并且置Vim为Insert模式待输入正文 | C |
复制当前行 | yy |
粘贴到光标之后/之前 | p P |
删除光标所在的字符 | x |
删除光标所在的单词 | dw |
删除光标所在的单词及后一个单词 | d2w |
删除单词并置Vim为Insert模式 | cw |
将一个字符替换为另一个字符如s | rs |
将后续输入替换当前内容,不改变Vim模式 | R |
改变字符的大小写 | ~ |
连接2行 | J |
重复执行上一次命令一次 | . |
我平时用到的就这么多了。
前面已经有提到过 yy
p
P
。这里补充的是对复制的内容的选择和将内容写到Vim寄存器中的方法。
在Normal模式下按 v
键会进入之前没有提及的模式,visual模式,这个模式用来选中文本用以删除或是复制。按 v
键后用移动光标的快捷键后会选中按 v
之前光标所在位置到当前光标位置的内容,然后可以用过 y
来复制,或者 d
来删除。如果想分别粘贴不同的内容就需要将不同的内容保存起来了。可以通过Vim寄存器来实现,Vim默认的10个寄存器,寄存器名为0~9 会按时间顺序保存删除或者是复制的内容,但我记不住哪个里面放什么内容,所以,通常都是显示的将一个内容放入指定的寄存器中,操作也简单,在之前所有复制粘贴操作之前加 "寄存器名
即可。比如 "ayy
复制当前行到寄存器a中, "ap
粘贴a中的内容到光标后。
查找和替换主要有以下几种方法
Intent | Vim Key Map |
---|---|
从光标当前位置向文件尾方向查找某关键字 | /keyword |
从管表当前位置向文件头方向查找某关键字 | ?keyword |
查询光标当前位置的单词(往文件尾方向) | #(文件头方向) * (文件尾方向) |
全文替换每行出现的第一个单词 | :s/one/two/ |
全文替换全部 | :s/one/two/g |
区间替换 第一行到第20行之间 | :1,20s/one/two/ |
替换内容含有’/’这个字符可以这样方便一些 | :s#/one#/home#g |
区间替换 当前行至下三行 | :.,+3s/one/two/ |
全文正则删除带关键字的行 | :g/one/d |
带关键字的行全部搬到文件尾 | :g/one/m$ |
将文本倒序 | :g/^/m0 |
保留带关键字的行,其余删除 | :g!/one/d |
宏是对机械化编辑的一种固化,就像脚本是一坨命令一样,宏也是一坨命令,不过是Vim命令。
个人不推荐宏,当编辑文本行数为10000+时,宏的速度直接被sed完爆甚至有点不可接受,还是用sed吧。
Intent | Vim key map |
---|---|
如何录制宏 | qayourlogichereq a是宏的名字 开始和结束都是q作标记。 |
如何执行宏 | @a |
Intent | Vim key map |
---|---|
保存 | :w |
另存为 | :w another.txt |
追加到某文件尾部 | :w >> another.txt |
保存文件某一部分 | :1,20w another.txt |
Intent | key |
---|---|
Vim中察看manpage | K (蛋疼) |
跳到local变量的定义处 | gd |
跳到global变量的定义处 | gD |
将数字加1 | <c-a> |
将数字减1 | <c-x> |
Esc的替代 | <c-o>接nomal命令 |
察看当前字符的ascii值 | ga |
进入ex mode | Q |
展开全部folder | zR |
收缩全部folder | zM |
展开当前folder | za |
Intent | Key |
---|---|
选择双引号之间的内容 | vi" |
选择双引号之间的内容包括双引号 | va" |
进入Visual模式的三种方式 | v V Ctrl-v |
Visual模式中跳到选中内容另一端 | o |
直接删除一个单词,不管光标在这个单词的哪个位置 | daw diw |
打开Vim直接在insert模式下 | Vim -c ‘startinsert’ |
用Vim-surround去插入删除括号(增加函数调用) | 仔细阅读Vim-surround doc |
上/下一个空格行 | ’{’ ‘}’ |
前面有提到在一个单词任一位置删除这个单词`diw`或者`daw`。后面的motion单位就是文本对象。主要有以下一些对象。
iw
inner wordaw
a word (a打头和i大头的区别就是a包括“边界”而i不包括)iW
inner WORDaW
a WORDis
inner sentenceas
a sentenceip
inner paragraphap
a paragraphi(
ori)
inner blocka(
ora)
a blocki<
ori>
inner blocka<
ora>
a blocki{
ori}
inner blocka{
ora}
a blocki"
inner blocka"
a blocki
inner blocka
a block
十年Vim的使用总结,就这么几张表,可是当所有命令组合起来时确实有时候有一种行云流水般的感觉,当然自己也不是什么Vim控,比如这篇文章就是在Emacs下编辑,编码之外的文档写作基本都是在Emacs下,因为Vim的模式恰恰是中文输入的硬伤。
这是我12年左右写的关于Vim的一些使用总结,现在是2019-07-11 星期四,而我现在已经全部用emacs开发了,或者换言之live in emacs。