好長時間沒有上來更新了, 今天趁老板不再上來休閑一下. 本章要說的是和vim的tags相關的內容. 之所以在跳轉之后就說明tags是因為這個功能相當的重要和實用. 好的東西自然是需要提前分享的.
首先, 要說的是關於vim使用ctags, cscope的相關教程, 網絡上已經有相當詳盡的文章可以搜索到. 這里不會在重復大多數網絡上可以搜索到的入門教程了. 並且在此提醒閱讀本篇博客的讀者, 如果你現在對ctags和cscope等並不了解, 那么請先在網絡里google所有和他們相關的教程, 花上大約至少一天上的時間認真研究他們的基本用法. 等到對他們有了基本的了解之后再回來看這里的說明你將會有更大的收獲.
好了, 廢話我就不再多說, 下面進入真題, 首先要說的是ctags是一個linux上很普遍的源碼分析工具, 可以將代碼中的函數變量等定義的位置記錄在一個名稱為tags的文件. 類似於數據庫記錄功能. tags文件的產出最簡單的方法是在需要生成tags的工程項目的根目錄下執行ctags -R命令, 這會調用tags遞歸的掃描當前目錄以及所有子目錄中可以被tags識別的文件所以文件數據信息都會匯集到tags文件中.這里總結一下幾個本人暫時知道的幾個需要注意的地方:
->ubuntu在默認系統環境中已經集成了ctags功能, 但這個ctags並不是完整的版本, 為了保證vim可正常的使用tags建議安裝完整的ctags支持, ubuntu完整的ctags被重名為exuberant-ctags但依然可以通過ctags索引到, 安裝命令如下:
sudo apt-get install ctags
->如果你的vim有使用echofunc插件來顯示函數的參數定義, 那么在使用ctags生產索引文件時需要使用如下附加參數:
ctags -R --fields=+lS
->ctags默認生成的索引文件只包含了對C語言的語法分析, 如果你需要ctags支持對C++語法分析. 需要使用下面的命令:
ctags -R --c++-kinds=+p --fields=+iaS --extra=+q
->如果你在C語言編寫的代碼中使用上面提到的C++命令生成tags, 那么你將驚訝的發現, 當你希望通過ctags跳轉到光標下函數定義的地方的時候, vim總是跳轉到這個函數定義的地方, 原因是ctags的C++命令增加了額外的語法分析以便支持C++更加復雜的語法結構, 這種額外的語法分析用在C語言中的時候就會出現跳轉默認定位到函數聲明的地方.
->ctags默認是支持C和C++的由於本人暫時沒有做過其他語言的開發工作, 所以不清楚ctags是否支持其他編程語言.
->請不要每次生成tags的時候都使用上面的命令, 包括以后其他的很多功能的實現都是如此, 命令是實現一個功能的原型, 如果僅僅使用命令在類unix(windows下面也是如此)的操作系統上實現常用的操作和功能, 那么你的操作將會變得很低效. 個人認為vim的使用也有這個規律, 最普遍的操作最好不要使用長長的命令來實現. 取而代之的方法是設置快捷鍵和命令的簡寫映射.
vim中的tags使用
vim配置tags使用的過程是, 先在終端中生成一個項目的tags文件, 再在vimrc中告訴vim哪里去找這個tags. 假如我們有一個項目在/home/boddy/hello/下面, 並且這個目錄下已經生成tags文件.那么只要在vimrc文件中添加如下語句就可以讓vim在每次啟動的時候自動找到這tags了:
set tags+=/home/boddy/holle/tags
如果你有多個tags需要使用,可以重復上面的語句, 也可以在同一個語句中加入多個路徑,每個路徑用","隔開.
這種方法是我最初使用tags的的方式, 缺點顯而易見, 每當我們開始新的項目的時候,不得不重新修改set tags后面的路徑, 雖然這樣的修改並不麻煩, 也不需要經常操作. 但不免還是讓人覺得太死板不智能.
為了讓vim對tags的支持更加自動話, 首先想到的方法是在vimrc中添加 settags+=./tags語句, 這樣當前目錄下的tags就不需要我們手動添加了.但我們不可能永遠在項目的根目錄操作. 如果在項目的子目錄里操作, 這個方法將會失效. 事實上, 任何一個項目文件夾都有一個顯著的特點: 任何子目錄遞歸向上都可以到達項目的根目錄. 這個特點將會在下面的實踐中大顯身手, 讓我們從各種和項目整體相關的操作中解脫出來. 這包括ctags cscope lookupfile等相關索引文件的自動添加和隨時更新. 在項目任意子目錄下的任意時刻對項目的自動make和make clean等. 這些最頻繁操作要么是自動完成的要么是通過vimrc的map功能映射到不同的快捷鍵上, 這會讓我們的vim具有所以IDE所具有的便捷性的同時保持了vim在編碼方面無比的高校性和在功能定制方便的無比的可定制性. 我想這就是我為什么寧願選擇折騰vim而沒有使用成熟的IDE的原因.
上面提到工程項目的目錄遞歸特性, 實際上, 我們需要使用一個tags最典型的需求是希望在一個項目中快速的找到函數,變量,宏等定義的地方. 這些查找大多是在項目文件夾內完成. 因此, 只要我們能上vim實現對當前所在目錄的遞歸查找功能, 這個需求就會滿足. 只要項目的根目錄下存在tags文件, 任何時候任在任何一個項目的子目錄下使用vim都可以正確的找到這個tags, 同時如果我們的電腦中存在多個項目, 當我們切換到另一個項目的子目錄的時候, vim遞歸查找到永遠是當前項目根目錄下面的tags(前提是tags存在於項目根目錄), 無意間就實現tags的自動切換功能.
實現vim對tags的自動遞歸查找其實很簡單, 因為vim已經實現了這個功能, 只是默認沒有開啟. 在vimrc添加下面兩行配置, 就會是見證奇跡的時刻:
set autochdir
set tags=tags;
set autochdir表示自動切換目錄的意思, set tags=tags;表示自動查找, 這兩句同時設置vim即可實現遞歸的tags查找, 注意: set tags=tags;這一句的最后有一個分號, 這個分號是不能省略的. vim的配置文件使用的是vim自己的腳步語言. 這里是少數幾個在行尾需要使用分號的地方之一.
最后需要說明的一點: ctags在默認的命令下生成的tags中使用的是相對路徑的存放所有查找結果, 這在多數情況下是一個優點, 因為相對路徑不依賴於項目的根目錄所在位置. 這樣在整個項目轉移到別的位置的時候, 相對路徑的tags依然可以正常的實現跳轉. 不過相對路徑的tags並不是沒有缺點, 如果你的vim中使用了FuzzyFinder來作為查找項目文件的工具, 你將驚訝的發現如果你在項目根目錄的子目錄下執行項目文件查找,在找到了想要的文件並最后回車跳轉的時候如果tags使用的是相對路徑, 這一步將會失敗, 因為FuzzyFinder無法正確的通過當前的目錄(不是項目根目錄)加上tags中的相對路徑計算出正確的文件位置. 解決的辦法是在ctags生產tags文件的時候使用絕對路徑, 使用方法是在ctags -R 的命令后面添加項目根目錄的絕對路徑, 如: ctags -R /home/boddy/hello/ , 使用絕對路徑可以保證tags在任何調用他的工具中正確的找到文件位置. 還有一個需要注意的是, 如果現在有下面的需求,我們需要對/usr/include下面所以文件和子目錄bits/做tags處理, 如果你按照上面的語法推導應該得出命令為(當前位置已經在/usr/include): ctags * bits/ /usr/include/, 可是讓我們失望的是這個樣執行沒有出錯但產生的結果竟然是相對路徑, 實際上這種情況下生成絕對路徑的命令寫法應該是: ctags /usr/include/* /usr/include/bits/* . 使用絕對路徑唯一的缺點是如果項目移動了, tags必需重新生成, 鑒於我們的項目在自己的電腦中移動的需求很小的同時重新生成一tags的時間也就是幾秒鍾的事情(只要不是超級大的項目,tags生成還是很快的), 我個人選擇了在任何時候使用ctags都使用絕對路徑, 當然這個功能是在vim中通過參數自動實現的, 本文的最后將會提到.
好了, vim對tags的自動查找功能實現了, 只是一個開始哦. 下面的介紹將會進一步增強vim在代碼跳轉和搜索上的能力.
ctags的優點是使用簡單, 生成的tags文件比較小, 使用時對tags的檢索也相對比較快, 對c語言的函數和宏定義跳轉相當准確高效. 他的缺點功能相對單一, 沒辦法實現對一個關鍵字出現位置的統計, 一個函數被調用的位置統計, 局部變量的定義跳轉往往沒有效果等. 我們在編碼的時候除了隨時查看函數定義的需求以外, 另一個比較常用的需求是對一個函數在項目中出現位置查找. 這個功能ctags是沒有的, 為了讓vim實現這個功能, 我們需要借助另一tags索引工具:cscope
網絡上對cscope的用法說明也很多, cscope的使用要不ctags復雜的不少,因為一樣的, 請先自行google cscope相關的教程提前了解.
cscope的用法和上面的ctags的用法很相似, 不同的地方有:
->生成索引文件的命令不同: cscope -Rbkq
-> 索引文件的名字不同: 一共有三個, 主要的文件是cscope.out, 另外兩個cscope.in.out cscope.po.out 是在生成命令中使用q參數才會有的文件, 這兩個文件可以加速cscope的查找速度, 注意 : windows下面也可以使用tags和ecscope的,但cscope的-q參數並不支持, 因為windows下面將不會有cscope.in.out 和 cscope.po.out文件
->vim中用法不同: ctags 默認在vim中典型的用法是ctrl+] 跳轉到光標下的關鍵字的定義處, ctrl+t跳轉回來. cscope在vim下面並沒有映射快捷鍵. 因為cscope的查找模式有近八種之多, 因此和vim配合使用的時候,默認是通過一組:cs find 開頭的命令來實現的. cscope的官方網站上有一份在vim中使用的建議快捷鍵映射配置. 個人覺得cscope雖然查找方式眾多, 但真正經常用到的也就三到四個. 可以試着將其分配到容易操作的鍵位上來. 官方的那個映射還是稍微有點難按了點.
->ubuntu默認系統下完全不支持cscope, 需要通過下面的命令安裝cscope:
sudo apt-get install cscope
cscope是一個ctags的增強版本, 可以提供上面提到的各種查找功能. 其實我們完全可以不用ctags而只用cscope的, 因為cscope可以做到所有ctags的功能. 但出於ctags在c語言函數等定義上跳轉的高效和准確性. 我個人是兩個同時使用的, 定義的跳轉通過ctrl+]和ctrl+t實現, 只要在需要其他查找的時候才會動用:cs find 命令.
好了, 既然實現了ctags的自動查找, cscope自然也要實現, 不能輸入人家ctags啊. 不過在vim中實現cscope的遞歸查找就比較麻煩. 首先要注意的一點是:簡單的在vimrc添加cscope路徑有兩種寫法:
linux的終端版本的vim7.3中需要使用如下的寫法vim才能正確的識別cscope的tags文件
cs add /home/boddy/hello/cscope.out /home/boddy/hello/
第二個參數是告訴vim cscope的索引文件中記錄的數據的相對路徑的起始位置在哪里, 大多情況下的這個位置是我們項目的根目錄. 因為我們告訴了vim這個相對路徑的起始位置, 因此cscope默認使用相對路徑產生的數據庫總是可以正確的跳轉.
windows下面的gvim7.3需要使用如下的寫法gvim才能正確識別cscope
cs a E:\project\hello\cscope.out
這里不需要添加相對路徑的起始位置, 具體原因不清楚, 可能windows下的gvim默認把cscope.out所在路徑解析成相對路徑的起始路徑了吧. 不過就是這個問題導致我在windows下面使用vim在很長一段時間中沒能正確的使用cscope.
上面說的注意事項是我在vim學習之初遇到的問題, 下面實現的遞歸索引功能將不會關注這個問題, 但依然需要注意這個事項.
vim中實現cscope的遞歸查找有兩種方式, 要么自己在vimrc中寫一個簡單函數讓vim啟動的時候去遞歸查找並添加cscope.out, 要么使用插件. 通過簡單函數的實現在網絡上可以搜索到, 同時我在最初也是用這種方法實現的, 不過在后來的某一天偶然讓我發現了vim有一個實現遞歸查找cscope的插件后就刪除了這個在vimrc中的函數,並使用插件來實現. 這樣可是簡化vimrc的篇幅的同時別人寫的插件肯定比簡單的函數要完善.
到下面的網站中搜索autoload_cscope.vim即可下載到這個插件.
http://vim-scripts.org/vim/scripts.html
針對這個插件有如下的說明:
這個插件只有一個腳步文件, 放到你的~/.vim/plugin目下即可使用, 該插件默認情況下是在我們打開.h/.c/.cpp文件的時候才會自動遞歸查找並添加cscope.out文件的,個人覺得既然我們在已經在項目子目錄下了, 大多時候都是希望cscope可用的, 即便我們是在編輯一個.txt文件我們可能希望手動搜索一個當前項目的關鍵字等. 另外即便是cscope.out文件我們用不到,但將其添加到當前編輯文件中來在性能上幾乎是沒有影響的. 因此, 我人為的修改了這個插件,以便讓它實現在打開任何文件的時候(包括新建空文件)都遞歸查找cscope.out
修改方法如下:
打開autoload_cscope.vim定位到文件的最后幾行,你將看到:
au BufEnter *.[chly] call <SID>Cycle_csdb() | call <SID>Cycle_macros_menus()
au BufEnter *.cc call <SID>Cycle_csdb() | call <SID>Cycle_macros_menus()
au BufUnload *.[chly] call <SID>Unload_csdb() | call <SID>Cycle_macros_menus()
au BufUnload *.cc call <SID>Unload_csdb() | call <SID>Cycle_macros_menus()
將他們修改為下面內容即可
au BufEnter * call <SID>Cycle_csdb() | call <SID>Cycle_macros_menus()
au BufUnload * call <SID>Unload_csdb() | call <SID>Cycle_macros_menus()
另外可以在vimrc中選擇性的添加如下內容:
set nocst "在cscope數據庫添加成功的時候不在命令欄現實提示信息.
set cspc=6 "cscope的查找結果在格式上最多顯示6層目錄.
let g:autocscope_menus=0 "關閉autocscope插件的快捷健映射.防止和我們定義的快捷鍵沖突.
"cscope相關的快捷鍵映射
nmap ff <c-]> "ff映射到ctrl+],這將中調用ctags的數據庫跳轉,在速度上會快一點. 如果發現ff無法實現跳轉,可以試着使用fg, 這個會調用cscope的數據庫實現跳轉.
nmap ss <c-t> "ss映射到ctrl+t , 在使用ff跳轉的使用通過ss跳轉回來,純屬個人在功能映射上的多余之舉.
"s:查找即查找C語言符號出現的地方
nmap fs :cs find s <C-R>=expand("<cword>")<CR><CR>
"g:查找函數、宏、枚舉等定義的位置
nmap fg :cs find g <C-R>=expand("<cword>")<CR><CR>
"c:查找光標下的函數被調用的地方
nmap fc :cs find c <C-R>=expand("<cword>")<CR><CR>
"t: 查找指定的字符串出現的地方
nmap ft :cs find t <C-R>=expand("<cword>")<CR><CR>
"e:egrep模式查找,相當於egrep功能
nmap fe :cs find e <C-R>=expand("<cword>")<CR><CR>
"f: 查找文件名,相當於lookupfile
nmap fn :cs find f <C-R>=expand("<cfile>")<CR><CR>
"i: 查找當前文件名出現過的地方
nmap fi :cs find i <C-R>=expand("<cfile>")<CR><CR>
"d: 查找本當前函數調用的函數
nmap fd :cs find d <C-R>=expand("<cword>")<CR><CR>
好了, cscope的自動化使用到此也大功告成,如果你想在vim中查看auto_cscope是否成功為你自動加載了cscope.out,可以在vim中執行 ":cs s" (這s是show的縮寫) 來查看與當前vim窗口關聯的所有cscope文件的信息.
下面是今天的最后一個自動索引功能的實現. 我廢話真的太多了, 寫博客都寫累了....休息中....
最后一個查找跳轉類型的數據庫文件是lookupfile這個插件. 實際上這個插件在很大程度上是多余的, 首先是cscope提供了文件查找功能. 其次是如果使用FuzzyFinder插件可以實現借助tags文件來查找項目文件. 不過鑒於lookupfile這個插件遇見的較早, 功能簡單, 查找優美, 設置好了之后使用也很方便又不和任何其他插件的沖突, 我還是永久的留下了這個插件. 下面是這個插件的相關設置過程:
到http://vim-scripts.org/vim/scripts.html 搜索lookupfile下載並安裝該插件.
編寫生成lookupfile索引文件的腳本, 這個我也是從網絡上抄來的, 內容可以如下:
#!/bin/sh
# generate tag file for lookupfile plugin
echo -e "!_TAG_FILE_SORTED\t2\t/2=foldcase/" > name.tags
find `pwd` -type f \( -name "*.[ch]" -o -name "*.[ch]pp" \) -printf "%f\t%p\t1\n"|sort -f >>name.tags
其中的name.tags是生成的的索引文件的名字, 你可以修改成任何期它的名字, 不過需要注意一定要使用一個后綴名, 不可起如nametags這樣的名字, 這在后面的會說明原因.這里的第二句命令find 后面使用了'pwd'參數 因此生成的name.tags中存放的是文件的絕對路徑, 如果不使用'pwd'參數,將使用相對路徑,這將出現的問題和FuzzyFinder使用相對路徑的tags文件相同的--在根目錄的子目錄搜索到文件后無法正確的跳轉.
將上面的內容保存為.sh后綴的文件(如:nametags.sh),將這個shell文件移動的到一個目錄下並將該目錄添加到環境變量中去. 個人建議是在~/.vim目錄下建立一個bin文件夾, 將nametags.sh移動到這里, 以后只要是和編程有關的shell文件或自建的批處理等文件都可以放到這里來, 這樣在日常備份的時候只要將~/.vim 文件夾和~/.vimrc文件打包就可以備份很多開發環境了, 本這這個原則, 我個人開發相關的一些其他的內容(如vim的補全列表, 標准項目模板, 個人使用的函數庫等)都會放在.vim文件夾中.bin文件夾下面存在放的都是需要執行的文件, 因此在第一次使用之前,重裝系統或移動到一台上使用的時候需要做如下兩個操作:
->將bin目錄下所有的文件賦予執行權限: sudo chmod 775 ~/.vim/bin/*
->將~/.vim/bin目錄添加到環境變量中去. 這個操作有很多中方式, 每個linux版本也不是太一樣, 可以自行百度解決.
nametags.sh在賦予可執行權限和添加環境變量操作之后就可以在任何目錄執行了, 執行的結果是nametags.sh 索引當前目錄的所有的子目錄, 將.c .h等源文件排序后添加到當前目錄的name.tags文件來.這name.tags相當與tags文件, 只是他的功能只能用來查找項目文件而已.
在項目根目錄執行nametags.sh之后我們需要向ctags和cscope一樣通過vimrc來告訴vim的lookupfile插件那里去找這個項目文件的索引,當然, 我們也需要實現vim自動對name.tags文件的自動遞歸查找. 這個功能在網絡上可就沒有現成的方法了, 因為很少有人會想到這個需求, 同時實現起來也很困難, 因為沒有多少人對vim了解到會編寫腳步的地步, 我至今還不太會編寫比較復雜的vim腳本文件. 好在大多數復雜的功能都已經被大神和前輩們實現了, 而我們需要做的就是讓這些功能更加的便捷並定制成符合自己需求的樣子就可以了.
在看過了lookupfile的幫助文檔和查閱了一些vim腳步資料之后, 在我不斷的嘗試性的編寫下竟然讓我實現了lookupfile的遞歸查找name.tags的功能. 這個算的上是我完全獨立實現的功能啦. 有紀念意義哦....
在vimrc中添加如下兩條語句,將實現lookupfile的自動遞歸查找name.tags文件:
let g:name_file=findfile("name.tags", ".;")
let g:LookupFile_TagExpr='g:name_file'
第一句的findfile函數實現了遞歸查找,注意:findfile函數似乎有一個神奇的特點, 如果你查找的是諸如name.tags這種帶有后綴名的文件, 那么它會返回一個絕對路徑的文件名字符串,可是如果你查找的是諸如nametags這樣沒有后綴名的文件, findfile依然可以正確的找到這個文件, 但返回的東西就很坑爹了, 直接是nametags, 路徑全部被吞掉了!!!
第二句是將第一句查找的結果連同路徑一起賦值給LookupFile_TagExpr變量, 這里似乎必須在name_file前面添加一個g:或s:才能正確的被vim識別.
為更家便捷的使用lookupfile查詢文件,我在vimrc設置了如下的對lookupfile的設置和快捷鍵的映射並從網絡找到了一個可以實現lookupfile查找時忽略大小寫的功能函數:
let g:LookupFile_MinPatLength=2
let g:LookupFile_PreserveLastPattern=0
let g:LookupFile_PreservePatternHistory=1
let g:LookupFile_AlwaysAcceptFirst=1
let g:LookupFile_AllowNewFiles=0
let g:indentLine_color_gui='#4e5656'
nmap ,l :LUTags<cr> "最常用
nmap ,kk :LUBufs<cr>
nmap ,kl :LUWalk<cr>
"LookupFile搜索時不區分大小寫
func! LookupFile_IgnoreCaseFunc(pattern)
let _tags = &tags
try
let &tags = eval(g:LookupFile_TagExpr)
let newpattern = '\c' . a:pattern
let tags = taglist(newpattern)
catch
echohl ErrorMsg | echo "Exception: " . v:exception | echohl NONE
return ""
finally
let &tags = _tags
endtry
let files = map(tags, 'v:val["filename"]')
return files
endfunc
let g:LookupFile_LookupFunc = 'LookupFile_IgnoreCaseFunc'
由於這三個索引功能的文件都不會在項目文件發生修改的時候自動更新, 因此需要我們手動的在需要更新的時候一個一個的更新他們,事實上這樣的更新在我們查看別人的項目和linux內核文件的時候並不是太過頻繁,有時甚至很少. 例如在查看linux內核源文件的時候我幾乎只需要建立一次索引就可以了, 重點這是一個很幸運的事情, 因為linux的內核文件是相當的龐大的, 對它建立tags和cscope.out以及name.tags文件加一起更新一次就需要近十來分鍾. 這三個數據庫文件都不支持增量更新. 即便我們只改動了整個項目的很小的一部分內核, 重新構建tags的時候依然需要對整個項目進去完全的檢索. 從這一點來看ctags和cscope並不適合超大型項目的頻繁構建. 之所以說這是一個幸運是因為我們很少會參與諸如linux內核這樣的項目構建. 即便公司真的有一個超大的項目, 分配到我們身上的大多是很小的一部分或一個模塊功能. ctags和cscope建立的速度還相當快了, linux內核產生的ctags可以達到100多兆, cscope.out更是達到300-400兆. 這樣的數據在幾分鍾內完成,可想而知, 不是超級巨型的項目, tags建立基本可以在十妙內完成.
現在還剩下最后一個問題, 雖然既然我們很幸運的可以正常使用他們, 但是在開發一個常規項目的時候頻繁的重新構建tags,cscope.out和name.tags是正常的操作之一, 這些索引數據都是通過命令建立的.每個命令又不是那么簡短. 編碼時,如果希望刷新tags的時候就跑到項目根目錄挨個執行一通他們的命令肯定是一件沒幾個人能接受的事情. 好在這些tags的構建名利幾乎都是固定的. vim提供了直接執行shell命令的能力. 再加上快捷鍵映射功能和項目的遞歸特性. 我們便有辦法將tags的更新簡化到一鍵操作, 一鍵操作是一個很了不起的能力, 因為除了全自動的操作, 應該沒有比一鍵操作還要簡潔高效的了.
下面是實現ctags, cscope,和lookupfile的一鍵刷新功能的設置:
->在項目的根目錄新建一個空的文件名為TOP的文件. 這個文件用來標識項目根目錄所在位置.(你可以將這個空文件做為你的項目標准模板的一部分)
->在vimrc中添加如下的函數和設置:
"nmap <f12> <esc>:call Go_top()<cr>:!ctags -R --c++-kinds=+p --fields=+iaS
"\ --extra=+q $PWD<cr>:call Do_CsTag()<cr>:!nametags.sh<cr><cr>:call Go_curr()<cr>
"imap <f12> <esc>:call Go_top()<cr>:!ctags -R --c++-kinds=+p --fields=+iaS
"\ --extra=+q $PWD<cr>:call Do_CsTag()<cr>:!nametags.sh<cr><cr>:call Go_curr()<cr>
"nmap <a-f12> <esc>:!ctags -R --fields=+lS $PWD<cr><cr>
"\:!cscope -Rbkq<cr><cr>:!nametags.sh<cr><cr>
"imap <a-f12> <esc>:!ctags -R --fields=+lS $PWD<cr><cr>
"\:!cscope -Rbkq<cr><cr>:!nametags.sh<cr><cr>
nmap <f12> <esc>:call Go_top()<cr>:!ctags -R --fields=+lS $PWD<cr><cr>
\:!cscope -Rbkq<cr><cr>:!nametags.sh<cr><cr>:call Go_curr()<cr>
imap <f12> <esc>:call Go_top()<cr>:!ctags -R --fields=+lS $PWD<cr><cr>
\:!cscope -Rbkq<cr><cr>:!nametags.sh<cr><cr>:call Go_curr()<cr>
func! Go_top()
wall
let g:Curr_dir=getcwd()
let i = 1
while i < 10
if filereadable("TOP")
return
else
cd ..
let i += 1
endif
endwhile
exec 'cd'.g:Curr_dir
endfunc
func! Go_curr()
exec 'cd'.g:Curr_dir
endfunc
func! Do_CsTag()
silent! exec "!find . -name '*.h' -o -name '*.c' -o -name '*.cpp'
\ -o -name 'Makefile' -o -name 'makefile' -o -name 'make*'
\ -o -name '*.cc' -o -name '*.C'-o -name '*.s'-o -name '*.S'>cscope.files"
silent! exec"!cscope -Rbkq -i cscope.files"
silent! exec"!cscope -Rbkq"
endfunc
說明:
->前面幾行用 " 注視掉的內容是對C++的支持, 在使用C++的時候可以解除他們的注釋並將下面C語言使用的映射添加注釋.
->整個對<F12>鍵的映射在看完上面的介紹之后應該可以看懂, 需要注意的是vim中命令模式下執行!開頭的內容會被送往vim所在的shell中執行並且很多時候在終端執行完vim傳遞的命令之后終端會給出一個提示說明執行完畢,並要求鍵入一個enter鍵才能返回到vim,vim的映射中的<cr>可以被自動發送到終端中去, 因此只要在調用終端命令之后在額外的添加一個<cr>就可以實現讓vim自動在終端中鍵入一個<enter>鍵. 另外, 其實這里的映射本來完全可以封裝在一個函數中完成的. 可是我自己無法實現在vim腳步的函數中向終端發送<enter>鍵的功能, 因此不得已才通過這種在映射中添加<cr>的方式實現自動交互的功能.
->映射中在執行ctags -R的時候額外的傳遞了一個$PWD變量, 這個變量在vim表示的是vim的當前工作目錄, 由於在執行ctags前已經調用Go_top()將當前目錄更改為項目的根目錄, 所以這里傳遞的實際上就項目的跟目錄, 這個變量的作用在之前說過, 它可是讓ctags生成的文件使用絕對路徑, 這會保證幾乎所有調用的tags的插件都可以正常的工作.
->Go_top()函數首先記錄下當前目錄位置然后遞歸的查找當前目錄所以父目錄下是否存在TOP文件, 如果存在就定位到那個目錄並返回, 如果不存在通過之前記錄的目錄返回到之前的目錄, 這樣做的好處是的即便沒有找到TOP標識,vim的當前目標也不至於跑到系統的根目錄上去.
->Go_curr()函數實現vim在成功找到TOP標識並在TOP所在目錄執行完所有命令之后通過g:Curr_dir記錄的全局變量來讓vim的當前目錄中回到執行命令前的狀態.
->Do_Cstags()函數是一個可選函數, 他的功能是可以在生成cscope的時候過濾掉一些不需要建立索引的文件. 事實上, 這個功能並是經常用到, 因為項目文件夾中的的文件大多和項目相關. 不相關的一些說明文件cscope也可能對其建立索引.為了讓所有可能建立索引的特殊文件(如:makefile)也可以實現跳轉.我自己並沒有過濾任何文件.
當上面所有的設置都正確工作了之后, 恭喜, vim和tags相關的配置就算告一段落了. 以后你要做的就是在項目根目錄建立TOP文件.在任何項目目錄中打開vim按下f12鍵, vim將會自動為你建立所需的所有tags文件. 之后只要你在任意項目目錄中打開vim屬於這個項目的所有tags文件都會被vim自動加載並正確工作. 在你編輯代碼的任何時刻, 你都可以通過一個f12鍵迅速的更新整個項目的所有tags索引. 你可以通過ctrl+]和ctrl+t來調用ctags, 在普通模式下通過"f"打頭快捷鍵調用cscope查詢,通過 ",l" 來調用lookupfile查詢項目文件. 這篇文章中提到到所有功能最終被濃縮到一個f12鍵上. 這種一勞永逸並可以大幅提高效率的感覺會讓你覺得付出的一切的是那么的值得. 這也是為什么我願意花掉整個下午的時間來寫這篇博客的原因. 希望這幾千的說明可以被更多願意使用vim的人看到. 也希望有其他的vim愛好者能夠和我更多的交流學習....
最后順便提及一下曾經研究過的另一個tags工具--gnu global 這個工具好像是一個日本人開發的, 曾經在水木社區上看到比較全面的使用方法, 按照上面的配置, global工具的確可以被vim識別並使用, 但由於global使用的是二進制存在, 內部tags數據又使用的是相對路徑, 這將出現上面提到過兩次的相同問題--只有在項目的根目錄使用global搜索, vim才能正確的跳轉. gnu global最大的優點是支持增量更新, 大型項目修改后global的更幾乎是瞬間完成. global在查詢速度上也很快, 查詢的結果比ctags要全面但相對與cscope要稍微弱一點. 在所有優點的背后由於我沒有找到讓他產生絕對路徑數據庫的辦法. 最終在折騰了幾天之后放棄了使用.