[原创] [YCM] YouCompleteMe安装彻底指南

  由于实在实在受不鸟ctags了: 代码中有不少类具备相同名字的变量, 好比 "id". 当我想看下当前的这个 "id" 究竟是哪一个id的时候, 可怕的事情粗线了, , , 一口气出来了10几个备选. 并且, 不能跳转到局部变量, 补全也不许确 , , , , , , 好吧, 我终于下定决心来折腾一下YouCompleteMe(YCM). php

  先简要介绍下楼主的开发环境: 一台能连外网的windows pc, 一台内网服务器开发机(64位的redhat6, 这个服务器并不能连外网). 平时搬砖都是用pc ssh 到服务器上, 直接在服务器上用vim码砖. html

  网上介绍YCM安装的攻略也有很多, 可是他们并不适合我当前的处境. 因而决定啃一啃官网文档. 毕竟这个老是最准确和详细的. 官网参见: https://github.com/Valloric/YouCompleteMe#commandspython

  虽然官网文档有一节说的是 "快速安装" , 可是做者说 "并不必定适用于你的环境" . 我并不认为个人环境上能够执行成功, 干脆就按照Full Installation Guide操做了. linux

 

  第一步, 升级vim.git

  YCM要求vim版本为7.3以上, 而个人开发机的版本才是7.0. orz~~. 另外, 也不要期望0疼的内网yum源. 另外, 我也不想从源码编译vim(虽然YCM的做者说了一句"Don't worry, it's easy"~~). 因而开始在goooogle上找vim的rpm包. 那么问题来了, 一共须要几个rpm包呢? 在开发机上用这个命令: rpm -qa | grep vim, 发现有四个包:  vim-common, vim-enhanced, vim-minimal, vim-filesystem. github

  幸亏公司里面能够直接上goooogle, 直接google "vim  rpm" 找到了这么一个网站: https://www.rpmfind.net/linux/rpm2html/search.php. 而后在这里面挨个去搜这几个包, 这个网站会把全部发行版的rpm包都列出来, 对于个人64位redhat6, 要选择el6_x86_64后缀的包. 将他们下载到pc上, 再rz命令传到开发机上, 安装之. 安装以前要先卸载掉以前的vim包. 而后就ok了, 的到了一个7.4版本的vim. (过程并非轻描淡写的两句话就搞定了, 至少我把所谓的 "Unix哲学" fuck了一百遍, , , ,). 数据库

  

  第二步, 下载YCM. json

  网上全部的攻略都说, 经过Vundle来下载YCM. 然而我这是内网机器啊啊啊啊, 并无法使用Vundle. 我也尝试经过在pc上搭了个http代理, 而后让开发机经过pc的代理来访问外网. 然而github上的连接都是https~~~~~Orz, 日了狗了. 仔细观察了下YCM的目录结构, 发现也就是和一个普通的vim插件差很少嘛~~因而在pc上git clone, 而后打个zip包, 再rz到开发机上. 等等, 文档上有一句这样的话: If you don't install YCM with Vundle, make sure you have run git submodule update --init --recursive after checking out the YCM repository (Vundle will do this for you) to fetch YCM's dependencies. 好吧, 先执行下git submodule update --init --recursive, 而后再打包传过去. 发现这个命令执行下去后, 多出来好多东西. 这些东西都是后面用的上的. vim

  将zip包解压到~/.vim/bundle/YouCompleteMe目录. windows

  

  第三步, 安装clang.

  YCM也是基于clang的补全. 安装clang确定是必要的. 用相似于安装vim的方法, 在google上找clang的rpm包. 主要是clang-3.4.2-4.el6.x86_64.rpm, llvm-3.4.2-4.el6.x86_64.rpm, llvm-libs-3.4.2-4.el6.x86_64.rpm这三个包. 这里有一点很重要, 就是64位系统, 必定要装x86_64的包. 开始的时候装错了, clang装了i686的包(i686实际上是32位的), 虽然clang也能正经常使用, 可是YCM编译C++引擎时就会杯具啦. 

  

  第四步, 安装CMake.

  此次内网的yum源终于有了点做用了. 直接yum install cmake就安装成功了. 

  

  第五步, 编译C++引擎(ycm_support_lib).

  若是不是用来补全C++, 那这一步就能够略过啦. 首先创建一个ycm_build目录(随便建在哪里都行. 这个只是一个临时目录而已). 而后cd ycm_build, 再执行

  cmake -G "Unix Makefiles" -DUSE_SYSTEM_LIBCLANG=ON  .   ~/.vim/bundle/YouCompleteMe/third_party/ycmd/cpp

  因为我使用的是系统的clang, 因此要用-DUSE_SYSTEM_LIBCLANG=ON. 

  这时候能够看到ycm_build目录下多出来一堆东西. 

  而后再执行 cmake --build . --target ycm_support_libs. 

  这一步若是顺利, 那再好不过了.

  第一次尝试的时候没有加-DUSE_SYSTEM_LIBCLANG=ON, 编译编译着就失败了. 

  第二次, 加了-DUSE_SYSTEM_LIBCLANG=ON以后, 执行到91%, 仍是报错, 后来发现是clang安装了32位的版本, , , , 

  终于编译经过啦, 这时候, 能够看到~/.vim/bundle/YouCompleteMe/third_party/ycmd目录下多了一个ycm_client_support.so和ycm_core.so, 这应该就是编译出来的C++引擎了. 

  

  第六步, 使YCM生效

  YCM须要一个叫作.ycm_extra_conf.py 的文件做为YCM "入口" . 启动vim的时候会去寻找这个文件, 而后加载它(从当前目录逐层往上找). 这个文件主要的意义在于, 让clang能把当前的源码文件 "编译 " 经过. 由于YCM是基于语义补全的, 会对.cpp进行语法分析和语义分析. 因而就得告诉clang一些具体的编译参数(比较重要的是-I, 得让clang知道去哪些目录下找头文件). 若是clang不能正确的编译.cpp, 那么不少补全的功能就失效了. 经过 :YcmDiags 命令能够查看当前的文件有哪些编译错误. 这个文件有两个主要的部分: 一个是flags数组, 在这里面是填编译选项的. 一个是compilation_database_folder. 这个compilation_database_folder是依赖于clang的一个特性. clang能够在编译的时候导出一个数据库, 而后用这个compilation_database_folder加载这个clang的数据库就能够获得一个最精确最完整的补全和跳转. 因为咱们的代码是gcc编译的(相信用clang编译的项目仍是少数), 因此并无数据库能够导出, 也只能用flags了. (这俩东西是二选一的. 若是有数据库, 优先读数据库, 没有的话就读flags). 

  参照模板修改了一下flags(主要就是加上几个 -I, 由于咱们的代码中引用了不少公司内部的库的头文件, 得让clang找到这些头文件的位置). 

  官网上的指南, 到了上一步就没了. 然而这个时候YCM并无生效呢~~~. 尝试把YouComplete目录下的autoload/youcompleteme.vim和plugin目录下的youcompleteme.vim均拷贝到~/.vim的autoload和plugin下, 这回终于有反应了, 可是报错说找不到ycm_core.so. 打开youcompleteme.vim, 发现里面在尝试从../third_party/ycmd和../python下找内容. 因而把~/.vim/bundle/YouCompleteMe/下的python和third_party目录都拷贝到~/.vim目录下. 

  这个时候, YCM终于能够用了. 写了一个test.cpp, 简单试了下, 不论是补全和跳转, 都很给力, 彻底符合本身的预期. 另外YCM还有语法检查的功能, 可是我以为错误标记太丑, 就给关了(let g:ycm_enable_diagnostic_signs = 0). 

  而后很是开心的用公司项目的源码试了下, , , 顿时脸一黑, , , UnicodeDecodeError: 'utf8' codec can't decode byte 0xcd in position 0: invalid continuation byte. 反复的报这个错误, YCM的各类功能彻底都木有了. T_T顿时感受辛辛苦苦二十年, 一晚上回到解放前. 仔细分析了下, 难道是源码中的中文是gbk的格式致使的? 因而将一个cpp转成utf8格式, 再试下, 果真YCM又能用了. NM, 这怎么办, 难道要把公司的源码都搞成utf8的么? 这不科学. 果断先去goooogle一下, 发现已经有人遇到了这个问题: https://github.com/Valloric/YouCompleteMe/issues/1458 YCM的做者在另一个帖子(https://github.com/Valloric/YouCompleteMe/issues/1378)中是这么回复的 "this might be related to whatever is set as your current file encoding in Vim". 看来猜想的不错, 毕竟做者是歪果仁, 凭啥让人家支持gbk嘞?

  不过毕竟是开源项目, 最大的好处是能够看到源码, 而且能够去修改~~因而决定改改源码让YCM支持gbk. 首先利用这个命令 :YcmDebugInfo , 能够看到报错的详细信息:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/root/.vim/autoload/../python/ycm/youcompleteme.py", line 526, in DebugInfo
    'debug_info' )
  File "/root/.vim/autoload/../python/ycm/client/base_request.py", line 71, in PostDataToHandler
    timeout ) )
  File "/root/.vim/autoload/../python/ycm/client/base_request.py", line 166, in JsonFromFuture
    raise MakeServerException( response.json() )
ycmd.responses.ServerError: UnicodeDecodeError: 'utf8' codec can't decode byte 0xc1 in position 0: invalid start byte
E858: Eval did not return a valid python object

  挨个文件打开看了看, 最后定位到问题可能出如今这里: 

  /root/.vim/third_party/ycmd/ycmd/utils.py, 中有一个RecursiveEncodeUnicodeToUtf8函数. 这个函数大概的意思是把源代码文件变成utf8格式, 而后后面再按utf8格式decode. 若是这一步有问题的话, 那么decode确定会出错. 因而修改了这个函数成以下的样子: 

def RecursiveEncodeUnicodeToUtf8( value ):
  if isinstance( value, unicode ):
    return value.encode( 'utf8' )
  if isinstance( value, str ):
    try:
        value = value.decode('GBK').encode('utf8')
    except UnicodeDecodeError,e:
        pass
    return value
  elif isinstance( value, collections.Mapping ):
    return dict( map( RecursiveEncodeUnicodeToUtf8, value.iteritems() ) ) 
  elif isinstance( value, collections.Iterable ):
    return type( value )( map( RecursiveEncodeUnicodeToUtf8, value ) ) 
  else:
    return value

  主要是针对if isinstance( value, str ):分支的修改. 以前是直接return value了. 这就意味着返回了一个gbk格式的字符串, 放到后面按utf8解析确定会错. 因而将这个值先按gbk decode, 再encode成utf8. 而后, 问题就解决啦!!

======================2016.03.15补充======================

  发现有些头文件中, 使用命名空间 :: 不能补全. 使用YcmToggleLogs命令观察到日志中有一个错误: (仅截取部分出错的调用栈)

  File "~/.vim/third_party/ycmd/third_party/bottle/bottle.py", line 861, in _handle
    return route.call(**args)
  File "~/.vim/third_party/ycmd/third_party/bottle/bottle.py", line 1734, in wrapper
    rv = callback(*a, **ka)
  File "~/.vim/third_party/ycmd/ycmd/../ycmd/watchdog_plugin.py", line 100, in wrapper
    return callback( *args, **kwargs )
  File "~/.vim/third_party/ycmd/ycmd/../ycmd/hmac_plugin.py", line 62, in wrapper
    body = callback( *args, **kwargs )
  File "~/.vim/third_party/ycmd/ycmd/../ycmd/handlers.py", line 126, in GetCompletions
    errors = errors ) ) 
  File "~/.vim/third_party/ycmd/ycmd/../ycmd/handlers.py", line 226, in _JsonResponse
    return json.dumps( data, default = _UniversalSerialize )
  File "/usr/lib64/python2.6/json/__init__.py", line 237, in dumps
    **kw).encode(obj)
  File "/usr/lib64/python2.6/json/encoder.py", line 367, in encode
    chunks = list(self.iterencode(o))

  问题仍然出在处理gbk编码上. 使用以下方法修改:  ~/.vim/third_party/ycmd/ycmd/../ycmd/handlers.py文件, _JsonResponse函数中:

from utils import RecursiveEncodeUnicodeToUtf8

def _JsonResponse( data ):
  response.set_header( 'Content-Type', 'application/json' )
  #return json.dumps( data, default = _UniversalSerialize )
  return json.dumps( RecursiveEncodeUnicodeToUtf8(data), default = _UniversalSerialize )

  用处理编码的函数处理一下传入的data参数, 问题解决. 

   

  至此, YCM真的能够用啦!

  效果确实完爆ctags几条街, 不枉我这么费力的折腾. 

  最后放上个人YCM配置: 

let g:ycm_global_ycm_extra_conf = '/search/odin/code/.ycm_extra_conf.py'
let g:ycm_confirm_extra_conf = 0 
let g:ycm_key_invoke_completion='<C-i>'
set completeopt=longest,menu
autocmd InsertLeave * if pumvisible() == 0|pclose|endif
inoremap <expr> <CR> pumvisible() ? "\<C-y>" : "\<CR>"
let g:ycm_enable_diagnostic_signs = 0 
let g:ycm_enable_diagnostic_highlighting = 1 
let g:ycm_collect_identifiers_from_comments_and_strings = 0 
let g:ycm_complete_in_comments = 0 
let g:ycm_complete_in_strings = 0 
let g:ycm_min_num_of_chars_for_completion = 2 

  更多的选项能够参见官网上的 option 部分. 

 

  总结

  google是在比baidu牛逼太多了. 没有google绝对玩不转. 

  仔细啃文档, 遇到的不少问题, 文档中都有说起. 

 

====================2016.06.22更新=======================

  到了新公司, 要在新的开发机上装ycm. 新公司的开发机虽然能够连外网(也就是能够用vundle来装ycm了), 可是没有root权限, 因而并无办法经过yum或者rpm装clang. 并且也不想手工从源码编译clang. 

  不过幸亏新公司的开发机也是redhat6, 将原来的clang相关的.so拷贝过来, 而后在.bashrc中增长一行export LD_LIBRARY_PATH="LD_LIBRARY_PATH:~/.vim/third_party/ycmd/ycmd/"

  这个时候加载ycm_core.so时就能够正确的找到libclang.so, 从而能够正确的调用clang了. 

====================2016.09.14更新=======================

  公司换开发机了. 新开发机的vim是7.2版本的, 并且没有sudo权限无法更版本, , , YCM又用不了了. 考虑从源码编译安装vim, 可是依赖的编译工具也须要安装, 仍是绕不开sudo权限. 后来脑子灵光一闪, 直接把旧开发机上的vim的可执行文件拷过来就能够嘞. 先 whereis vim, 找到vim安装目录, 在/usr/bin/vim /usr/share/vim中, 而后把对应目录里的内容拷贝到本身的home目录下, 在 bashrc 里面把 vim alias 成home目录下的新版vim. 这时候报错, vim会尝试在/usr/share/vim/vim74中尝试寻找系统配置, 这个路径是编译的时候就肯定了的, 无法运行时去改. 只好去找管理员同窗, 让帮忙建了一个软链接指向本身home目录的内容. 最终搞定了. 

相关文章
相关标签/搜索