vimbook-OPL(18-20)

CH18 更多基本编辑命令

18.1 word 和 WORD

在 Vim 有两个’单词’,其中 word 表示由字符、数字和下划线等组成,类似于 C 中的标识符。 word 由 ‘iskeyword’ 来指定哪些字符构成一个单词。而 WORD 的中文翻译是字串,就是用空白符分割的任何非空白符序列。Vim 中的移动操作是针对单词的,另外一些则是针对字串的。

word 的定制之所以需要用 ‘iskeyword’ 选项来界定的原因在于,Lisp 语言允许标识符中有’-’,PHP 中的标识符中允许 ‘$’ 符,特定的文件类型可以指定不同的单词定义,方便做跳转。

值得说明的是 iskeyword 默认的值是 iskeyword=@,48-57,_,192-255 ,此中 @ 表示所有 C 语言中 isalpha() 返回 true 的字符,如果需要 @ 字符本身得用 @-@ 表示。数字可以用 0-9 或者 48-57,超过两位的数字就是 ASCII 码表示。如果要排除某些字符,用 ^ 作为前导字符,如: ^x 排除 x 字符,^a-c 排除 a 到 c 的字符。

‘isfname’ 中的字符集控制 gf 命令判断光标下的字符串是否为文件路径; ‘isident’ 定制的字符集控制 [d 命令查找宏定义; ‘isprint’ 中的字符集控制 \p 元字符所表示的可打印字符。

18.2 跳转命令

绝大数为程序员设计的编辑都会有回跳和前进的功能,当在函数定义之前做跳转或者搜索时可以回到以前的位置。Vim 定义了跳跃栈的概念,调用 :jumps 可以查看当前的跳跃栈。> 字符指示的行表示当前的位置,CTRL-O 将会跳一次跳跃栈,CTRL-I 或者 <TAB> 将前进一次跳跃栈。

18.3 回绕控制

Vim 可以用 ‘whichwrap’ 选项来定制比如 h l 这些操作是否可以跨行。

字符 模式
b <BS> 普通和可视
s <Space> 普通和可视
h “h” 普通和可视 (不建议)
l “l” 普通和可视 (不建议)
< <Left> 普通和可视
> <Right> 普通和可视
~ “~” 普通
[ <Left> 插入和替换
] <Right> 插入和替换

‘whichwrap’ 选项是以逗号分隔的以上选项的字符集,指定对应的字符则对应的键可以跨行移动光标。

18.4 滚动屏幕

向上滚动屏幕导致光标往前面的行号。 CTRL-U 向上滚动由 ‘scroll’ 选项指定的行数; CTRL-Y 向上滚动一行; CTRL-B 向上滚动一屏;

向下滚动则刚好相反。 CTRL-D 向下滚动由 ‘scroll’ 选项指定的行数; CTRL-E 向下滚动一行; CTRL-F 向下滚动一屏;

当移动光标顶到了顶部或者底部时,将滚动 ‘scrolljump’ 选项指定的行数,如::set scrolljump=5 ,表示当光标到达边缘时将滚动 5 行。‘sidescroll’ 控制水平方向的屏幕滚动。:set scrolloff=3 控制光标距离边缘的行数必须至少大于 3 行,否则会引起屏幕的滚动。

z<Enter> 导致当前行滚动到屏幕的顶部;zt 的效果一样会移动到屏幕的顶部,除了 zt 保持光标在原来的列上,而 z<Enter> 将光标移动到第一个非空字符上。 z- 导致当前行滚动到屏幕的底部,并且将光标移动到第一个非空字符上;zb 则在将当前行移动到底部时,光标保持在当前列不动; z. 将当前行滚动到屏幕中央,并且将光标移动到第一个非空字符;zz 将当前行移动到屏幕中央,光标保持在当前列不动;

18.5 编辑命令

D 从当前字符删除直到行尾; C 从光标处删除直到行尾,并进入插入模式; s 删除光标下的字符,并进入插入模式,相当于 xi 命令组合; S 删除整行并进入插入模式; x 删除光标下的字符,并将光标向右移动一个字符,X 删除光标前的一个字符,并将光标向左移动一个字符; I 在当前行的第一个非空白字符 ^ 命令的位置前插入,gI 在当前行的行首 0 命令的位置开始插入;

18.5.1 Vim 算术运算

Vim 还可以对光标下的数字进行递增,CTRL-A 命令就能够完成;使用 CTRL-X 能够对光标下的数字进行递减;Vim 能够识别十进制、八进制和十六进制以及字母序列和二进制数字格式。而且如果在调用这两个命令前提供一个数字,光标底下的数字将加上或者减去提供的那个数字。如:设光标底下的数字是 123, 调用 5 -> CTRL-X ,光标底下的数字将变成 118 。记住调用 . 命令可以重复上一次的命令调用。

默认情况下识别除了字母序列以外的所有数字格式,这个由选项 ’nrformats’ 控制。

18.5.2 替换模式

J 命令将光标下相邻两个行的换行符替换成空格,gJ 则将此两行的换行符去掉。后面的行的前导空白符将会被删除,但是尾部空白不会删除; R 命令将进入替换模式,输入的内容将替换掉原本光标下的内容。这里有一个小问题:当遇到 <Tab> 时输入的字符会把整个 <Tab> 给覆盖掉,为了保持原本文本的对齐,调用 gR 进入 virtual replace mode,这样遇到 <Tab> 时不会立即将 <Tab> 给覆盖掉,而是一次一点点这样持续进行。

18.5.3 改变大小写

g~ 后接移动命令,将导致对应的移动过的字符的改变大小写的,如: g~$ 将直到行尾的字符大写变小写,小写变大写。g~~ 将整一行改变大小写; gU motion 将移动过的字符变成大写。gUU 将一整行变成大写; gu motionguu 将字符变成小写;

18.5.4 小技巧

在很多终端中的 Vim 是不允许复制的,为了复制某个日志文件的第 18903 行,该怎么办呢?总不能把整个文件都 cat 下,然后再复制吧!很简单,:.,.w /tmp/a.log 就可以将当前行输出到一个特定的文件。

.,. 表示 Ex 命令中的一个 range (范围),这在 Vim 中是非常重要的一个概念。其中 . 表示当前行, $ 表示最后一行,可以用 /pattern/ ?pattern? 表示搜索或回溯搜索的位置,中间用逗号分隔就组成一个特定的 range。如果仅指定逗号前面的部分就选中那一行作为范围。在帮助文档 :range 中可以找到非常详细的文档描述。

CH19 Vim 正则表达式

19.1 搜索

Vim 中大部分的特性都是通过选项来控制的,导致 Vim 中选项非常之多。搜索通过 ‘ignorecase’ 和 ’noignorecase’ 来控制是否大小写敏感。通过 ‘wrapscan’ 和 ’nowrapscan’ 来控制是否回绕搜索。

* 向下搜索整个匹配整个单词,相当于 <\word\> ,而 # 则执行相反方向的整词搜索。g*g# 执行类似的搜索,但是不必精确匹配整个单词。如:g* 能够搜索 const 和 constant ,而 * 如果在 const 处执行,将不会搜索到 constant , 这是两者之间的区别。

搜索命令还支持指定 offset 的方式,如:/const/2 将光标停留在搜索到的 const 所在行的后两行。offset 可以是负数,这样就停留在上两行。如果 offset 以字母 b 开头,则为从搜索到的模式的第一个字符向后偏移 offset 个字符。如:/const/b2 光标将停留在字母 n 的位置。偏移以字母 e 开头,则从搜索的匹配串的最后一个字符开始偏移。如:/const/e2 从停留在字母 t 的后两个字符处。/const/e-2 将停留在字母 n 处。

当执行完以上命令时,调用 n 命令移动到下一个匹配的位置,值得注意的是所有的搜索都是从当前位置开始的,如上:/const/-2 会继续搜索到同一个位置,并将光标又置于上两行,这样导致光标不动。

//5 将重复上一次的搜索,并将光标置于下 5 行,这是是一个快捷的重复搜索。// 将取消 offset 并进行重复搜索。/ 则直接重复上次搜索,我更倾向于使用 n 命令。

反向搜索的命令是 ?,如:?const?2 ?const?b2 ?const?e2 ?const?e-2 ??5 ?? ? 的功能都类似于以上 / 命令的用法,除了是反向搜索以外。

19.2 正则表达式

正则表达式的最小单位是 atom,原子可以是字符、元字符、单词边界(\< \\>)、行边界(^ $)这些特殊位置。除此之外还包括限定词(Quantifiers)和捕获组(Grouping)以及 or (\|) 。其它的详见 vim正则表达式 笔记。

CH20 文本块和多文件复杂处理

20.1 register

寄存器在 Vim 中是个特别的概念,在别的编辑器中只有一个剪切板,所有复制的数据都进入此剪切板,而 Vim 中有 26 个普通剪切板以及多个特殊专用剪切板。Vim 成这些剪切板为寄存器(register)。最常用的寄存器是无名寄存器 "" ,调用 y 命令进入的就是这个寄存器。寄存器的名字必须以引号开头,而这个无名寄存器的名字就是引号。普通的寄存器由字母 a~z 组成,比如用 a 寄存器进行 yank 和 paste 的命令如下: "ayy 进行复制 "ap 进行粘贴。如果用大写字母调用则向寄存器中添加新的行,如:"Ayy 向寄存器 a 中增加新行。

调用 :registers 查看所有寄存器中的内容,:registers ax 则指定查看寄存器 a 和 x 的内容。特殊寄存器 1~9 表示最近删除的文本,最近的为 1 一直到最远的 9 。"1p 将最近删除的文本粘贴回去。除此之外还有一些特殊的寄存器如下表:

寄存器 描述
0 最近复制的内容
- 行内删除寄存器
. 最近插入的文本
% 当前文件名
# CTRL-^ 命令打开的轮换文件名
: 最近执行的 Ex 命令名
/ 最近搜索过的内容
* 鼠标选中的内容,这个可以访问别的应用的剪切板,方便多应用交流数据
_ 黑洞寄存器,将吞噬所有删除的内容,如:"_dd 将内容吞噬掉,并且不会出现在 “1 寄存器中
= 表达式寄存器,调用 "= 将给用户一个机会输入算术表达式,回车后,调用 p 将结果粘贴在文档中

20.2 +cmd

有时我们编辑文件的时候需要打开文件并直接定位到特定的一行,特别是用 Vim 查看日志文件的时候。我们可以用 vim file.c +1297 打开文件并将光标定位到 1297 行。除此之外 + 后面还可以接 /pattern 将光标定位第一个查找到的 pattern 处,如:vim +/#include file.c 定位到第一个 #include 处。另外,有一个我自己很少用的,+ 后还可以接任何 Ex 命令。还有其它一些命令可以接 + 命令,如::next :previous :rewind :last

20.3 全局标记

小写字母是局部于文件的标记,大写字母是全局标记(mark),局部标记 ma 每个文件都可以标记,调用 'a`a 是回到当前文件的标记。如果想即便离开了标记文件还可以回去的话,得用 mA ,那就可以通过 `A 回到标记的文件。全局标记只能存在一个,也就是说当在另外一个文件做了 mA 标记将导致之前的标记失效。

20.4 viminfo 文件

Vim 定义了一个成为 viminfo 的文件,用来当 Vim 退出时保存历史信息,如:Ex 命令历史、搜索历史、输入历史、寄存器内容、标记以及缓冲列表和全局变量。viminfo 的启用通过 ‘viminfo’ 选项来设定,选项中的参数用逗号分隔,如以下设定:

:set viminfo='50,<1000,s100,:0,n~/vim/viminfo

下面解释为 ‘viminfo’ 而设定的参数的含义。

控制符 描述
' 保存编辑过的文件的最大数目,为它们记住位置标记(marks),viminfo 选项必须包含此参数。
f 值为 1 或 0,如果为 1 或者不设置则表示保存位置标记,只有为 0 时不保存位置标记
< 每个寄存器保存的最大行数
: Ex 命令历史的行数
/ 搜索历史的最大项目数,保存的数目不会超过 ‘history’ 选项的值
@ 保存输入行历史的最大项目数目
% 是否保存和恢复缓冲区列表,设定了的话只有在 Vim 启动不指定文件名时才生效
s 所有寄存器能够保存的 KB 长度,超过后将不保存
! 设定时保存大写字母开头并且不包含小写字母的全局变量
n 后接 viminfo 的文件名,此参数必须是最后一个参数

以上示例中的选项值表示*保存最近编辑过的 50 个文件的位置信息,寄存器中的内容保存少于 1000 行的内容,所有寄存器的内容最多保存 100KB ,不保存 Ex 命令历史,将 viminfo 信息保存在 ~/vim/viminfo。其它参数使用默认值。在 Vim 中可以调用 :wviminfo[!] [file] 将 viminfo 写入到文件 file 中,以及调用 :rviminfo[!] [file] 从文件 file 中读取内容到当前 Vim session 中。

20.5 处理长行

gk gj 两个命令可以长行回绕时移动到屏幕中的下/上一行,而不是逻辑上的下/上一行。这个命令当查看长行日志时很有用。

长行的回绕有好几个选项:’linebreak’ ‘breakat’ ‘showbreak’ ‘display’ ,在此不打算介绍。因为我觉得不重要 :-)