由於實在實在受不鳥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目錄的內容. 最終搞定了.