8.1 快速编辑结对符
平时,最让我头痛的字符莫过于 {}、""、[] 等这类结对符,输入它们之所以麻烦,主要因为A)盲打很难找准它们位置,B)还得同时按住shift键。两者再一叠加,非常影响我的思维。要高效输入结对符,应该是输入少量几个字母(对,字母,不是字符)后 vim 自动为你输入完整结对符,而非是我输入一半 vim 输入另一半(不用 delimitMate 的原因)。刚好,这在 UltiSnips 能力范围内,只要定义好模板,可完美地解决这类问题,具体模板见上例中最后的结对符部分。
在定义结对符模板时,你应该考虑加上模板控制参数 i。默认情况下,UltiSnips 只会当模板名前是空白字符或行首时才进行模板补全,比如,定义 () 的模板如下:
snippet b "bracket"
(${1})${2}
endsnippet
我要调用函数 printf(),在输入完 printf 后应该接着输入括号模板名 b,然后输入模板展开快捷键 <leader><tab>,你会发现 UltiSnips 无法帮你补全模板,因为它看到的不是 b 而是 printfb,这在模板文件中根本未定义。有一种间接解决方式是在 printf 后加个空格,再输入 b<leader><tab> 进行补全,这就成了 printf (),不喜欢这种编码风格。其实,UltiSnips 的作者也注意到这个问题了,他让你可以通过前面提过的模板控制参数 i 进行解决。重新定义 () 的模板如下:
snippet b "bracket" i
(${1})${2}
endsnippet
这样,UltiSnips 只管光标前 1 个字符是否是 b,若是则补全 (),不论 b 前是否有其他字符。类似,其他结对符模板都按此加上 i 控制参数。结对符模板完整定义参见上一节 cpp.snippets 示例。如下是几个快速输入结对符的演示:
(快速输入结对符)
另外,要想高效编辑结对符,你得了解 vim 自身的某些快捷键。比如,有如下字符串且光标在该字符串的任意字符上,这时在命令模式下键入 va) 后将选中包括括号在内的整个字符串:
(快速选中结对符)
其中,v 是动作、a 是范围、) 是结对符。结对符命令的动作包括:选中 v、复制 y、删除 d、删除后插入 c;结对符命令的范围包括:含结对符 a、不含结对符 i。针对不同结对符,组合不同动作和范围就有 4*2 种方式。比如,di{ 删除不含结对符 {} 的字符串,va[ 将选中含结对符 [] 内的所有字符。
选中结对符内的文本是我较为频繁的操作之一,通过诸如 vi[ 的命令可以选中当前结对符 [] 内的所有文本,这虽谈不上麻烦,但每次都得去看下是 ]、)、> 还是 },总是有点别扭。有款叫 wildfire.vim(https://github.com/gcmt/wildfire.vim )的插件,让我能更自然地选中结对符内的文本,有了它,我只需按下空格(你也可以设置成其他快捷键),自动选中光标所在区域最近的一层结对符内的文本,如果没有结对符,则选择最近的一个段落。
简单设置:
" 快捷键
map <SPACE> <Plug>(wildfire-fuel)
vmap <S-SPACE> <Plug>(wildfire-water)
" 适用于哪些结对符
let g:wildfire_objects = ["i'", 'i"', "i)", "i]", "i}", "i>", "ip"]
这样,在 vim 的命令模式下,一次空格选中最近一层结对符内的文本,两次则选中近两层内的文本,三次三层,依此类推;或者键入 3space,直接选中三层内的文本;若要取消,键入 shift-space 即可。另外,结对符类型也可以在 wildfire_objects 变量中指定。如下图所示:
(快速选中结对符文本)