vim之补全2(完全个人定制版)

时间:2021-06-24 15:19:51

  关于补全的方面要说的的确很多, 这里选择分为两个章叙述. 如果你想学vim, 你需要有很强的耐心, 如果你想锻炼这种耐心, 你可以试着先看完我之前的文章. 好了, 下面继续我们的vim补全吧.

  vim补全1中曾经提及到supertab在更换版本后和UltiSnips成功共用tab的解决方案, 在此之前主要的叙述主要在做一件事情:将vim的tags补全和字典补全从supertab的补全功能中分离出来, 让supertab只负责一件事情:就近补全. 这样子做似乎没有必要, 因为我们完全可以将他们全部放到supertab接管的tab键上来, 下面要说的就是为什么我们需要这样做.

  从最初的叙述中不难看出的想要实现最快最智能的补全的需要实现的几个功能:优秀的补全列表排列算法,可预见性的补全列表结果,高速的补全列表建立.优秀的补全算法这一点上其实vim也有比较好的插件, 从很早依赖与tags的Autocomplpop和omnicppcomplete到后来比较出名的neocomplcache,再到后来相当智能的YouCompleteMe,最后到我现在留下的clang_complete. 下面就一一看一下这些插件的特点.

  首先是Autocomlpop和omnicppcomplete他们都是通过tags生成补全列表的,同时对tags的内容做过更加精确的分析可以实现诸如c++的一些类成员补全的高级功能. 不过由于tags文件本身的限制, 这种补全在实用性方面并不是太好. 他们都是比较早的vim补全插件.

  后来出现的一个neocomplcache插件比较出名, 网络上介绍他的文章很多. 主要的特点是通过在打开文件的时候一次性建立补全信息的缓存来提高补全速度. 这个插件我曾经使用过一段时间, 首先是打开文件的时候由于necocomplcache需要建立缓存, 如果文件比较大的话你将明显感觉到卡顿. 现最新的neocomplcache 已经改名为neocomplete,新的neocomplete通过lua的支持解决了启动时建立缓存的过慢的问题. 当然他也需要更加高版本的vim的支持. 缓存一旦建立完成就比较快了. 补全列表几乎全部是瞬间弹出, 可是这么高的补全速度不见得是好事, 如果你设置了necomplcache的自动弹出功能. 你将发现,这个弹出功能在很多不需要弹出的地方依然工作, 由于弹出速度相当的快,很多时候你在快速的移动光标的时候他都可见缝插针的弹出补全, 这个么积极的补全最终让我选择了关闭他的自动补全. 在需要的时候通过按键触发. 这个触发键自然会被想到映射到tab键上, 网络上的确有这样做的实现方案. 很明显neocomplete是不能和supertab共存的. 这问题并不是太过严重并且我曾经翻遍网络后在一个外文网站上找到了让neocomplcache和UltiSnips共存的方法. 可是最为严重的的问题是neocomplete和clang_complete冲突. 因为他们同时调用vim提高的全能补全函数接口. 调用同一个接口的结果是一个插件总是压制另一个插件导致被压制插件完全没有触发的能力.这种冲出似乎完全没有办法解决. 而clang_comlete又有很多我无法替代的优秀特性. 最终我还是选择了放弃neocomplcache.

  关于YouCompleteMe就更加奇葩了, 首先是他无法和clang_complete,supertab,neocmplcache共存. 其次是他需要clang3.3的支持, ubuntu目前默认支持比较完整的clang3.0,这包括clang3.0相关的libclang库(这个库可以加快clang_complete的速度) 不知道是不是由于clang没到3.3的问题, 在我成功编译安装了YouCompleteMe之后clang支持的所有补全特性一个都没有出来过. 官方文档之说YouCompleteMe是支持UltiSnips的,在使用中我也发现补全列表中有UltiSnips触发关键字的身影,可惜的是在选择这个些触发关键词之后简单的按enter键是无法展开UltiSnips的代码补全块的.百度后得出的解释是这里需要设置UltiSnips的展开键映射到<tab>键之外的其他键上并在补全列表中选择触发关键字之后通过这个重新映射的展开键来展开.可惜, 我没倒腾到这一步就已经把YouCompleteMe卸载了, 因为他的clang特性在我的ubuntu12.04中始终没有成功过! 另外, 这个插件在编译之后竟然达到了60M的大小!我其他vim配置加起来20M都不到, 真是佩服.,这么大的容量明显在编译YouCompleteMe的时候clang3.3是下载了的, 可是谁能告诉我, 为什么clang的特性一个都没实现呢.

  上面四个补全工具一一被淘汰之后, 最后剩下的唯一一个自动补全就是clang_complete了, 这是一个比较难控制的插件, 好在他和supertab以及UltiSnips并不冲突. 在ubuntu下使用clang_complete首先你需要下载clang编译器和libclang库. 下载命令如下:

sudo apt-get install clang libclang1 libclang-dev

  安装好clang编译器环境之后clang_complete就可以正常工作了.clang_complete主要实现了两个很重要的功能, 第一个是clang_complete可以动态的检测代码中语法错误, 这些语法错误是建立在clang编译器时时编译的结果的基础上的,因此相当准确. 其实clang_complete通过分析clang时时编译的结果实现比较高级的补全. 其中默认情况下clang_complete在". , ->和::"三个标识符后面自动触发. 这个这些标识符在C和C++中代表这指针的间接访问, 类对象成员变量和成员函数的调用等.这些地方的补全在简单的关键字检索方式很难实现精确的补全. clang_complete借助与calng编译器时时编译结果中的元素信息实现了这类补全的精确补全. 这个功能在补全C代码中的结构体, C++中的类对象时变得超级实用.

    clang_complete的配置选众多, 网络中有很多地方可以找到一些现成的配置.但这些配置大多是类似的, 我按照网络上能找到的配置放在vimrc中以后发现vim变得不稳定. 首先是vim在某些情况下比较容易崩溃. 比如#include代码的中出现错误时就容易直接闪退. 又比如在写c代码时经常会收到vim被致命信号ABRT中断的问题. 很明显这些不稳定是clang_complete在clang后台时时编译代码后动态解析编译结果并将编译错误显示到vim的时候出现了无法解析的内容所致.这是一个严重的问题. 因为你不知道vim会什么时候崩溃, 崩溃后之前编写后没保存的代码将会丢失, 又一个坑字了得!  好在一天实在受不了clang的随时驾崩之后. 我开始查看clang的帮助文档. 在帮助文档的帮助下终于解决了这个问题.下面是关于calng_complete全局变量配置的一些意义和功能解说:

g:clang_complete-auto_select 该变量置1时,calng_complete在弹出补全列表之后会自动选择第一个选项但不会生效, 置2时自动选择首选项后同时将该项生效.

最初我将该变量设置为1, 后来发现这个选择完全可以使用默认的0, 因为在弹出补全列表之后你可以通过tab实现相同的效果.

g:clang_complete_copen 该变量控制语法错误检查后是否打开quickfix窗口列表. 默认值是0表示不打开. 如果你不想在编码时时不时看到quickfix窗口的弹出, 这里保持默认设置即可.

g:clang_hl_errors 该变量控制是否高亮显示clang检测到的语法错误, 这个功能相当实用. 默认值是1表示打开, 如果你希望用到clang_complete的智能语法检测功能.这里保持默认设置即可.

g:clang_periodic_quickfix 这个变量是上面崩溃问题的关键, 网络上的配置为实现clang_complete对当前代码的时时语法检查, 这里大多将其设置成了1, 这个变量控制着是否定时刷新quickfix里的错误信息. 在该变量等于1时,只要我们的光标在vim中保持三秒不动, clang_complete就会在后台给我们刷新clang时时编译出来的结果. 这个功能的本意是好的. 可是clang_complete在刷新quickfix的时候似乎对某些错误无法正确的解析, 这就导致了vim突然的退出和致命错误提示. 很明显为了避免vim突然的退出导致的编辑内容丢失. 这里一定要保持默认设置.

g:clang_close_preview  clang_complete在补全的时候默认是可以打开补全预览的,所谓的补全预览就是在vim的上面展开一个小的名为"草稿"窗口, 里面显示的是当前补全列表中选择内容的完整内容预览, 这个功能并不实用, 因为补全列表中的内容已经相当详细了, 突然打开的草稿窗口只会给编辑带来不顺畅感.因此这里建议将其设置为1来关闭预览功能.

g:clang_snippets_engine clang_complete的补全带有一定的snippets功能, 所谓的snippets功能就是当补全内容是一个函数的时候clang_complete在补全确认后会首先定位到函数的第一个参数区并让用户修改, 修改完毕之后你可以通过回到普通模式(我这里通过`键返回普通模式)后使用tab键跳转到下一个变量区继续修改.clang_complete的说明文档中似乎表示支持Ultisnips的snippets功能, 也就是如果我们安装了UltiSnips并在这里将clang_snippets_engine设置成UltiSnips我就可以通<<vim之补全1>>中提及的ii和II来跳转可修改区. 可惜的是我尝试过这里设置成UltiSnips结果是snippets功能紊乱. 最后还是保持了默认的普通模式tab键跳转的方式. 好在这个方式还是相当方便的.

g:clang_snippets 这个变量似乎是用于开启clang_complete的snippets功能的, 而clang_complete的这个选择默认值是0,因此这里可以设置成1

g:clang_use_library 这个变量用于开启通过libclang库来加快clang_complete的反应速度. ubuntu在安装了libclang1 和 libclang-dev库之后就可使用libclang库了. 所以这里明显是需要打开的.

g:clang_user_options 这一项用于对编译器的一些设置, C语言编辑下这里不需要设置, C++开发时可以在其中添加必要的参数.

g:clang_complete_macros 似乎是用来补全宏什么的, 暂时不太清楚到底有啥作用, 将其设置为1暂时没有发现问题.

综合上面的描述, 最终我们的对clang_complete的配置可以归结为一下几句话, 将其写到vimrc中即可:

"clang_complete插件设置
let g:clang_snippets=1
let g:clang_use_library=1
let g:clang_close_preview=1
let g:clang_complete_macros=1
"let g:clang_user_options='-stdlib=libc++ -std=c++11 -IIncludePath'

上面的设置我们关闭了quickfix的定时更新功能. 这是为了防止vim意外崩溃而做的设置, 可是这也将导致clang_complete无法自动时时的显示语法错误了. 是否clang_complete提供给我们的强悍的语法检查功能就无法使用了呢? 非也, 帮助文档中提到我们可以通过手动调用函数的方式来触发quickfix的更新.也就是说任何说后我们希望看到clang时时编译之后的语法错误提示, 我们只有手动调用这个函数即可. 当然,每次看个语法错误提示还要手动调用个函数当前不是我的风格. 通过将这个函数的调用捆绑在快捷键上是我惯用的伎俩, 可是捆绑在那个快捷键上好呢? 我的设计是和共用保存快捷键. 这样的共用设计实现了任何时候刷新quickfix内容之前文件总是被保存过了, 首先是这样的手动更新vim崩溃的可能性变得很小, 其次即便崩溃了,也没关系, 因为我们刚刚保存过! 在我的vimrc中保存用的快捷被映射到了alt+w上因此我们只要添加下面的配置就可以实现保存和clang_complete语法检测共用alt+w组合的效果(为了防止在普通文件保存时触发ClangUpdateQuickFix函数而出现错误提示,这里配置成只在c和cpp类型的文件中触发该函数调用):

imap ^[w        <esc>:call Smart_save()<cr>a
imap <a-w>    <esc>:call Smart_save()<cr>a
nmap ^[w       <esc>:call Smart_save()<cr>
nmap <a-w>   <esc>:call Smart_save()<cr>
func! Smart_save()
   exec "w"
   if &filetype == 'c' || &filetype == 'cpp'
      call g:ClangUpdateQuickFix()
   endif
endfunc

注意其中的^[w 是通过在插入模式下先按下ctrl+v再按下alt+w产生的特殊字符,这种方式输入的alt+w是ubuntu的gnome-terminal终端唯一能识别的alt组合键的配置方法, 关于alt键的映射问题可以通过 :help map-alt-keys 和 :help map 以及 :help alt 来获得相关帮助.

和clang_complete相关的配置还有一个是

set completeopt=menu,longest 这是一个相当有效的设置. 这在后面的字典补全的地方会有说明,这里暂时就不做解释了.

最后要提及和calng_complete的相关的设置是项目根目录下.clang_complete文件. 这个文件是我们手动建立的. 假设你当前编辑的项目源码中包含了一个自己定义的头文件,而这个头文件并不再当前源文件所在的文件夹中而是在项目根目录的子目录include下面, 很明显,如果不告诉clang这个我们自己的头文件的位置所在, clang在时时编译我们的当前的源文件的时候将总是提示我们的头文件找不到. 为了解决这个类问题, 我们需要配置项目根目录的.clang_complete文件. 这个文件中可以设置所有和编译器相关的参数. 示例如下:

-w
-I include
-I lib/ipc
-I lib/unix
-I lib/net

-w 表示不对编译时的warning报错和高亮, 如果你希望代码更加严重, 最好不要在这里加入-w, 如果你不想消除代码中的worning有不想每次查看语法错误的时候都看到这些worning被高亮, 那么可以在这里添加-w

-I 后面跟的是目录名表示告诉编译器到那里去找到我们自己定义的头文件.

  clang_complete补全对应着优秀的补全必要特性中的高级算法. 另一个必要特性是可遇见性的补全结果.这个特性主要是为了提高补全的命中率和盲补的可能性. 在VAX中由于算法比较复杂, 补全的可预见性也被算法接管, 但vim中就不是这样了, 因为vim没有这么智能. 虽然和其他的IDE相比vim在补全的智能程度上的确有所不及.但是真所谓笨鸟先飞, 傻人有傻福.(^_^). vim的作者在设计vim的时候很明显也意识到了这一点, 既然程序本身不能做到高度的智能, 那么我们就需要将一部分需要智能区分的工作交给用户, 要知道人永远要比计算机更加的智能. vim为此设计了多达15种补全模式, 为的就是将不同场景中的补全分开并且将触发各种补全的时机交给用户, 也就是希望用户可以智能的先过滤到一部分完全不必要的补全来达到提高补全精度的目的. 这十种补全中我们需要关注的只有几个,  下面要说的就是最后要上场的四个vim自带的补全配置, 这些补全在<<vim之补全1>>中最开始就提到过. 首先要说的是tags补全, 由于tags补全对项目中不在上下文和buffer中的关键字补全行之有效并且tags补全大多时候是可以预测的(可预测指到在我们知道什么时候时候后需要触发的tags补全).  因此, 我们有必要将其保留, tags补全分析到这里最后需要考虑的就是怎么能更加简单的调用它. vim大多数捕全面是通过ctrl+x加上另一组ctrl组合键触发, 没调用一次补全需要按四个键, 很明显这是不合适的. 为此,我们需要想出更好的调用方式. 不过, 在思考这个问题之前我们需要解决另一个更加棘手的问题: 不知道是不是插件冲突的问题, 在我根据上面的表述将supertab和UltiSnips成功的共存之后, 出现了一个严重的问题: 在插入模式下输入ctrl+x组合键的时候, vim会自动输入下面的字段:

=<SNR>53_ManualCompletionEnter(
      )

很明显这是某个地方映射了ctrl+x键并且没有正确执行的结果. 定位的我们安装的supertab.vim文件中, 搜索<c-x>, 很快728行(你可能不在这一行).找到了如下代码:

imap <c-x> <c-r>=<SID>ManualCompletionEnter()<cr>

在这一行的开头添加"将其注释掉, 清除所有.vim/view目下的所有记录, 打开文件重新输入<ctrl+x><ctrl+]>, tags补全成功被调出.

  supertab的映射问题解决之后我们可以继续考虑上面的需求. 我们需要尽量用最少和最容易操作的按键来触发补全的调用.补全的触发键首先是ctrl和alt组合键不能用, 因为在我的<<vim之快速跳转>>中有提及,ctrl的组合键首先是有一部分被vim默认占用后不可重新映射, 另有一部分如组合键直接不能实现映射. 其他能用的ctrl组合键大多用于实现vim的跳转和定位. alt组合键在操作上并不流畅,比较适合做操作并不频繁的各种功能映射. 其次, 补全的触发键必须在插入模式直接触发, 这样
又导致这里的映射不能像普通模式那样通过单键来实现.
  ctrl,alt,单键都不能做映射, 难道补全就不能实现更好的重映射了吗? 非也, 之前提过vim有一个神奇的双键映射功能. 由于vim有一个按键等待模式, 因此即便的实在插入模式下双键映射依然可以很好的工作, 比如我们的在插入模式下用jj做映射,任何时候我们在插入模式下快速的输入jj键都可以触发这个映射. 你可能会问题, 这样一来我不是不能输入连续的两个jj了吗?非也, vim对所有的用户输入如果在功能上无法是未决的, 那么vim会进入输入等待模式,这个等待时间默认是1秒, 在你输入一个字符后的1妙内,vim会等待你输入第二个字符来确定输入的功能. 如果在这一秒内你没有输入第二个字符来触发某个映射.那么vim就会认为你是希望单纯的输入第一个字符. 因此在1秒后你的字符会转换为正常的字符并上屏.这样一来, 如果我们对jj做了映射, 我们有希望输入两个连续的jj的话就很容易实现了, 我们只要在输入第一个j之后等待大约1妙再输入下一个j就可以了.如果你不了vim多字符映射功能, 那么你很可能会为在某天你从别人那借鉴了某些设置之后发现自己vim的输入变得莫名奇妙的卡顿了.实际上这种卡顿在大多数情况下是不会影响我们正常编辑的, 只要我们编辑的的内容没有和双字映射重叠, 我们大可不必理会这样的停顿, 你的编码速度有多快就写多快吧.放心, vim会跟上你的编辑速度的. 当然这里在做双键映射的时候也不能做任意的组合, 某些在你编码或编辑中出现频率很高度组合是不适合做双字映射的.比如方便又按的"ll"就不适合编程的人用来做映射."ll"在很多很常用的的单词中出现(malloc是最多的一个),因此如果你用他来的做映射,那么你不得不在每次输入malloc的都时候都要停顿一次,并且这种停顿往往被我们一气呵成的打出一个malloc所误伤! 第二个双字映射需要注意的是要尽量使用那些尽量容易操作的组合, "jj"组合要远比"pp"组合好按的多. 最后一个需要注意的时候最好只用几个特定的按键来产生各种不同的组合来的映射.因为只要一个按键在你的vim配置中有一个相关开头的映射, 我们在任何时候输入这个按键都会看到卡顿现象. 如果你把键盘上的26个字母和其他字符全用双字映射了一遍, 那么每个按键在任何时候按下的时候都会被卡顿. 我想应该不会有人会喜欢中样的输入体验的.综合上面的叙述, 我的双字映射中大多用到了"jkl"和"JKL"这六个字符做组合映射. 这里我挑选了相当好按键的"kk"来做为tags补全的触发映射.

  你可能会说"jj"键比"kk"键更加易于操作, 为什么不用它来做为tags补全的映射呢? 答案是我们还有一个比tags补全使用更加频繁有效的补全方式, 那就是字典补全! 这个补全在vim中的默认触发键是<ctrl+x><ctrl+k>, 在我之前叙述中多次提到过字典补全, 可见他的功效之高, 首先说明字典补全的原理:我们在一个任意名字的文件中用空格做为关键词写下多个词组, 这些词组是我们希望用于补全的词组. 在.vimrc通过下面的方式告诉vim我们的字典在哪里:
set dictionary+=~/.vim/tab/C.dic
  我将所有的个人补全字典全部放在了~/.vim/tab目录下, 出了用于c语言补全的C.dic还有针对c++的cpp.dic和针对于网络编程开发net.dic等等, 这些字典完事是我们自己设计的因此字典的名称,字典内容以及字典的分类方式完全取决于我们个人. 如果我们用c语言的同时还需要使用网络方面的开发接口, 这里可以在vimrc继续添加如下内容:
set dictionary+=~/.vim/tab/net.dic
字典在路径指定给vim之后插入模式下任何时候我们通过<ctrl+x><ctrl+k>触发字典补全,vim都会按照vimrc中添加的顺序来检索所有存在的字典文件. 单个文件中vim的检索顺序是从文件头顺序到文件尾.

  字典补全的原理是很简单的, 第一次看到你可能会觉得字典补全没有什么特别,也没什么强大的地方. 不过下面对字典补全优点的分析可能会让你改变这个看法.首先, 字典补全的字典是我们自己设计的, 这将给予我们的是最为灵活的补全列表定制功能. 使用vim的人大多是程序员, 而程序员使用的语言有很多, 也许你和我一样是一个普通的c语言的使用者. 也可能你是一个php工程师或着使用python做开发. 无论你是用什么语言, 字典补全总是可用的. 因为你php的工程师完全可以自己定制一个php.dic供自己使用.其次字典补全可以让步补全的精度提到非常高的一个高度. 现在假设你和我一样都是C语言程序员, 但我的开发主要是做网络信息采集和处理, 而你的开发是用c语言来写驱动程序.这么一来即便我们用的是完全相同的语言, 我的C.dic也可能和你的c字典大不相同, 因为我可能永远不会用到驱动程序里的接口函数, 你也可能完全不管任何应用开发使用的函数.补全字典可以做到补全列表的高度定制化, 任何你用不到的关键词都可以被你排除在外, 等到哪天你需要用到它时, 再添加和修改字典补全也是很容易的. 这种完全按需求的定制字典可以保证补全列表中基本没有冗余的内容. 第三个补全字典的优点是便于切换,这在之前已经提到, 我们完全可以定制补全种类的字典的在不同场合单独或组合使用. 字典补全的第四个优点是补全的结果是可预测的. 这个特性相当的实用, 因为它可以让字典补全实现盲补(我自己发明的词, 在<<vim之补全1>>中有提及). 盲补就像盲打一样是所有补全中最快的一个, vim在检索补全字典中的内容时总是按照从头到尾的顺序扫描并排列结果的. 一来只要字典中的关键词顺序没有变. 补全出来的结果也不会变, 二来我们任何时候都可以通过修改补全字典中关键字的内容和顺序来让补全列表的结果变得更加合理高效.字典补全最适合用于各种编程语言或普通编辑中那些不变的语法关键词, 这些语法类的关键词首先是固定不变的, 其次是高频率的出现. 比如C语言中语法关键子struct这个关键词出现的频率相当的高, 如果我们使用就近补全或者是tags补全等其他方式的话, 补全列表的结果会因上下文内容的变化而产生化, 这样我们不得不在每次的触发中下意识的去查找.而字典补全则完全不同, 如果你吧sturct在C.dic中放置在非常靠前的地方, 那么你将能够确定每次通过s触发字典补全的时候首选词总是struct, 这样就实现了盲补的特点. 字典补全的最后一个优点是对一些有规律的很长的带有体系特点并且往往很长的接口函数和接口关键词的的补补全行之有效.下面是我的C.dic中关于linux线程的字典记录:

pthread_t
   pthread_create()        pthread_exit()
   pthread_keycreate()     pthread_keydelete()
   pthread_join()          pthread_self()
   pthread_setpecific()    pthread_getspecific()
   pthread_cleanup_push()  pthread_cancel()
   pthread_detach()        pthread_equal()

pthread_attr_t
   pthread_attr_default()        pthread_attr_init()
   pthread_attr_getschedparam()  pthread_attr_setcheparam()
   pthread_attr_setscope()       pthread_attr_setdetachstate()

PTHREAD_
   PTHREAD_CANCELED
   PTHREAD_PROCESS_
   PTHREAD_PROCESS_PRIVATE PTHREAD_PROCESS_SHARED
   PTHREAD_SCOPE_
   PTHREAD_SCOPE_SYSTEM    PTHREAD_SCOPE_PROSESS
   PTHREAD_CREATE_
   PTHREAD_CREATE_DETACHED PTHREAD_CREATE_JOINABLE

cond_attr
   pthread_cond_
   pthread_cond_init() pthread_cond_destroy()
   pthread_cond_broadcast() pthread_cond_singal()
   pthread_cond_timewait() pthread_cond_wait()

pthread_delay_t
   pthread_delay_np()

pthread_mutex_t
   pthread_mutex_init()          pthread_mutex_destroy()
   pthread_mutex_lock()          pthread_mutex_unlock()
   pthread_mutex_mutex()         pthread_mutex_trylock()
   pthread_mutexattr_setptype()  pthread_mutexattr_setpshared()
   pthread_mutexattr_init()      pthread_mutexattr_destroy()
      PTHREAD_MUTEX_
      PTHREAD_MUTEX_NORMAL    PTHREAD_MUTEX_ERRORCHECK
      PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_DEFAULT
      PTHREAD_MUTEX_INITIALIZER

sched_param {
      sched_priority
   }
   sched_

pthread_once_t
   PTHREAD_ONCE_INIT

这个不怎完善的线程接口字典有很多规律性的特点 他们大多以pthread_和PTHREAD_开头后面往往还有一些子字段,它们中大多数比较长而显得不那么友好, 如果你总是一个一个去打的话,即便利用就近补全只打首次, 你也会觉得很麻烦. 这里利用字典补全当我们利用字典补全在任何时候通过pth触发, 出现在补全列表首的将会是pthread_这个开头总会是正确的, 我们在他的后面再加上一些子字段的关键字继续补全就会将补全精确定位到我们需要的结果上.这个补全相当的好用.
  你可能会觉得制作这个补全字典比较麻烦, 首先是你可以在感觉到某个高频词需要加入字典的时候将其添加进来. 你也可以在看相关书籍的时候作为一种总结性的笔记边看边把他们加入到字典. 这种方法比较系统, 也可以帮助我们学习. 总之, 这些字典是日月累起来的分散到零散的工作时间中去之后, 你会发现制作他们基本上没有额外的时间开销.

  在我的vimrc中利用"jj"这个双字映射中算是最容易操作的映射来触发字典补全, 另外还设置了"JJ"来触发路径补全. "JK"来触发头文件补全(在<<vim之快算跳转>>中曾经把"jj"和"kk"用于上下移动, 这里更新为补全触发. 由于在编码中从成对的标点最后右移跳出标点的操作相当的频繁,因此保留了"jk"右移和"kj"左移的功能). 综上所述, 下面给我的vimrc相关映射配置片段:
imap kj  <left>
imap jk  <right>
imap jj  <c-x><c-k>
imap kk  <c-x><c-]>
imap JJ  <c-x><c-f>
imap JK  <c-x><c-i>

  vim补全相关的内容最后还有一个小小问题需要讨论一下, 这个问题是vimrc中completeopt的设置带来的. 首先是completeopt用于控制补全列表的显示方式, 主要是三个, menu方式表示展开补全列表, longset方式表示在补全列表中存在公共的最长字串时将其直接上屏, preview表示打开预览窗口. 第一个menu一般是必选的, 第三个preview因为没有多少苦实质的用处,使用后反而会在的补全不时的给你在窗口上方弹出个"草稿"窗口而打乱了我们输入节奏, 因此建议不要使用. 问题出在第二个longest上面, 这是一个比较纠结的补全显示方式, 他的优点是,如果你输入向上面拥有公共字串的pthred_类型的补全时, longset开启之后pthread_会在
你触发补全时就上屏, 并且由于这时没有选择任何一个补全列表中选项, 因此当我们继续输入之后子字段时补全列表会很自然的更新到更加精确的补全列表上来. 如果longset关闭, 那么触发后会默认选择第一个选项, 如果上面的列表中第一个词pthread_t修改为pthread_ 那么会在出现pthread_之后很自然的继续输入mu等来继续精确选择诸如pthread_mutix_init的选择可是问题出在继续输入mu等子字段后补全列表会自动关闭, 我们必须通过再次使用"jj"来重新弹出补全列表. 这个样每个具有子字段的补全必须使用至少四个j才输入完毕. 这在输入上完全没有longest开启时那么自然. 可是如果真的设置longest方式, 如果我们输入诸如struct这样
完全没有公共字符串的单词时又会变得麻烦, 因为他不会默认给你选择补全列表中的第一个选项. 这在输入时又变得的不那么自然了, 这么一折腾longest变得相当蛋疼. 开也不是, 关也不是. 这个问题我酷似冥想了好几天无果...之后只想出了一个可以稍微方便一点的办法就是将上面字典中ptheard_这样的公共字串写成pthread_t这样的多一个单词的关键词并且关闭longest, 这样一来在输入pth触发字典补全之后会选中第一选项pthread_t, 如果我们想输入phread_mutix_init就先删除一个单词t, 这个时候补全列表上不会选择任何一个选项, 之后当我们再输入mu时补全列表是不会关闭的, 我们就可以省去一次"jj"的触发输入. 当然, 这种输入只能稍微的让补全变得更加平滑一点, 并不是解决的根本办法. 如果那位读者有更好的解决办法希望可以给予回复或发邮件告诉我, 个人将不甚感激.

  我的邮箱如下:pangchol@163.com

  好了, vim补全相关的内容我暂时就研究到这里, 终于写完了, 最后来做一个总结:
  我们拥有自己的补全字典, 在写项目代码时首先在vimrc指定正确的补全字典. 将前辈的代码和自己的函数库等加入自己的项目中. 利用f12键随时建立或更新tags和cscope. 我们需要最学会UltiSnips的代码块补全定制方式, 在必要的时候修改~/.vim/vunder/UltiSnips/UltiSnips目录下相关的代码块补全模板来定制符合项目和个人需求的代码块补全. 我们需要认真定制和记忆这些代码块补全的触发关键词, 在任何时候都可以通过输入这些触发关键词后输入Tab键来触发代码块补全功能. 任何时候如果需要补全一个上下文或其他打开的缓冲区中出现过的单词, 我们都可以通过Tab键触发就近补全(只要注意不要输入了和代码块补全触发关键词相同的内容来触发即可). 这个单词在上下文中离光标越近越容易实现盲补.如果一个函数或者变量名等存在于前辈的代码或者函数库中, 我们可以通过"kk"键来触发tags补全来比较精确的找到他们. 任何时候如果你想输入语法中的关键字或者编程语言中的标准库接口等都可以通过"jj"来触发字典补全(已经定制好的)高速而精确的实现,标准接口函数名往往在第一次使用时通过字典补全触发,之后大多使用就近补全更加快速高效. 而数量不多词组简单但又出现频率极高的语法关键字(诸如: struct static等)我们最好总是采用字典补全, 认真设计你补全字典尽量保证越高频的语法关键在字典补全的越前面. 你将发现发多时候你输入他们都是在盲补!如果你需要输入系统中的路径你可以试着使用"JJ"触发路径补全. 如果你确定一个关键子存在于当前文件包含的某个头文件中, 你也可以试着通过"JK"来触发头文件补全. 最后, 任何时候clang可以支持时时编译的编程语言中(C,C++,object-c等), 在我们输入".","->","::"后clang_complete会自动通过clang的编译分析来得到补全精度相当高的结构体成员或类成员的补全.如果我们希望查看一下当前编辑的程序有没有语法错误,你也使用alt+w来保存后自定触发clang_complete的语法错误分析和高亮功能,clang_complete的语法错误高亮功能只在类型为c或cpp的文件中被触发.

后记:

2013-1214

最近几天在使用vim跳转的时候发现ctrl+]的跳转有些诡异, 很多时候都不会跳转的关键字的定义上. ctrl+]在默认情况下被用作ctags三跳转. 通过:h CTRL+]查看相关的帮助文档才知道ctrl+鼠标左键和ctrl+]的功能相同ctrl+鼠标右键和ctrl+t的功能相同(哎, vim到底还有多少好东西我还没有知道呢?), 这时再测试ctrl+左键的跳转, 诡异的现象出现了, ctrl+左键竟然和ctrl+]的跳转结果不相同! 我来个去, vim帮助文档写错了?? 仔细想了想帮助文档应该不会有错, 这样可能的问题应该是ctrl+]被某个插件替换了. 想想自己的所有插件中可以实现跳转的功能的除了cscope就是剩下clang_complete了, cscope之前没有问题, 所以目标首先锁定在clang_complete上, 通过:h clang_complete查看它的帮助文档后原因就找到了, clang_complete默认将ctrl+]和ctrl+t作为跳转到声明的快捷映射了. ctrl+]本来的功能是跳转到定义! 原因找到之后就很容易解决了, 在vimrc添加如果内容将clang_complete的跳转映射修改到不用的键位上(键盘上的映射组合都快用完了, 想再找一个好用的快捷键不容易啊, 最后只能用ctrl+P和ctrl+T了):

let g:clang_jumpto_back_key="<C-T>"
let g:clang_jumpto_declaration_key="<C-P>"

这样映射之后, ctrl+]将保持原有的通过tags跳转到定义位置的功能. 如果cscope存在的情况下,vim的ctrl+]默认会先搜索cscope中的数据库.在定义跳转上tags的跳转要比cscope准确一点(tags几乎用作定义跳转). 为了上vim的ctrl+]默认先搜索tags文件. 我们需要在vimrc添加如下配置:

set nocst

我们可以在vimrc添加专门用于cscope的定义跳转映射以便在ctrl+]跳转失败的情况下使用cscope试试.关于这方面的讨论, 更具体的内容可以在我的<<vim之tags>>中找到.

如果鼠标就在身边, 其实ctrl+鼠标左键和ctrl+鼠标右键的组合要比ctrl+]和ctrl+t更加方便一点.

如果你真的需要跳转的一个关键字声明的地方可以通ctrl+P(注意是大写的P)来实现. ctrl+t 和ctrl+T都可以实现递归的跳回到之前位置的功能.

在这次ctrl+]功能修正的过程中还发现了一个坑爹的问题, 很久之前我就发现vim中有些快捷键组合是绝对不能用来做映射, 如果用了后果就是vim彻底凌乱. 这样的组合键典型例子有: ctrl+[, alt+[, alt+] . 一直以来知道的他们不能使用, 但具体原因并不明了. 今天在配置ctrl+]的时候意外的留意到vim如果直接输入ctrl+[ 输入提示中出现的^[符号, 而这个^[符号是特殊的组合键的转译起始字符并且<esc>键也被vim当作^[来处理. 同时alt键的组合键由于在gnome-terminal中会转译成<esc>开头的特殊组合键,因此又会用^[来代替, 我来个去, 这么多的键位重合, vim不凌乱才怪哦...

2013-1216

这几天使用vim发现即便通过alt+w来在保存之后调用ClangUpdateQuickFix()函数来手动触发语法错误检测. vim还是出现频繁崩溃的问题. 实在受不了了只能再次求助百度谷歌. 在网上海搜了一翻之后在clang_complete作者的github上找到了一些相关的问题讨论. 不过可惜的是没有找到具体的解决方法. 仔细观察讨论的内容发现似乎最新的clang_complete修复了不少容易崩溃的问题. 在看看自己vimrc中vunder对clang_complete的下载源设置:

Bunder 'vim-scripts/clang_complete'

vim-scripts在github上是一个专门存放vim.org下插件拷贝镜像的地方, 也就是指我当前的clang_complete是发布在vim.org上的最后的一个版本. 仔细看看clang_complete作者的github发现在Rip-Rip/clang_complete目录下,也就是指这里的版本可能要比vim.org上最新的版本要新. 于是将上面的源配置修改为:

Bundle 'Rip-Rip/clang_complete'

重新清理和更新了clang_complete之后崩溃的问题暂时还没有遇到了, 希望的真的是版本的问题吧.

最后在作者git的讨论中似乎还看到如果开启libclang功能会导致崩溃的可能增加. 因此将 let g:clang_use_library=1 这句删除来关闭libclang的使用可能会好一点, 我暂时没有关闭, 先用着观察一段时间吧.

2014-0105

经过较长时间的测试这里得出的最新结论是clang_complete的语法错误高亮功能的确非常容易导致vim的突然崩溃, 最近终于绝对将这个功能彻底从vim的常用功能中剔除, 保持他的默认关闭,并且分配一个不适很好用的快捷键给它. 现在想想其实代码的语法错误检查功能并不是一个非常必要的功能, 因为在编码的过程中语法错误几乎是无法避免的, 编码时不关注这些临时的语法错误其实不会降低编码效率. 现在vimrc中和clang_compelte相关的设置如果如下(关闭的libclang支持和自动语法检测):

"clang_complete
let g:clang_snippets=1
let g:clang_close_preview=1
let g:clang_complete_macros=1

"避免和ctrl+],ctrl+t原有的功能冲突
let g:clang_jumpto_back_key="<a-t>"
let g:clang_jumpto_declaration_key="<a-d>"

ino <a-s> <esc>:call Show_error()<cr>a
nno <a-s> <esc>:call Show_error()<cr>

func! Show_error()
   wall
   if &filetype == 'c' || &filetype == 'cpp'
      call g:ClangUpdateQuickFix()
   endif
endfunc