vim 下想实现code中 function/class 等 definition跳转. 比较简单的方法就是使用ctags 分析代码, 然后根据生成的tags table文件, 来做code index 的定位. 目前比较流行的 code definition/references 查询是LSP, 需要架设一个 language service backend, 与之通信. 这里先记录下使用ctags的方法.
1. Ctags
Ctags 用于根据Code 生成 index 文件( ctags table), 其中标注所有 identifer 的文件位置信息.
{tagname}\t{tagfile}\t{tagaddress}
Ctags 有其他变种, 比如 Emacs 下有 Etags (也支持 Ctags)
arch 安装Ctags tool: yay -S ctags
2. Vim + Ctags
2.1 生成 Tags文件
首先需要先 generate ctags file, 在 project root dir 下:
ctags -R .
-R表示 recursively, 该遍历目录下所有子文件夹, 递归下去.表示 filepath, 即当前文件夹
此时会在当前文件夹下生成 tags 文件, 稍微打开看一下, 可以看到类似
Greetings hello/greetings.go /^func Greetings()...
指定了idenfier Greetings 所在的文件名即位置
vim 打开调用Greettings 方法的文件, 在调用出按 Ctrl-] 即会跳转到所在文件.
2.2 配置vim指向Tags文件
一般会将tags文件放在各个project 的目录下, 这样方便区分. 但其中涉及到, 如何让vim寻找到当前project的tags文件, 以及当所处位置不在project 根目录, 例如 子目录下时, 如何找到所处project的tags文件.
vim 使用 option tags 来指定tags文件的寻找路径. 可以vim 内:echo &tags 来查看当前的 tags .值.
我自己的配置需求是:
- 不进入到VCS(version control system), 如git, hg 等的代码库中. (可以通过设置ignore来避免, 但是每个project配置有点麻烦)
- 当所处位置并非project根目录时, 可以找到当前project 的tags文件. 基于以上, 有如下配置:
- 生成Ctags时需要增加
ctags -R --tag-relative -f ./git/tags .--tag-relative表示index文件路径是tags文件的相对位置-f表示生成的tags文件output路径
这样生成的tags文件在根目录.git/下, 其中index路径就会是tags的相对路径:
Greetings ../hello/greetings.go /^func Greetings()...
- 配置vimrc
# .vimrc let my_workspace = "/home/myname/workspace/" set tags=.git/tags;eval(my_workspace),.hg/tags;eval(my_workspace),.local/tags;eval(my_workspace) # 可以:help file-searching 了解vim的路径search配置语法
此处配置tags值意思为, 从当前路径, 一直向上到 workspace/ 目录, 寻找 .git(hg/local)/tags 文件, 找到即为 tags文件. 这样就解决了当前位置处于project子目录下时, 无法定位到tags文件, 以及文件相对路径的问题.
2.3 自动化
我也不想每次更改code文件后, 再cd到根目录手动执行
ctags -R --tag-relative -f ./git/tags .
如果用的不是git(公司workflow) 可能是hg, 还需要更改命令 因此有需求:
- 每次更改write操作时, 生成tags文件
- 判断当前的文件夹结构, 指定tags文件的位置
- 不能卡
解决方法: vimrc 中定义
# .vimrc
let my_workspace = "/home/myname/workspace/"
set tags=.git/tags;eval(my_workspace),.hg/tags;eval(my_workspace),.local/tags;eval(my_workspace)
function ReloadTags()
let my_workspace = "/home/myname/workspace/"
let dirs = ['.git', '.hg', '.local'] # .local 为自定义习惯
let tags_dir = '.'
for dir in dirs
let founddir= finddir(dir, './;'..my_workspace, 1)
if strlen(founddir) > 0
let tags_dir = founddir
endif
endfor
exec 'silent! !ctags -R --tag-relative -f '..tags_dir..'/tags . &'
endfunction
ReloadTags() 方法判断当前的project VCS环境, 在对应的VCS local目录下 (.git/.hg), 生成 tags文件. 其中运行的命令 ` exec ‘silent! !ctags -R –tag-relative -f ‘..tags_dir..’/tags . & 中 silent 让vim不显示后续的输出, 末尾 &` 意为 background运行.
同时可以添加autocmd
autocmd BufWritePost *.go call ReloadTags()
go文件在Write操作后, 会运行ReloadTags()方法, 更新project目录下 .git/.hg 下的tags文件 也可以自己map keys 去手动 toggle.
4. 参考
https://kulkarniamit.github.io/whatwhyhow/howto/use-vim-ctags.html
