在 vim 中使用 LeaderF 和 gtags

本文最后更新于 2024年6月12日 凌晨

☆ 缘由和一些 vim 历史

前些年 vim 没有子窗口的概念,日本的一个 vim 插件作者为了解决这个问题生撸了一套流程,当时 unite.vim 横空出世惊艳了我一把。后面随着技术发展,neovim 和 vim 先后引入了 floating window ,这才使得情况变得好了一点。

https://github.com/vim/vim/blob/master/runtime/doc/popup.txt
https://neovim.io/doc/user/api.html#api-floatwin

得益于新技术,在 vim 中使用 LeaderF 插件的交互体验上了一个台阶。

Demo

neovim 从 0.9 版本开始放弃对 cscope 的支持 https://twitter.com/Neovim/status/1580933880579641344

这给我阅读源码造成了一些问题,虽然 vim 仍然可以使用 cscope,但 vim 的启动速度不如 nvim 快,所以我开始寻找替代方案。同时,我发现 denite.nvim,unite.vim,作者也不怎么维护了,一想似乎是很多年没有维护 vim 配置了。

在网络上搜索在 neovim 上阅读源码的方法,无意发现有人提到用 LeaderF 替代 tagbar,于是有了现在的这篇文章。

☆ 安装vim 插件 LeaderF

插件的仓库地址:https://github.com/Yggdroot/LeaderF/tree/master

使用 Vundle.vim 安装,在 .vimrc 中添加下面一行

1
Plug 'Yggdroot/LeaderF', { 'do': ':LeaderfInstallCExtension' }

在 vim 中执行下面命令,即可成功安装,如果遇到网络问题,自己挂上线路即可。

1
2
:so %
:PlugInstall

☆ LeaderF 配置

1
2
3
4
5
6
7
8
9
10
11
let g:Lf_WorkingDirectoryMode = 'Ac'
let g:Lf_RootMarkers = ['.git', '.svn', '.hg', '.project', '.root']
let g:Lf_DefaultExternalTool='rg'
let g:Lf_WindowPosition = 'popup'

let g:Lf_UseCache = 0
let g:Lf_PreviewInPopup = 1
let g:Lf_ShowHidden = 1 "show hidden files

" preview or not
let g:Lf_PreviewResult = {'Function': 0, 'BufTag': 0 }
  • g:Lf_WorkingDirectoryMode = ‘AF’ 设置工作目录,如果没有找到 RootMarker 以当前文件所在目录作为工作目录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
g:Lf_WorkingDirectoryMode                       *g:Lf_WorkingDirectoryMode*
This option customizes LeaderF's working directory.
e.g., >
let g:Lf_WorkingDirectoryMode = 'Ac'
<
c - the directory of the current working directory.(default)
a - the nearest ancestor of current working directory that contains one of
directories or files defined in |g:Lf_RootMarkers|. Fall back to 'c' if
no such ancestor directory found.
A - the nearest ancestor of current file that contains one of directories
or files defined in |g:Lf_RootMarkers|. Fall back to 'c' if no such
ancestor directory found.
f - the directory of the current file.
F - if the current working directory is not the direct ancestor of current
file, use the directory of the current file as LeaderF's working
directory, otherwise, same as 'c'.
Note: if "f", "F" is included with "a" or "A", use the behavior of "f" or
"F"(as a fallback) when a root can't be found.

  • g:Lf_DefaultExternalTool 索引文件使用的外部工具,可选值为 ‘rg’, ‘pt’, ‘ag’, ‘find’,作者推荐使用 rg
  • g:Lf_UseCache 列文件时是否使用缓存,默认值为 1,设置为 0 不使用缓存
  • g:Lf_WindowPosition,LeaderF 窗口的位置,可选值有 top, bottom, left, right, popup,fullscreen,默认值为 bottom
  • g:Lf_PreviewInPopup 设置是否在 LeaderF 的 popup 窗口显示预览, 默认值为 0
  • g:Lf_ShowHidden 是否显示隐藏文件和目录,默认值为 0 不显示隐藏文件
  • g:Lf_PreviewResult 设置需要显示预览的内容,默认值为全 1
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    let g:Lf_PreviewResult = {
    \ 'File': 1,
    \ 'Buffer': 1,
    \ 'Mru': 1,
    \ 'Tag': 1,
    \ 'BufTag': 1,
    \ 'Function': 1,
    \ 'Line': 1,
    \ 'Colorscheme': 1,
    \ 'Rg': 1,
    \ 'Gtags': 1
    \}

配置好了 LeaderF,在 vim/neovim 的命令行输入 :Leaderf tab 就可以看到相关的命令了,如果忘记了具体的命令也没有关系,使用 LeaderfSelf 即可列出所有的 Leaderf 命令。如何想查看具体命令的使用方法,在命令窗口输入 :Leaderf -h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
subcommands:

{file,tag,function,mru,searchHistory,cmdHistory,help,line,colorscheme,gtags,
self,bufTag,buffer,rg,filetype,command,window,quickfix,loclist,jumps}
file search files
tag navigate tags using the tags file
function navigate functions or methods in the buffer
mru search most recently used files
searchHistory execute the search command in the history
cmdHistory execute the command in the history
help navigate the help tags
line search a line in the buffer
colorscheme switch between colorschemes
gtags navigate tags using the gtags
self execute the commands of itself
bufTag navigate tags in the buffer
buffer search buffers
rg grep using rg
filetype navigate the filetype
command execute built-in/user-defined Ex commands.
window search windows.
quickfix navigate the quickfix.
loclist navigate the location list.

好了,我可以放弃 fzf.vim, ctrp.vim, tagbar, unite.vim, denite.vim 等一系列 vim 插件了。 在日常使用中,每次都输入那么长的命令不太方便,我们可以配置以下容易记忆的快捷键。

1
2
3
4
5
6
7
noremap <leader>fs :LeaderfSelf<cr>
noremap <leader>fm :LeaderfMru<cr>
noremap <leader>ff :LeaderfFunction<cr>
noremap <leader>fb :LeaderfBuffer<cr>
noremap <leader>ft :LeaderfBufTag<cr>
noremap <leader>fl :LeaderfLine<cr>
noremap <leader>fw :LeaderfWindow<cr>

值得一提的是 rg 命令,可以直接在目录文件内搜索字符串,ripgrep 的正则表达式比 vim 的正则要正常多了。vim 的正则表达式的用法可以参见:https://vimdoc.sourceforge.net/htmldoc/pattern.html
以前我为了规避 vim 的正则使用了一个插件 https://github.com/othree/eregex.vim 现在这个插件也不需要了。

要利用 LeaderF 调用 rg 命令,可以使用命令行 :Leaderf! rg, 为了方便使用使用下面的快捷键查询光标所在 word

1
nmap <leader>frb <Plug>LeaderfRgCwordLiteralBoundary

运行 LeaderF 之后的操作

在执行 Leaderf 命令弹出的界面后,需要使用特定的快捷键进行操作,这些快捷键可以使用 help leaderf-prompt 进行查看。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Once LeaderF is launched:                       *prompt* *leaderf-prompt*
<C-C>, <ESC> : quit from LeaderF.
<C-R> : switch between fuzzy search mode and regex mode.
<C-F> : switch between full path search mode and name only search mode.
<Tab> : switch to normal mode.
<C-V>, <S-Insert> : paste from clipboard.
<C-U> : clear the prompt.
<C-W> : delete the word before the cursor in the prompt.
<C-J>, <C-K> : navigate the result list.
<Up>, <Down> : recall last/next input pattern from history.
<2-LeftMouse> or <CR> : open the file under cursor or selected(when
multiple files are selected).
<C-X> : open in horizontal split window.
<C-]> : open in vertical split window.
<C-T> : open in new tabpage.
<F5> : refresh the cache.
<C-LeftMouse> or <C-S> : select multiple files.
<S-LeftMouse> : select consecutive multiple files.
<C-A> : select all files.
<C-L> : clear all selections.
<BS> : delete the preceding character in the prompt.
<Del> : delete the current character in the prompt.
<Home>: move the cursor to the begin of the prompt.
<End> : move the cursor to the end of the prompt.
<Left>: move the cursor one character to the left.
<Right> : move the cursor one character to the right.
<C-P> : preview the result.
<C-Up> : scroll up in the popup preview window.
<C-Down> : scroll down in the popup preview window.
  • tab : 切换输入模式和 normal 模式,进入 normal 模式后可以使用 jk 上下移动光标来查看文件。
  • q : normal 模式中,退出 LeaderF

在输入模式下,可以使用下面的快捷键进行分屏显示。

  • ctrl + x : 横向分割
  • ctrl + ] : 竖向分割
  • ctrl + T : tab 分割

☆ LeaderF + gtags 的配置

gtags 和 LeaderF 配置起来使用,实在是很不错。首先要配置好 gtags,关于 global 的安装详见 GNU Global 的使用,这里就不再详细说了,gtags 的配置添加下面两行即可。

1
2
let $GTAGSLABEL='native-pygments'
let $GTAGSCONF='/usr/local/share/gtags/gtags.conf'

配置好系统 gtags,global ,gtags.conf 的正确路径。

1
2
3
4
let g:Lf_Global = '/usr/local/bin/global'
let g:Lf_Gtags = '/usr/local/bin/gtags'
let g:Lf_Gtagsconf = '/usr/local/share/gtags/gtags.conf'
let g:Lf_Gtagslabel = 'native-pygments'

下面两行是配置gtags 自动生成 tags 文件,主要是三个文件 GPATH , GRTAGS GTAGS

1
2
3
let g:Lf_GtagsSource = 2
let g:Lf_GtagsAutoGenerate = 1
let g:Lf_GtagsAutoUpdate = 1
  • g:Lf_GtagsSource 设置 gtags 的目标文件,设置为 2 通过执行外部命令判断目标文件 (例如 git ls-files
  • g:Lf_GtagsAutoGenerate 是否自动生成 tags 文件。设置成 1 并且在 g:Lf_RootMarkers 定义的目录中,tags 会自动生成
  • g:Lf_GtagsAutoUpdate 是否自动更新 tags。如果设置成 0 ,需要先执行 Leaderf gtags --update 手动更新 tags 文件

tags 文件生成的目录和 g:Lf_CacheDirectory 的配置相关, g:Lf_CacheDirectory 用于设置缓存文件目录,默认的目录是 $HOME (Windows 上会有变化)使用 _ 替换了 /

1
2
ls ~/.LfCache/gtags/_home_henices_.vim 
GPATH GRTAGS GTAGS

新版本的 LeaderF,g:Lf_CacheDirectory 的默认值发生了变化,Linux 系统一般为 ~/.cache/LeaderF,详情如下:

1
2
3
4
5
6
7
8
g:Lf_CacheDirectory                              *g:Lf_CacheDirectory*
Set this option to change the location of the cache directory.
e.g. >
let g:Lf_CacheDirectory = '/root'
<
On Windows, default value is '%APPDATA%'.
On Linux, default value is '$XDG_CACHE_HOME' if $XDG_CACHE_HOME is defined,
otherwise, default value is '$HOME/.cache'.

完成了上面的配置后,我们就可以正常使用 LeaderF 来对 gtags 生成的 tags 文件进行浏览了,在命令行使用 :Leaderf gtags -h 可以看到详细的命令。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
: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 [--auto-jump [<TYPE>]] [-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] [--next | --previous]

...

LeaderF gtags 最常用的 :Leaderf[!] gtags -d/-r/-s/-g, 具体的作用如下所示,! 表示直接进入 normal 模式。

1
2
3
4
5
6
7
8
9
--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>

日常使用中,同样可以配置快捷键方便使用,下面的快捷键将对光标所在单词进行 tag 查找。

1
2
3
4
nmap <leader>fgd <Plug>LeaderfGtagsDefinition
nmap <leader>fgr <Plug>LeaderfGtagsReference
nmap <leader>fgs <Plug>LeaderfGtagsSymbol
nmap <leader>fgg <Plug>LeaderfGtagsGrep

运行 LeaderF gtags 之后

使用 LeaderF + gtags 跳转到源码后,如果想继续跳转到下一个或者上一个 tag 可以使用 Leaderf gtags --nextLeaderf gtags --previous

1
2
3

noremap <leader>fgn :<C-U><C-R>=printf("Leaderf gtags --next %s", "")<CR><CR>
noremap <leader>fgp :<C-U><C-R>=printf("Leaderf gtags --previous %s", "")<CR><CR>

☆ LeaderF 的其他用法

LeaderF 还可以和 Gutentags 配合使用进行源码浏览,但已经超出本文内容,有兴趣者可以查阅网络上的其他资料。

☆ 参考资料

https://github.com/Yggdroot/LeaderF/blob/master/doc/leaderf.txt
https://retzzz.github.io/dc9af5aa/
https://github.com/Yggdroot/LeaderF/blob/master/README.md
https://zhuanlan.zhihu.com/p/36279445
https://github.com/vim/vim/blob/master/runtime/doc/popup.txt
https://neovim.io/doc/user/api.html#api-floatwin


在 vim 中使用 LeaderF 和 gtags
https://usmacd.com/cn/vim_gtags_leaderf/
作者
henices
发布于
2023年7月20日
许可协议