廢話
生命在於折騰。
之前不用這個插件的原因:
-
因為要使這個插件起作用,前前后后需要下載幾百MB(win下更是超過了1GB)的東西,包括了Clang編譯器,ycmd的c艹源碼還有ycm本身的vim script,也就是說我安裝這個插件比一個IDE的體積還大。
-
另外一個原因就是就是Windows下使用安裝插件及其麻煩,這樣的話就有可能無法保持相同體驗,還不如不要算了。
現在為什么使用呢?第一句話。
熟悉之后感覺前面的折騰還是非常值得的,體驗接近ide了,ycm不僅提供了語義級別的智能補全,還提供了函數定義跳轉,而所有這些都是非常迅速不會導致vim變卡。
編譯流程
主要參考官方wiki中的這一部分Instructions for 64-bit using MinGW64 clang。
這短短的幾句話忽略了大量的細節,如果是新手的話相信是很難搞得定的。所以我來細化它。
編譯環境——MSYS2
MSYS2是作為MinGw的替代者出現的,它的出現可以說結束了Windows下模擬Linux環境混亂的局面,它移植來自Arch Linux中強大的包管理器:pacman。我們通過pacman來安裝其它東西,比如說MinGw(目的在於提供編譯WIndows本地程序的工具集),git(我們再也不需要裝一個msysgit然后再裝一個MinGw了)。
這是一篇較為詳細的對比:Cygwin 與 MinGW/MSYS/MSYS2,如何選擇?
官方對比How does MSYS2 differ from Cygwin
Cygwin的話,我的理解,它提供接口更加兼容POSIX,在win api和應用之間加了一層兼容層(cygwin1.dll,所以由Cygwin的gcc編譯出來的應用程序在效率是輸給MinGw)的,所以在win下編譯unix下開源軟件,首選是MinGw,你可以看到好多IDE(CodeBlock,QT等)其實包含了MinGW。
官網:msys2,下載對應平台的exe安裝好之后,在開始菜單打開MSYS2 Shell,你還會發現另外兩個快捷方式MinGW-W64 win64 Shell和MinGW-W64 win32 Shell,不過現在它們還打不開。
$ pacman --needed -Sy bash pacman pacman-mirrors msys2-runtime
執行完畢之后,關閉Shell,重新打開。
$ pacman -Su #更新其它組件到最新..
關閉重打開,更換pacman的源。
$ cd /etc/pacman.d
$ gvim -p mirrorlist.mingw32 mirrorlist.mingw64 mirrorlist.msys #你在文件瀏覽器打開這個路徑然后用其它編輯器,這里只是示意而已.
然后在這三個文件最開頭分別添加一行(按照上面的順序)
Server = http://mirrors.ustc.edu.cn/msys2/REPOS/MINGW/i686
Server = http://mirrors.ustc.edu.cn/msys2/REPOS/MINGW/x86_64
Server = http://mirrors.ustc.edu.cn/msys2/REPOS/MSYS2/$arch
# 安裝以下東西..
pacman -Sy
pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-gdb mingw-w64-x86_64-make tmux zsh git mingw64/mingw-w64-x86_64-cmake winpty-git
解釋下,第一個是編譯器,第二個是調試器,第三個工程管理工具make,后面兩個是文本三巨頭之二,在后來是版本管理神器,cmake則是很受歡迎的新一代工程管理工具也是我們編譯YCM需要的工具,最后一個是解決mintty下有些工具卡死沒有輸出的情況的工具。
# winpty使用示例,這里直接調用python會卡死..
$ winpty python
mingw-w64相關默認安裝/mingw64下,所以在MSYS2 Shell下默認找不到gcc命令的,你可以將相關路徑添加到諸如/etc/profile,.bashrc和.zshrc這樣的文件中,也可以在win下面改Path變量。
但更直接的方式在打開前面提到的快捷方式MinGW-W64 win64 Shell,注意別看錯打開win32的shell了。
pacman的基本使用
# 安裝新包
pacman -S <package_names|package_groups>
# 移除包
pacman -R <package_names|package_groups>
# 搜索包,支持正則搜索
pacman -Ss <name_pattern>
編譯一個64位的gvim/vim (非必須,你可以直接到官網下載安裝即可,我有點強迫症,想保證兩者 都是64位的)
打開MinGW-W64 win64 Shell。
VIM官網只提供了32位的程序,按照上面的wiki我們需要編譯一個帶有python2特性的64位vim好來驗證我們是否安裝成功了。
這里可以使用我的編譯腳本和vim配置,從github上脫下之后,里面有一個build_vim.sh腳本用於編譯vim。
$ git clone https://github.com/tracyone/vim vim_conf && cd vim_conf
在執行這個腳本之前你需要,你需要編輯這個腳本最開頭宏定義,里面定義諸如安裝路徑,python安裝路徑,架構等..
其中有python2,python3,ruby,lua,tcl,perl等外部接口支持,注意把不需要的接口對應的變量注釋掉,否則會編譯失敗。
去官網下載最新python2,並加入Path變量,然后安裝,然后修改下面幾個變量
# python2 support
PYTHON=/e/program_files/Python27
DYNAMIC_PYTHON=yes
PYTHON_VER=27
$ ./build_vim.sh all
上面那條命令會下載vim源代碼(接近800MB),並編譯gvim和vim最終打開文件管理器,你只需要雙擊install.exe然后再彈出的界面中輸入d就能完成最后的安裝了。另外,也拷貝了我的vimrc到vim安裝目錄下。所以首次打開時會自動安裝插件,請確保網絡通暢,由於需要安裝較多插件(並且包含了接近200MB的YCM插件),請非常耐心等待。
安裝YCM(非編譯)
上面那一步順利完成的話已經裝好,如果沒有就自己按照下面的方法來做。
第一種方法:
"使用vim-plug插件來安裝ycm,下面這句話加入你的vimrc然后重啟vim,執行`:PlugInstall YouCompleteMe`
Plug 'Valloric/YouCompleteMe', { 'do': 'git submodule update --init --recursive' }
第二種:你可以手動安裝YCM,執行下面命令之后,將YouCompleteMe整個拷貝到vim插件管理器指定的路徑之下。
git clone https://github.com/Valloric/YouCompleteMe.git
cd YouCompleteMe
git submodule update --init --recursive
編譯YCM
打開MinGW-W64 win64 Shell。
首先你得下載一個編譯好的64位的LLVM with Clang Support的安裝包,奇怪的是官網只提供了32位的安裝包,一度我想用MinGw從源代碼編譯一個,后來太多錯誤就放棄了,最后發現這個項目有提供64位的安裝包ClangOnWin。
安裝到你想要安裝的地方,這里我安裝到c:/LLVM。
Windows路徑的變換:如果Windows路徑是"C:\Program Files\Vim",那么你需要用轉義符號轉義空格,並改變斜杠的方向,最后變成"C:/Program\ Files/Vim"。
$ cd <root of vim installed>/vimfiles/bundle/YouCompleteMe
$ mkdir build ;cd build
$ cmake -G "MSYS Makefiles" -DCMAKE_MAKE_PROGRAM=E:/msys64/mingw64/bin/mingw32-make.exe -DPATH_TO_LLVM_ROOT=C:/LLVM . E:/program_files/Vim/vimfiles/bundle/YouCompleteMe/third_party/ycmd/cpp
$ mingw32-make ycm_core
至此結束,要確保正常運行,需要保證添加到Path環境變量中,在MSYS2 Shell中同樣需要保證這兩個路徑在Path變量中,否則會出現加載模塊的錯誤。
- Python2所在路徑
- mingw64/bin路徑
- llvm所在路徑
也可以分別將相關的dll拷貝到YouCompleteMe/third_party/ycmd路徑下:libstdc++-6.dll,libwinpthread-1.dll,libclang.dll和libgcc_s_seh-1.dll。
使用教程和配置
使用YouCompleteMe對工程進行補全,需要為每個工程提供一個項目文件:.ycm_extra_conf.py,主要指定的是編譯器的option,比如說連接的庫,庫路徑,頭文件路徑,宏定義,編譯器優化標志等等。
這里要介紹一個插件,YCM-Generator,他能根據項目中Makefiles等文件生成對應的.ycm_extra_conf.py文件。很多時候這東西是不錯的,但是某些情況還是不夠靈活,比如說如果Makefile里面是交叉編譯器的話,它不支持。
項目根目錄下打開vim並執行:call GenYcm()(我的配置里面的函數)。
另外一個插件ycm_simple_conf,這個插件需要編寫一個xml文件,然后根據這個xml文件生成,這樣的話就比較靈活點。
在每個項目的根目錄下編寫.ycm_simple_conf.xml文件。
<project type="c++">
<include path="/home/local/libA/include"/>
<include path="/home/local/libB/include"/>
<include path="include"/>
<include path="build"/>
</project>
這個函數是為插件YCM-Generator的寫的。
function! GenYCM()
let l:cur_dir=getcwd()
cd $VIMFILES/bundle/YCM-Generator
:silent execute ":!./config_gen.py ".l:cur_dir
if v:shell_error == 0
echom "Generate successfully!"
:YcmRestartServer
else
echom "Generate failed!"
endif
exec ":cd ". l:cur_dir
endfunction
"下面三個映射是YCM提供函數跳轉功能
nnoremap <leader>jd :YcmCompleter GoToDefinitionElseDeclaration<CR>
nnoremap <leader>jl :YcmCompleter GoToDeclaration<CR>
nnoremap <leader>jf :YcmCompleter GoToDefinition<CR>
"由於Clang編譯器的限制,ycm跳轉功能有致命缺陷,無法跳到另外文件的函數定義上面
"所以我寫了YcmGotoDef函數加上插件a.vim,實現粗略的跳轉,原理是當Ycm跳到對應函數
"聲明的頭文件時使用a.vim的功能跳到對應源文件,在使用正則搜索,跳到函數定義上面。
nnoremap <Leader>g :call TracyoneGotoDef("")<cr>
nnoremap <C-\>g :call TracyoneGotoDef("sp")<cr>
function! TracyoneGotoDef(open_type)
let l:ycm_ret=s:YcmGotoDef(a:open_type)
if l:ycm_ret < 0
try
execute "cs find g ".expand("<cword>")
catch /^Vim\%((\a\+)\)\=:E/
call s:EchoWarning("cscope query failed")
if a:open_type != "" | wincmd q | endif
return -1
endtry
else
return 0
endif
return 0
endfunction
func! s:YcmGotoDef(open_type)
let l:cur_word=expand("<cword>")."\s*\(.*[^;]$"
:redir => l:msg
execute a:open_type
silent! execute ":YcmCompleter GoToDefinition"
:redir END
let l:rs=split(l:msg,'\r\n\|\n')
"make sure index valid
if get(l:rs,-1,3) !=3 && l:rs[-1] =~ 'Runtime.*'
:redir => l:msg
silent! execute ":YcmCompleter GoToDeclaration"
:redir END
let l:rs=split(l:msg,'\r\n\|\n')
if get(l:rs,-1,3) != 3 && l:rs[-1] !~ 'Runtime.*'
execute ":silent! A"
" search failed then go back
if search(l:cur_word) == 0
execute ":silent! A"
return -2
endif
return 3
elseif get(l:rs,-1,3) == 3 "not exist no error
return 0
else
return -3
endif
else
return 1
endif
endfunc
"當tab補全時vim上面會分裂一個小窗口顯示函數變量原型,我們按下esc或者結束補全時
"自動關閉那個窗口
autocmd misc_group InsertLeave * if pumvisible() == 0|pclose|endif
let g:syntastic_always_populate_loc_list = 1
let g:ycm_confirm_extra_conf=0
let g:syntastic_always_populate_loc_list = 1
let g:ycm_semantic_triggers = {
\ 'c' : ['->', ' ', '.', ' ', '(', '[', '&'],
\ 'cpp,objcpp' : ['->', '.', ' ', '(', '[', '&', '::'],
\ 'perl' : ['->', '::', ' '],
\ 'php' : ['->', '::', '.'],
\ 'cs,java,javascript,d,vim,python,perl6,scala,vb,elixir,go' : ['.'],
\ 'ruby' : ['.', '::'],
\ 'lua' : ['.', ':']
\ }
let g:ycm_collect_identifiers_from_tag_files = 1
let g:ycm_filetype_blacklist = {
\ 'tagbar' : 1,
\ 'qf' : 1,
\ 'notes' : 1,
\ 'unite' : 1,
\ 'text' : 1,
\ 'vimwiki' : 1,
\ 'pandoc' : 1,
\ 'infolog' : 1,
\ 'mail' : 1
\}
let g:ycm_global_ycm_extra_conf = $VIMFILES . "/bundle/YouCompleteMe/third_party/ycmd/cpp/ycm/.ycm_extra_conf.py"
關於.ycm_extra_conf.py的生成,最后我用ultisnips這個插件,自定義snippets,生成.ycm_extra_conf.py的配置,你可以參考
snippets中python.snippets的寫法。
ubuntu 32bit 安裝ycm流程
下面假設你已經通過vim的vundle之類的插件管理器安裝好Ycm了。
編譯安裝clang編譯器到/opt/Clang
sudo update;sudo apt-get upgrade
sudo apt-get install g++ subversion cmake autoconf automake
cd ~;mkdir -p work/clang && cd work/clang
svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm
cd llvm/tools
svn co http://llvm.org/svn/llvm-project/cfe/trunk clang
cd ../../
mkdir build && cd build
cmake ../llvm
cmake --build .
cmake -DCMAKE_INSTALL_PREFIX=/opt/Clang -P cmake_install.cmake
編譯ycm
cd ~/.vim/bundle/YouCompleteMe/
mkdir build
cd build
cmake -G "Unix Makefiles" -DPATH_TO_LLVM_ROOT=/opt/Clang . ~/.vim/bundle/YouCompleteMe/third_party/ycmd/cpp && make ycm_core
其它
ycm的github主頁上已經提供了在vs下編譯ycmd的官方方法,詳細點這里
