首頁 > 軟體

Vim之tags 詳解

2020-06-16 17:23:46

  本章要說的是和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:projecthellocscope.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檔案的資訊.

更多詳情見請繼續閱讀下一頁的精彩內容http://www.linuxidc.com/Linux/2017-01/139378p2.htm


IT145.com E-mail:sddin#qq.com