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

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

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

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

  第一步, 升级vim.

  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.

  幸好公司里面可以直接上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.

  网上所有的攻略都说, 通过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, 然后再打包传过去. 发现这个命令执行下去后, 多出来好多东西. 这些东西都是后面用的上的.

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

  

  第三步, 安装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目录的内容. 最终搞定了.

[原创] [YCM] YouCompleteMe安装完全指南的更多相关文章

  1. Cadence OrCad Allegro SPB 16.6 下载及安装破解指南

    Cadence公司的电子设计自动化产品涵盖了电子设计的整个流程,包括系统级设计,功能验证,IC综合及布局布线,模拟.混合信号及射频IC设计,全定制集成电路设计,IC物理验证,PCB设计和硬件仿真建模等 ...

  2. YCM的安装与配置

    花了好几天的时间,总算把YCM装上了.期间遇到了各种问题,主要还是因为刚进linux,对linux环境不熟, 命令资料等查了半天.当然,YCM也普遍被认为是一种安装配置复杂,但使用起来超简单的插件. ...

  3. CentOS 7下的Vim自动补齐插件YouCompleteMe安装及配置

    备注:现在对于 YouCompleteMe 的安装应采用更为简单的方法,即利用 Vundle 来安装这个插件.具体方法可见: Vundle 主页 YouCompleteMe 主页 而 .vimrc 的 ...

  4. Linux --- vim 安装、支持python3的配置、插件自动补全YCM的安装配置及全过程错误总结

    1.git(用来下载vim和相关插件) sudo apt-get install git 2,cmake(用来编译clang-llvm) sudo apt-get install build-esse ...

  5. 【转】Vim自动补全插件----YouCompleteMe安装与配置

    原文网址:http://www.cnblogs.com/zhongcq/p/3630047.html 使用Vim编写程序少不了使用自动补全插件,在Linux下有没有类似VS中的Visual Assis ...

  6. Vim自动补全插件----YouCompleteMe安装与配置

    Vim自动补全插件----YouCompleteMe安装与配置 使用Vim编写程序少不了使用自动补全插件,在Linux下有没有类似VS中的Visual Assist X这么方便快捷的补全插件呢?以前用 ...

  7. Vim插件YouCompleteMe安装记录(号称最难装的Vim插件?)

    使用 PulginInstall 安装就不要想了,如果你没有梯子的话 自己的 ssr 被封,使用的同事的 ss,但是同事设置的加密方式在 linux 上的 ss 应用不支持... 好吧,直接上过程 1 ...

  8. [原创]Java性能优化权威指南读书思维导图

    [原创]Java性能优化权威指南读书思维导图 书名:Java性能优化权威指南 原书名:Java performance 作者: (美)Charlie Hunt    Binu John 译者: 柳飞 ...

  9. [转载]SharePoint 2013测试环境安装配置指南

    软件版本 Windows Server 2012 标准版 SQL Server 2012 标准版 SharePoint Server 2013 企业版 Office Web Apps 2013 备注: ...

随机推荐

  1. 关于mat2gray

    最小的是0,最大的是1,那么介于中间的那些值我们怎么处理? 那么事实上我们试了很多样例之后.. 我猜他是..每个步长step_length=1/(max-min+1) 然后每个值就会变成(val-1) ...

  2. JAVA Day9

    1.StringBuffer类 优点: 内存的管理! StringBuffer: String 增强版 StringBuffer sb = new StringBuffer(); StringBuff ...

  3. 【centOS】账号管理

    一.认识/etc/passwd和/etc/shadow 1.passwd的构造 上图为passwd其中一个用户的用户信息,分别表示为[用户名][密码][UID][GID][注释][家目录][Shell ...

  4. 浅谈SQLiteOpenHelper之onUpgrade例子

    当你看到这个博文,首先你要了解onCreate这个创建方法,再来继续下文!(可以参考我的上一个博文http://www.cnblogs.com/896240130Master/p/6119616.ht ...

  5. java-集合4

    浏览以下内容前,请点击并阅读 声明 对象排序 一个List对象中如果元素类型为String,则其按字母表顺序排序,而如果元素类型为Date,则按照年代排序,那如何判断元素的排序呢?String和Dat ...

  6. CentOS两台服务器利用scp拷贝文件

    yum install -y openssh-clients scp -r -P 26611 /usr/local/ssdb-20160518/ root@10.10.6.199:/usr/local ...

  7. Leetcode Construct Binary Tree from Inorder and Postorder Traversal

    Given inorder and postorder traversal of a tree, construct the binary tree. Note:You may assume that ...

  8. 对Ajax连接的认识~为毛不能上传文件!!!

    最近做毕设的时候需要用到上传图片的功能,但是我的毕设全部的传输都是基于ajax的请求,百度了一圈发现TMD居然说ajax不能上传文件!!当时我就不乐意了啊,那难道其他人都用的是黑科技吗?!又来网上的大 ...

  9. SQL Server 存储过程

    Transact-SQL中的存储过程,非常类似于Java语言中的方法,它可以重复调用.当存储过程执行一次后,可以将语句缓存中,这样下次执行的时候直接使用缓存中的语句.这样就可以提高存储过程的性能. Ø ...

  10. Odoo domain 中的 like, ilike, =like, =ilike 举例说明【转】

    Odoo domain 中的 like, ilike, =like, =ilike 举例说明 Odoo domain 操作符使用场景非常多,很多小伙伴被 like, ilike, =like, =il ...