`Leaderf gtags`:模糊匹配與最強靜態符號索引工具的完美結合

Yggdroot發表於2019-05-06

Gtags

Gtags也就是GNU GLOBAL,是一個非常強大的原始碼符號索引工具。它通過建立索引資料庫,不但可以查詢函式的定義,還可以查詢函式的所有引用(被呼叫的地方);而且它還可以增量地更新索引資料庫,當程式碼有所改變時,它可以在很短的時間內更新索引資料庫,保持索引資料庫和程式碼同步。
韋大的 Vim 8 中 C/C++ 符號索引:GTags 篇 對 gtags 有比較詳細的介紹,本文再做一些補充。

  1. GLOBAL-6.6.3 released Gtags的最新版本已經是6.6.3,該版本 fix了韋大文中提到的 Windows下面檔名大小寫的 bug。
  2. 在 Linux上,不配置let $GTAGSCONF = '/path/to/share/gtags/gtags.conf'也可以正常工作。
  3. 當專案檔案的路徑包含非ASCII字元時,使用pygments會報UnicodeEncodeError: 'latin-1' codec can't encode characters in position 5-8: ordinal not in range(256)

自動生成Gtags索引資料庫

LeaderF 可以自己管理 gtags 資料庫(GTAGS,GRTAGS,GPATH),它不會在你的專案目錄下生成任何額外的檔案或目錄。gtags 資料庫檔案儲存在$HOME/.LfCache/gtags/%PATH%OF%YOUR%PROJECT/下面, %PATH%OF%YOUR%PROJECT 是把你專案路徑中的 \/ 替換成 %
只要設定let g:Lf_GtagsAutoGenerate = 1, LeaderF 就會在開啟第一個檔案時自動生成 gtags 資料庫。當程式碼有更改並且已經有 gtags 資料庫生成時,更改的程式碼會自動同步到 gtags 資料庫(即使g:Lf_GtagsAutoGenerate是0)。
只有在專案根目錄下有g:Lf_RootMarkers(預設值是['.git', '.hg', '.svn'])裡面指定的檔案或目錄時,LeaderF 才會自動生成 gtags 資料庫;否則只能手動生成 gtags 資料庫:Leaderf gtags --update,但是當程式碼有更改時,gtags 資料庫依然可以自動更新。

Leaderf gtags 使用介紹

具體使用方法可以用:Leaderf gtags -h來檢視。

usage:
Leaderf[!] gtags [-h] [--remove] [--recall]
Leaderf[!] gtags --update [--gtagsconf <FILE>] [--gtagslabel <LABEL>] [--accept-dotfiles]
                 [--skip-unreadable] [--skip-symlink [<TYPE>]] [--gtagslibpath <PATH> [<PATH> ...]]
Leaderf[!] gtags [--current-buffer | --all-buffers | --all] [--result <FORMAT>] [COMMON_OPTIONS]
Leaderf[!] gtags -d <PATTERN> [--auto-jump [<TYPE>]] [-i] [--literal] [--path-style <FORMAT>] [-S <DIR>]
                 [--append] [--match-path] [--gtagsconf <FILE>] [--gtagslabel <LABEL>] [COMMON_OPTIONS]
Leaderf[!] gtags -r <PATTERN> [--auto-jump [<TYPE>]] [-i] [--literal] [--path-style <FORMAT>] [-S <DIR>]
                 [--append] [--match-path] [--gtagsconf <FILE>] [--gtagslabel <LABEL>] [COMMON_OPTIONS]
Leaderf[!] gtags -s <PATTERN> [-i] [--literal] [--path-style <FORMAT>] [-S <DIR>] [--append]
                 [--match-path] [--gtagsconf <FILE>] [--gtagslabel <LABEL>] [COMMON_OPTIONS]
Leaderf[!] gtags -g <PATTERN> [-i] [--literal] [--path-style <FORMAT>] [-S <DIR>] [--append]
                 [--match-path] [--gtagsconf <FILE>] [--gtagslabel <LABEL>] [COMMON_OPTIONS]
Leaderf[!] gtags --by-context [-i] [--literal] [--path-style <FORMAT>] [-S <DIR>] [--append]
                 [--match-path] [--gtagsconf <FILE>] [--gtagslabel <LABEL>] [COMMON_OPTIONS]

[COMMON_OPTIONS]: [--reverse] [--stayOpen] [--input <INPUT> | --cword]
                  [--top | --bottom | --left | --right | --belowright | --aboveleft | --fullScreen]
                  [--nameOnly | --fullPath | --fuzzy | --regexMode] [--nowrap]
 

optional arguments:
  -h, --help            show this help message and exit

specific arguments:
  --update              Create tag files if tag files do not exist, update the tag files otherwise.
  --remove              Remove the tag files generated.
  --accept-dotfiles     Accept files and directories whose names begin with a dot. By default, gtags
                        ignores them.
  --skip-unreadable     Skip unreadable files.
  --gtagsconf <FILE>    Set environment variable GTAGSCONF to <FILE>.
  --gtagslabel <LABEL>  Set environment variable GTAGSLABEL to <LABEL>.
  --skip-symlink [<TYPE>]
                        Skip symbolic links. If type is 'f' then skip only symbolic links for file, else
                        if 'd' then skip only symbolic links for directory. The default value of type is
                        'a' (all symbolic links).
  --gtagslibpath <PATH> [<PATH> ...]
                        Specify the paths to search for library functions.
  -d <PATTERN>, --definition <PATTERN>
                        Show locations of definitions.
  -r <PATTERN>, --reference <PATTERN>
                        Show reference to a symbol which has definitions.
  -s <PATTERN>, --symbol <PATTERN>
                        Show reference to a symbol which has no definition.
  -g <PATTERN>, --grep <PATTERN>
                        Show all lines which match to the <PATTERN>.
  --by-context          Decide tag type by context at cursor position. If the context is a definition of
                        the pattern then use -r, else if there is at least one definition of the pattern
                        then use -d, else use -s. Regular expression is not allowed for pattern.
  -i, --ignore-case     Ignore case distinctions in the pattern.
  --literal             Execute literal search instead of regular expression search.
  --path-style <FORMAT>
                        Show path names using <FORMAT>, which may be one of: `relative`, `absolute`,
                        `shorter`, `abslib` or `through`. `relative` means relative path. `absolute`
                        means absolute path. `shorter` means the shorter one of relative and absolute
                        path. `abslib` means absolute path for libraries (GTAGSLIBPATH) and relative path
                        for the rest. `through` means the relative path from the project root directory
                        (internal format of GPATH). The default is `relative`.
  -S <DIR>, --scope <DIR>
                        Show only tags which exist under <DIR> directory.
  --recall              Recall last search. If the result window is closed, reopen it.
  --match-path          Match the file path when fuzzy searching.
  --append              Append to the previous search results.
  --current-buffer      Show tags in current buffer.
  --all-buffers         Show tags in all listed buffers.
  --all                 Show tags in the whole project.
  --result <FORMAT>     Show result using format, which may be one of: `ctags`(default), `ctags-x`,
                        `ctags-mod`.
  --auto-jump [<TYPE>]  Jump to the tag directly when there is only one match. <TYPE> can be 'h', 'v' or
                        't', which mean jump to a horizontally, vertically split window, or a new tabpage
                        respectively. If <TYPE> is omitted, jump to a position in current window.

common arguments:
  --reverse             show results in bottom-up order
  --stayOpen            don't quit LeaderF after accepting an entry
  --input <INPUT>       specifies INPUT as the pattern inputted in advance
  --cword               current word under cursor is inputted in advance
  --top                 the LeaderF window is at the top of the screen
  --bottom              the LeaderF window is at the bottom of the screen
  --left                the LeaderF window is at the left of the screen
  --right               the LeaderF window is at the right of the screen
  --belowright          the LeaderF window is at the belowright of the screen
  --aboveleft           the LeaderF window is at the aboveleft of the screen
  --fullScreen          the LeaderF window takes up the full screen
  --nameOnly            LeaderF is in NameOnly mode by default
  --fullPath            LeaderF is in FullPath mode by default
  --fuzzy               LeaderF is in Fuzzy mode by default
  --regexMode           LeaderF is in Regex mode by default
  --nowrap              long lines in the LeaderF window won't wrap
  --next                Jump to the next result.
  --previous            Jump to the previous result.

If [!] is given, enter normal mode directly.
複製程式碼

注意:如果:Leaderf後面有感嘆號,會直接進入normal模式;如果沒有感嘆號,則是輸入模式,此時可以輸入字元來進行模糊匹配過濾。可以用tab鍵在兩個模式間來回切換。

手動生成gtags資料庫

Leaderf[!] gtags --update [--gtagsconf <FILE>] [--gtagslabel <LABEL>] [--accept-dotfiles]
                 [--skip-unreadable] [--skip-symlink [<TYPE>]] [--gtagslibpath <PATH> [<PATH> ...]]
複製程式碼

此命令完全非同步,不會卡住你的UI。

  1. --gtagsconf <FILE>

    用來指定 gtags.conf 檔案的路徑,一般情況下不需要指定,預設值就可以很好地工作。對於Windows上,如果相對於gtags.exe所在路徑有../share/gtags/gtags.conf,也不需要指定該選項。如果需要使用者自己特有的針對 gtags 的配置,可以指定使用者的配置檔案。

    也可以在vimrc裡設定g:Lf_Gtagsconf達到同樣的目的。

  2. --gtagslabel <LABEL>

    用來指定gtagslabel,如果不指定,預設值是 'default'<LABEL>gtags.conf 中的:

    • default
      使用內建parser,只支援 6 種語言(C,C++,Java,PHP4,Yacc,彙編)。
    • ctags
      使用exuberant-ctags作為語言parser,支援 40+ 種語言,只能生成定義索引不能生成引用索引。
    • new-ctags
      使用universal-ctags作為語言parser,支援 100+ 種語言,只能生成定義索引不能生成引用索引。雖然貌似universal-ctags已經支援生成引用tags,但是依然不能配合gtags工作(見這裡),我也試了各種操作都沒成功,也許是因為這個PR沒有被merge。
    • pygments
      使用pygments作為語言parser,號稱支援300+種語言。
    • native-pygments
      對於原生支援的6種語言使用內建parser,其他語言使用pygments作為parser。
    • 等等

    也可以在vimrc裡設定g:Lf_Gtagslabel達到同樣的目的。

  3. --gtagslibpath <PATH> [<PATH> ...]

    用來指定專案所用 library 的 Paths,這樣就可以生成 library 的索引,查詢定義或引用時可以跳轉到 library 程式碼中去。後面指定的路徑還可以是一個或多個其他專案路徑,跳轉時可以跳到其他專案中的檔案。

查詢tags

Leaderf[!] gtags [--current-buffer | --all-buffers | --all] [--result <FORMAT>] [COMMON_OPTIONS]
複製程式碼

此命令可以列出當前buffer、所有開啟的buffer或者整個專案的tags。

  1. Leaderf[!] gtags等同於Leaderf[!] gtags --all,列出整個專案的tags。
  2. --result <FORMAT> 指定顯示格式,可以是ctags(default), ctags-x或者ctags-mod
    • ctags格式
      ctags格式
    • ctags-x格式
      ctags-x格式
    • ctags-mod格式
      ctags-mod格式

查詢定義、引用

Leaderf[!] gtags -d <PATTERN> [--auto-jump [<TYPE>]] [-i] [--literal] [--path-style <FORMAT>] [-S <DIR>]
                 [--append] [--match-path] [--gtagsconf <FILE>] [--gtagslabel <LABEL>] [COMMON_OPTIONS]
Leaderf[!] gtags -r <PATTERN> [--auto-jump [<TYPE>]] [-i] [--literal] [--path-style <FORMAT>] [-S <DIR>]
                 [--append] [--match-path] [--gtagsconf <FILE>] [--gtagslabel <LABEL>] [COMMON_OPTIONS]
複製程式碼
  1. <PATTERN>可以是正規表示式。
  2. --auto-jump [<TYPE>] 意思是如果只有一個結果直接跳過去。

其他

  1. Leaderf[!] gtags -g <PATTERN>功能已被Leaderf rg包含。
  2. Leaderf gtags --nextLeaderf gtags --previous相當於quickfix的:cnext:cprevious命令,在LeaderF結果視窗關閉的情況下也可以使用。
  3. 更多內容請參考:Leaderf gtags -h 和 doc。

使用示例

let g:Lf_GtagsAutoGenerate = 1
let g:Lf_Gtagslabel = 'native-pygments'
noremap <leader>fr :<C-U><C-R>=printf("Leaderf! gtags -r %s --auto-jump", expand("<cword>"))<CR><CR>
noremap <leader>fd :<C-U><C-R>=printf("Leaderf! gtags -d %s --auto-jump", expand("<cword>"))<CR><CR>
noremap <leader>fo :<C-U><C-R>=printf("Leaderf! gtags --recall %s", "")<CR><CR>
noremap <leader>fn :<C-U><C-R>=printf("Leaderf gtags --next %s", "")<CR><CR>
noremap <leader>fp :<C-U><C-R>=printf("Leaderf gtags --previous %s", "")<CR><CR>
複製程式碼

相關文章