1. 程式人生 > >Vim下的程式碼自動補全 使用ctags和OmniCppComplete taglist + cscope + global

Vim下的程式碼自動補全 使用ctags和OmniCppComplete taglist + cscope + global

【概念】

程式碼自動補全和程式碼跳轉閱讀,應該是作為程式設計師最常用的功能之一了,具體二者是指什麼我就不解釋了。微軟的Visual Studio就是靠這兩樣必殺技牢牢佔據著廣大windows程式設計師的心(這裡面要有強大的VS外掛Visual Assistant X一份功勞)。。。但是Linux程式設計師其實更幸福,不花錢就能搞定這兩大功能。

從本質上說,這二者的實現都依賴於一樣東西:tag。tag就是程式中的關鍵詞,在C++中主要包括:變數、函式名、類名等。程式碼自動補全實際上是tag的匹配(例如,程式設計師輸入cla時,由於存在class這個c++的tag,就可以用class匹配cla);程式碼跳轉閱讀實際上是tag的查詢(例如,程式設計師要查詢一個函式func(),只需要在別的檔案中尋找這個func這個tag的位置即可)。

【準備】

我現在的系統是Ubuntu Desktop 10.04 LTS版本。當然,一切工作的前提是你能上網,而且配置好了一個可用的源。

1. 安裝Vim和Vim基本外掛

我們需要首先安裝好Vim和Vim的基本外掛。這些使用apt-get安裝即可:

sudo apt-get install vim vim-doc vim-scripts

其中vim-scripts是vim的一些基本外掛,包括語法高亮的支援、縮排等等。

2. Vim配置檔案

Vim強大的功能,其來源基本上就兩個地方:外掛,以及配置檔案

上面已經下載了Vim的基本外掛,下面說一下Vim的基本配置。Vim本身的系統配置資料夾是在/usr/share/vim/和/etc/vim/兩個資料夾下,我們一般不要去改變這些,改了以後不容易恢復。我們需要在使用者資料夾下建立自己的配置檔案。假設使用者的名字是user。進入使用者資料夾(/home/user/)之後,用gedit新建一個名叫.vimrc的檔案:

gedit .vimrc

之所以用gedit是因為vim裡面不能拷貝貼上,為了方便大段大段的文字貼上,還是先用gedit吧。。。

然後把下面的文字拷貝進這個檔案之後儲存:

" This line should not be removed as it ensures that various options are
" properly set to work with the Vim-related packages available in Debian.
runtime! debian.vim

" Uncomment the next line to make Vim more Vi-compatible
" NOTE: debian.vim sets 'nocompatible'.  Setting 'compatible' changes numerous
" options, so any other options should be set AFTER setting 'compatible'.
set nocompatible

" Vim5 and later versions support syntax highlighting. Uncommenting the
" following enables syntax highlighting by default.
if has("syntax")
  syntax on
endif

" detect file type
filetype on
filetype plugin on

" If using a dark background within the editing area and syntax highlighting
" turn on this option as well
set background=dark

" Uncomment the following to have Vim jump to the last position when
" reopening a file
if has("autocmd")
  au BufReadPost * if line("'/"") > 1 && line("'/"") <= line("$") | exe "normal! g'/"" | endif
  "have Vim load indentation rules and plugins according to the detected filetype
  filetype plugin indent on
endif

" The following are commented out as they cause vim to behave a lot
" differently from regular Vi. They are highly recommended though.
set showcmd		" Show (partial) command in status line.
set showmatch		" Show matching brackets.
"set ignorecase		" Do case insensitive matching
"set smartcase		" Do smart case matching
set incsearch		" Incremental search
set autowrite		" Automatically save before commands like :next and :make
set autoindent
set smartindent
set tabstop=4
set shiftwidth=4
"set hidden             " Hide buffers when they are abandoned
set mouse=a		" Enable mouse usage (all modes)
set number			" Enable line number
set previewwindow	" 
set history=50	" set command history to 50

注意:單個雙引號開頭的行是註釋掉的,不起作用的。

儲存檔案之後,啟動Vim。在我的電腦商,此時Vim已經是這種效果了(語法高亮挺漂亮的–這個是由vim-scripts中的外掛支援的):

vim_origin

3. ctags安裝和使用

或者

sudo apt-get install exuberant-ctags (不能是emacs的ctags)(建議使用這個)

ctags的作用就是把c/c++檔案分割成tag,並預設儲存在一個叫tags的檔案中。假設我們現在有一些.h/.c/.cpp檔案在/home/user/code/資料夾下,那麼在這個資料夾下執行下面的命令就能生成此專案的tags檔案:

ctags -R --c++-kinds=+p --fields=+iaS --extra=+q

此時在這個目錄下就能找到一個叫tags的檔案了。

4. 程式碼跳轉

經過上面的步驟之後,就可以在本專案內實現程式碼跳轉了,但不能跳轉到不是在本專案的程式碼中定義的tag。例如在上面的圖中,本地定義了一個類A,在main函式中定義了一個A類的物件,我們可以按“Ctrl+]”組合鍵跳轉到class A那一行。然後可以按“Ctrl+O”跳轉回來。

如果想實現跳轉到非本專案的tag,則必須讓Vim知道這些tag在哪。剛才的專案在/home/user/code/目錄下,現在假設又有一個專案在/home/user/program/下,那麼我們需要在此專案下生成program這個專案的tags檔案(還是用上面的ctags命令)。

生成之後,要告知Vim,需要在Vim的配置檔案中(/home/user/.vimrc)新增下面的內容:

" -- ctags setting --
set tags=tags
set tags+=./tags	" add current directory's generated tags file
set tags+=~/program/tags " add new tags file

最後一行就表示在搜尋tags檔案的時候,也要搜尋/home/user/program/資料夾下的tags檔案。儲存以後重啟,此時應該就能實現不同專案間的跳轉了。這是因為我們經常會基於一些庫開發軟體,我們需要檢視庫中函式或者類的定義的時候,會經常需要此功能。

5. 自動補全的安裝

這個外掛是基於ctags資料庫即tags檔案實現的。

上面我們也介紹瞭如何獨立於系統配置檔案之外,建立自己的Vim配置檔案。當我們自己下載Vim外掛的時候,也可以另外建立目錄,放置我們自己的外掛。這個目錄一般為/home/user/.vim,另外還需要建立一個外掛子目錄,一個外掛文件子目錄,以上的可以進入/home/user目錄下通過下面的命令執行:

mkdir .vim
cd .vim
mkdir plugin
mkdir doc

然後將下載到的zip檔案放到.vim目錄下,直接解壓(外掛也包括兩個目錄,一個plugin,一個doc,所以它會自動放置到對應的目錄)。

注意,我在ubuntu10.10把omnicppcomplete-0.41.zip解壓後有三個資料夾:after, autoload, doc我直接把這三個資料夾放在 .vim這個資料夾下,並沒有放在plugin資料夾裡面,因為我把這三個資料夾放在plugin資料夾下面開啟vim也就是用vim編輯一個檔案的時候會又出錯資訊,但是放在 .vim也可以正常自動補全,兩種方法都試一下看看。

unzip omnicppcomplete-0.41.zip

外掛這就算安裝完了。然後再到vim配置檔案中加入如下的配置:

"-- omnicppcomplete setting --
set completeopt=menu,menuone
let OmniCpp_MayCompleteDot = 1 " autocomplete with .
let OmniCpp_MayCompleteArrow = 1 " autocomplete with ->
let OmniCpp_MayCompleteScope = 1 " autocomplete with ::
let OmniCpp_SelectFirstItem = 2 " select first item (but don't insert)
let OmniCpp_NamespaceSearch = 2 " search namespaces in this and included files
let OmniCpp_ShowPrototypeInAbbr = 1 " show function prototype  in popup window
let OmniCpp_GlobalScopeSearch=1
let OmniCpp_DisplayMode=1
let OmniCpp_DefaultNamespaces=["std"]

(前幾行就是提供了C++中的./->/::等操作符的提示和自動完成)。

6. 自動補全功能的測試

C++開發中經常會用到C++標準庫的程式碼,因此STL的自動補全很重要。可以下載一份C++標準庫的原始碼來測試一下自動補全功能。

sudo apt-get install build-essential

然後在/usr/include/c++下就可以找到標準庫的標頭檔案了。在此資料夾下生成tags檔案,並新增到vim的配置檔案中(不再重複上面的內容),然後在程式設計的時候就可以使用自動補全功能了。

我直接把/usr/include/下的所有檔案都做了ctags,也就是說如果你想把哪個類庫加入到vim的程式碼提示功能下面,你只需要在這個類庫的最底層資料夾下面執行  ctags -R --c++-kinds=+p --fields=+iaS --extra=+q ,這時候會在這個資料夾下面生成一個tags檔案,然後把這個tags檔案加入到/home/user/.vimrc配置檔案中(請參考上面)

下面展示了一張vector的函式補全的效果圖:

vector_auto_complete

PS:在自動補全的點,Vim必須知道可能補全的定義。比如說,在namespace std名稱空間下的變數和函式,必須要用using namespace std;暴露出來,否則是不能補全的。在.cpp檔案中還可以,在.h檔案中這樣就不是好的做法了。暫時不知道這個問題是由於我自己配置錯誤還是程式沒有實現。

1.其他的VIM外掛的功能和使用:

taglist 精幹,需要ctags的支撐 ,直接可以在左邊列出函式列表,全域性引數列表。(可以排序)

cscope 比較強大,可以對函式以及部分型別定義進行跳轉,但有些BUG,好像在某些條件下無法正確找到分析列舉的定義。也就是檢視你所使用的型別的定義。

global新版本可以嵌入vim使用,提供比較完整解析和型別索引,和cscope比,稍微差些的就是對型別引用的列印列表中沒有標識這個引用在什麼函式中進行的。其實我的感覺global可能不是為vim所生,它的主要目的是用html的方式進行表達相關的關聯關係和索引,使用起來感覺沒有cscope的那麼貼切。

cscope和global都是檢視你所使用的型別的定義。以上已經說出了兩者的區別。

大家可以根據自己的需求和實際情況進行安裝,以上的外掛都是基於ctags的基礎上的,因此必須先裝ctags再裝其他外掛。

2、相關的擴充套件的安裝
程式碼
apt-get install  exuberant-ctags  cscope global

cppcomplete 和 taglist 要到www.vim.org的擴充套件列表中下載。

taglist 的下載地址:
http://www.vim.org/scripts/script.php?script_id=273
cppcomplete 的下載地址:
http://www.vim.org/scripts/script.php?script_id=527

然後在 建立目錄
程式碼
$HOME/.vim/plugin


將下載的cppcomplete.vim 和 taglist.vim拷貝到$HOME/.vim/plugin中。
安裝了global後,最新4.8.6 以上版本有帶vim的擴充套件,將它也拷貝到 $HOME/.vim/plugin

debian sid 版本global安裝後擴充套件檔案在
程式碼
/usr/share/doc/global/examples/gtags.vim.gz

需要拷貝到$HOME/.vim/plugin後解壓,解壓方法:
程式碼
gzip -d  gtags.vim.gz



3、使用這些工具
1)準備工作,先修改一下$HOME/.vimrc檔案

為了更好的使用cscope請新增如下內容,這樣Ctrl-]的跳轉將由cscope的tags進行分析。
程式碼

if has("cscope")
set csprg=/usr/bin/cscope
set csto=0
set cst
set nocsverb
" add any database in current directory
if filereadable("cscope.out")
cs add cscope.out
" else add database pointed to by environment
elseif $CSCOPE_DB != ""
cs add $CSCOPE_DB
endif
set csverb
set cscopetag
set cscopequickfix=s-,g-,c-,d-,t-,e-,f-,i-
endif


2)使用cscope
cscope的tag生成最簡單的方法是:
c中:cscope -Rbq
在你的開發工程的最上層目錄執行cscope-indexer,它會遍歷下面的所有目錄,生成兩個檔案,一個是cscope.files,這個檔案記錄需要生成tags的檔名,可以手工修改,另一個是cscope格式的tags檔案cscope.out。

c++中使用cscope
-R: 在生成索引檔案時,搜尋子目錄樹中的程式碼
-b: 只生成索引檔案,不進入cscope的介面
-k: 在生成索引檔案時,不搜尋/usr/include目錄
-q: 生成cscope.in.out和cscope.po.out檔案,加快cscope的索引速度

#!/bin/sh
find . -name "*.h" -o -name "*.c" -o -name "*.cc" -name "*.cpp" > cscope.files
cscope -bkq -i cscope.files
ctags -R

完成後,你在生成了cscope.out的目錄開啟工程的任意檔案,就可以使用Ctrl-]跳轉查詢型別定義了。
程式碼
進入vim後第一件事是要把剛才生成的cscope檔案匯入到vim中來, 用下面的命令:
        :cs add /home/wooin/vim71/cscope.out /home/wooin/vim71 
        上面這條命令很重要, 必須寫全, 不能只寫前半句:
        :cs add /home/wooin/vim71/cscope.out

cs f s xxxx  查詢xxxx出現的地方,它能詳細列出哪些檔案的哪行的哪個函式引用,以及該行的內容,比較不錯


cscope find的用法:
    cs find c|d|e|f|g|i|s|t name
    0 或 s 查詢本 C 符號(可以跳過註釋) 
    1 或 g 查詢本定義 
    2 或 d 查詢本函式呼叫的函式 
    3 或 c 查詢呼叫本函式的函式 
    4 或 t 查詢本字串 
    6 或 e 查詢本 egrep 模式 
    7 或 f 查詢本檔案 
    8 或 i 查詢包含本檔案的檔案 
cscope find得快捷方式:
新增到~/.vimrc中, 並重啟vim:
nmap <C-c><C-k> :cs find s <C-R>=expand("<cword>")<CR><CR>
nmap <C-c><C-g> :cs find g <C-R>=expand("<cword>")<CR><CR>
nmap <C-c><C-l> :cs find c <C-R>=expand("<cword>")<CR><CR>
nmap <C-c><C-t> :cs find t <C-R>=expand("<cword>")<CR><CR>
nmap <C-c><C-e> :cs find e <C-R>=expand("<cword>")<CR><CR>
nmap <C-c><C-f> :cs find f <C-R>=expand("<cfile>")<CR><CR>
nmap <C-c><C-i> :cs find i ^<C-R>=expand("<cfile>")<CR><CR>
nmap <C-c><C-d> :cs find d <C-R>=expand("<cword>")<CR><CR>



它的所有命令的使用請參考:
程式碼

help cscope


3)使用taglist
taglist的功能是即時生成當前檔案的函式列表和全域性變數列表,便於索引。
在vim中命令模式下使用
Tlist 開啟或者關閉當前檔案的索引;
TlistSync 立即在開啟的索引視窗中定位當前的游標所在位置屬於哪個函式或者結構定義中。
還有其他命令,請參考它的幫助檔案。

4)ctags的使用
其實cscope是用來替代ctags功能的,ctags的應用廣泛,還有其他很多的軟體依賴它。

例如要生成cppcomplete需要的tags檔案的話,需要執行下面的命令:
程式碼

ctags -n -f cppcomplete.tags --fields=+ai --C++-types=+p * -L cscope.files

注意,我在最後使用了引數"-L cscope.files" 這僅僅是借用拉cscope生成的檔案索引來幫助ctags去查詢相關工程檔案生成tags。

5)使用cppcomplete
cppcomplete的使用我只是簡單的發現它可以根據上述方法生成的tags檔案來自動補全型別成員。
使用很簡單,就是在你需要補全型別成員時,按F8鍵就可以拉。它一般第一次使用時,需要讓你確認是使用已有的tags檔案還是再生成一次。一般我們自己控制生成tags檔案的時機,它就使用就行拉。
但它也受到ctags的分析能力比較差的限制,可能不是每次都能幫到你。

6)使用global
在工程的最上層目錄執行命令:
gtags
等待它完成所有需要的global自己格式的tags的生成
生成完後,需要的就是在生成tags的目錄開啟你的工程的任意檔案,用如下命令查詢相關型別引用和關聯關係:
程式碼

Gtags xxxx      查詢xxxx的定義
Gtags -r xxxx   查詢xxxx的引用
Gtags -s xxxx   查詢xxxx出現的地方


它的提示資訊很有顯,不如cscope更直觀,但對大型分析的比較完整。
有時它無法正常分析的型別 可以試試用帶-s 的引數的方式找找型別定義。再不行,我就不清楚拉 smile.gif


indent -kr -nut -ts2 -i3 -l-1 *.c
valgrind --leak-check=full --leak-resolution=high --show-reachable=yes

在你的開發工程的最上層目錄執行cscope-indexer
ctags -n -f cppcomplete.tags --fields=+ai --C++-types=+p * -L cscope.files

:!ctags -R
Tlist 開啟或者關閉當前檔案的索引;
TlistSync 立即在開啟的索引視窗中定位當前的游標所在位置屬於哪個函式或者結構定義中。
u             更新taglist視窗中的tag
s             更改排序方式,在按名字排序和按出現順序排序間切換
[[            跳到前一個檔案
]]            跳到後一個檔案

將游標停放在函式或者變數的位置,gd會高亮出當前檔案中所有的函式或者變數,按n檢視下一個
 gg游標返回到檔案的頂部 

ctrl + p  : 自動匹配 補齊
ctrl +] :跳轉   ctrl + T : 返回跳轉
把游標移到"stdio.h"的任一字元上,鍵入"gf",則Vim會自動開啟/usr/include/stdio.h檔案。使用"Ctrl-O"  可返回到原先的檔案中。

"cscope find"的用法:
cs find c|d|e|f|g|i|s|t name
0 或 s     查詢本 C 符號(可以跳過註釋)
1 或 g     查詢本定義
2 或 d     查詢本函式呼叫的函式
3 或 c     查詢呼叫本函式的函式
4 或 t     查詢本字串
6 或 e     查詢本 egrep 模式
7 或 f     查詢本檔案
8 或 i     查詢包含本檔案的檔案
:cw  在視窗中顯示

vim :
複製5行: y5y , p
游標在單詞間移動 w  b
行移動: 0(行首) $(行尾)
分割視窗:   :split   切換 ctrl + w
向下滾屏:  ctrl +D ctrl +u    
替換 : %s/four/4/g
刪除多行 n dd