Vim
首发于Vim

YouCompleteMe 中容易忽略的配置

YCM 版本演进很快,以前是半异步(偶尔会卡),2017年中进化成全异步(完全无等待了),网上很多针对老版本的配置方法也都是有问题的,并不能发挥出 YCM 的最佳水平,下面列举额一些容易被忽略的配置问题:


语义补全自动触发

大家都知道 YCM 有语义补全,但默认语义补全是要你输入 . 或者 -> 或者 :: 后才会弹出:

上图中 time 后面输入了点以后才会弹出基于语义的补全,而默认状态下即便你设置了:

let g:ycm_min_num_identifier_candidate_chars = 2

两个字符以后自动弹出的是基于符号的补全(当前所有 buffer 中收集到的符号),不是基于语义的补全,所以前面没有输入过 printf 的话,接下来输入了 pri 也不能自动语义补全为 printf:

其实 YCM 里触发语义补全有一个快捷键:

let g:ycm_key_invoke_completion = '<c-space>'

默认是用 CTRL+SPACE 来触发补全的,中文操作系统下,CTRL+SPACE被系统劫持用作输入法切换,无法正确传到终端,所以一般要改成 CTRL+Z:

let g:ycm_key_invoke_completion = '<c-z>'

好了,那么我们输入到 pri 的时候按下 CTRL+Z,语义补全就弹出了:

前面设置基于符号的补全,只需要输入两个字符,就会自动弹出,但它相对语义补全有三个不足:

  1. YCM需要在所有打开的文件中收集符号,除非前面写过 printf,或者同时打开的文件里有 printf,YCM才能正确进行符号补全,否则首次出现的东西 YCM不知道该怎么办。
  2. 编码时首次出现的符号才是我们最需要补全的东西,前面都写过了,还要你符号补全作甚?如果你第一次写了printf 的头三个字母,没有语义补全的话 YCM 就傻了。
  3. 符号补全只有一个符号名,不能像语义补全一样给出参数格式,返回值等丰富信息。

所以符号补全就是傻瓜猜测,语义补全才是我们最想要的东西,那为啥 YCM必须要输入了:. 或者 -> 或者 :: 才能弹出语义补全呢?对于顶层符号(比如 printf)的语义补全为啥必须要我按一个快捷键(上面设置的 CTRL+Z)呢?

因为 2017年 8月之前 YCM 还是半异步模式,符号补全很快,默认基本都可以自动弹出,而语义补全经常卡半天。所以为了避免用户每敲个字母就等待半天的尴尬,YCM 的语义补全一直使用被动触发(输入 ->或 . 或 ::,或者按 CTRL+SPACE/Z),这种方式解决了输入不流畅问题,但也带来了操作麻烦问题。

而 2017年 8月后的版本,请求发送过去,使用 timer 轮询结果,有结果才弹出,没返回也不影响输入,从这个版本开始 YCM 才进入了全异步时代,体验基本赶上 emacs 补全(emacs很早就有异步机制)和各种 IDE。

既然 YCM 进入了全异步模式,语义补全不会卡到用户输入了,那么可不可以不需要用户主动触发就自动弹出呢?在你的配置中加上这个语义触发的条件即可,默认的语义补全触发条件是 ycm_semantic_trigger :

let g:ycm_semantic_triggers =  {
  \   'c' : ['->', '.'],
  \   'objc' : ['->', '.', 're!\[[_a-zA-Z]+\w*\s', 're!^\s*[^\W\d]\w*\s',
  \             're!\[.*\]\s'],
  \   'ocaml' : ['.', '#'],
  \   'cpp,objcpp' : ['->', '.', '::'],
  \   'perl' : ['->'],
  \   'php' : ['->', '::'],
  \   'cs,java,javascript,typescript,d,python,perl6,scala,vb,elixir,go' : ['.'],
  \   'ruby' : ['.', '::'],
  \   'lua' : ['.', ':'],
  \   'erlang' : [':'],
  \ }

代表在 C/C++ 下面,用户除了手工 CTRL+SPACE/C 外,写代码的时候必须输入:. 或 -> 或 :: 才能弹出语义补全,下面我们在自己的 vimrc 中加入如下额外设置:

let g:ycm_semantic_triggers =  {
			\ 'c,cpp,python,java,go,erlang,perl': ['re!\w{2}'],
			\ 'cs,lua,javascript': ['re!\w{2}'],
			\ }

vimrc 中定义了 g:ycm_semantic_triggers 以后,默认的 . / -> / :: 是不会被覆盖的,只会追加成新的 trigger,这里我们追加了一个正则表达式,代表相关语言的源文件中,用户只需要输入符号的两个字母,即可自动弹出语义补全:

['re!\w{2}']

就是上面这个,括号里面的数字可以修改,这里我们默认输入两个字母才弹出。好了,接下来重启 Vim ,跑到刚才我们的小程序那里,输入 pr 后不用按任何键就自动弹出语义补全了:

全异步机制的 YCM 搭配 g:ycm_semantic_triggers 设置,终于让 YCM 的补全效果如丝般顺滑了,自动语义补全使整个编码的工作流顺畅不少。

注意升级你的 VIM 为 8.0 以后,YCM到最新版。Windows 下的32位版本 YCM 你不用自己编译,我一直在维护更新,可以到这里下载:

韦易笑:如何在 Windows 下使用 Vim 的 YouCompleteMe 插件?

而 Linux 下面的 YCM 安装相对比较自动化一点,直接跑 install.py 即可。


补全对话框的颜色修改

不少人觉得 Vim 自动补全的弹出窗口默认配色很丑:

如果你和我一样想把它改成上面比较素雅的灰色的话,可以自己定义 highlight:

highlight PMenu ctermfg=0 ctermbg=242 guifg=black guibg=darkgrey
highlight PMenuSel ctermfg=242 ctermbg=8 guifg=darkgrey guibg=black

这个配置是我上面的灰色配置,xterm 下的配色见:

Xterm, HEX, RGB, HSL

对照这个表格你还可以改成你喜欢的颜色。


屏蔽一些不喜欢的 YCM 功能

我不喜欢 YCM 自动弹出函数原型预览窗口,它搞乱我的布局,我有其他方法查看函数的原型,如果你和我一样想关闭该功能的话,增加两行配置:

set completeopt=menu,menuone
let g:ycm_add_preview_to_completeopt = 0

这样讨厌的预览窗口就不会不经过我同意随便乱弹了。

YCM默认会显示诊断信息,语言标注出来你代码问题,我一般不需要,要分析问题我有其他手段,我更喜欢我原型编译或者静态检查工具以后,你再给我标注出问题来,你如果也想屏蔽 YCM 的诊断信息,你可以设置:

let g:ycm_show_diagnostics_ui = 0

这样你可以用其他插件来完成自动/非自动代码静态检查。


最终配置

好吧,下面是最终的 YCM 配置:

let g:ycm_add_preview_to_completeopt = 0
let g:ycm_show_diagnostics_ui = 0
let g:ycm_server_log_level = 'info'
let g:ycm_min_num_identifier_candidate_chars = 2
let g:ycm_collect_identifiers_from_comments_and_strings = 1
let g:ycm_complete_in_strings=1
let g:ycm_key_invoke_completion = '<c-z>'
set completeopt=menu,menuone

noremap <c-z> <NOP>

let g:ycm_semantic_triggers =  {
			\ 'c,cpp,python,java,go,erlang,perl': ['re!\w{2}'],
			\ 'cs,lua,javascript': ['re!\w{2}'],
			\ }

最后建议设置一下:g:ycm_filetype_whitelist 这个白名单,避免编辑白名单外的文件类型时 YCM也在那分析半天,比如你打开个 1MB 的 TXT 文件,YCM还要再那里空跑半天就傻了:

let g:ycm_filetype_whitelist = { 
			\ "c":1,
			\ "cpp":1, 
			\ "objc":1,
                        。。。。
			\ "sh":1,
			\ "zsh":1,
			\ "zimbu":1,
			\ }

好了,祝各位使用愉快。

编辑于 2018-01-18

文章被以下专栏收录

    写一些从想法到脚本实现的详细过程,我希望更多的用户可以自己写脚本