Citre 0.4 发布了!
Xref 后端适配器
Citre 现在增加了 Xref 后端适配器,可以把 Xref 后端变成 Citre 后端,从而利用 Citre 提供的代码阅读工具。
基于这个适配器,提供了 eglot 后端。准确的定义/引用查询配合 citre-peek
不离开 buffer 就可以追踪代码逻辑的设计,可以提供流畅的代码阅读体验。
(不离开当前文件就可以 peek 到系统库中的定义)
用户手册还提供了把 elisp 的 xref 后端变成 Citre 后端的例子,非常简单。您可以参照这个例子来适配其他的 xref 后端,比如 dumb-jump 等等。
citre-query-*
命令
现在提供了 citre-query-jump(-to-reference)
和 citre-query-peek(-reference)
命令。直接 call 的话会让用户输入符号名字;带有 prefix argument 的话则会在用户输入时使用项目中的符号作为补全。
重写了 tags 文件生成命令
这部分移除了一些少用的特性,此外原先是将命令嵌入 tags 文件中的(太 hack 了),现在改成了使用 Universal Ctags 本身就支持的 option file。因此更新后您可能需要重新生成已有的 tags 文件。
24 个赞
ksqsf
3
很好用,感谢作者的贡献!
我之前就想自己手搓第二个功能,但是还没等我开始写代码,新版就推出这个功能了!很凑巧
1 个赞
以前用 Citre 来浏览大型项目(如 linux kernel)或 lsp 不支持的项目,但因为和 eglot 有冲突,就关闭了,现在好了,又可以愉快的使用了。感谢大佬的工作!
这个新特性引入一个Bug:不能指定dir/file了。
原因是: ctags
传参格式是 ctags [options] [file(s)]
,目前向导生成的dir/file是在 option file 中(citre-ctags-cmd-buf-add-dir-or-file),所以 ctags 没有读取 dir/file 入参。
我本地验证结果:
ctags --options=/home/archer/.cache/tags/\!home\!archer\!workspace\!android\!xxxx\!android\!.ctags
- 只能生成当前项目的tag
ctags --options=/home/archer/.cache/tags/\!home\!archer\!workspace\!android\!xxxx\!android\!.ctags ./ /home/archer/workspace/aosp/frameworks/base
- 能生成当前项目的tag + aosp/frameworks/base(指定的外部项目)的tag
谢谢,这是我疏忽了,已经修复了,办法是又加了一个文件储存要扫描的文件列表,然后在 option file 中用 -L
选项读取。
Youmu
8
好像新版本没有再将 tags 放在 .ctags.d 目录下了,这是为啥呀?
考虑到用户「把 tags 文件放在特别的地方」一般是为了避免污染工程目录,现在就留了三种方式:
-
工程目录下 .ctags.d
目录放 option file,tags
或 .tags
是 tags 文件。这个做法好处是生成以后你可以直接在工程目录下运行 $ ctags
来更新。
-
所有相关的文件全都放到 ~/.cache/tags/
。
-
有其他特殊需求,手动生成 tags 文件,再把 citre-tags-file
作为 directory-local variable 来修改(用户手册有例子)。
我一直都是 lsp 和 citre 同时使用,然后各用各个的😂 tags强在跨语言的补全能力,同时还不需要本地工具链即可使用。两套系统一起使用这么久了也没觉得有什么问题,挺舒服的。即便现在 citre-peek 能配合 lsp 来用,感觉也没有什么配置的动力😂
kinono
11
其实不需要配置的,M-x eglot
以后 eglot 后端就可用了,跳转不了的时候 tags 和 global 会作为 fallback。eglot 后端的主要意义是让 lsp 能用上 citre-jump
和 citre-peek
。
citre0.4 我在没有开启eglot时,总会提示错误:error in process sentinel: Selecting deleted buffer
开启eglot以后错误就消失了。这是不是需要有啥配置可以忽略eglot?感觉是citre默认在读取eglot的buffer?
kinono
13
我一般也不开 eglot,没碰见这个错误。可否提供复现步骤或者 backtrace?
有一个问题哈,citre 中我使用 tags 作为 backends,company-mode 中我的 backends 是(company-capf company-keywords company-dabbrev company-files)
,但是开启补全时(C++),我写 uint32_
这种它似乎也会去 tags 中搜寻,然后会特别卡。
另一个 readtags 的疑惑就是,每一次 peek
或者 complete 操作,都回去 tags 整个文件中做 binary search 吗?我有这个疑问是因为我的 tags 文件很大(6GiB),我怕太频繁的查询会消耗硬盘过度
kinono
15
我写 uint32_
这种它似乎也会去 tags 中搜寻,然后会特别卡。
它肯定是会去搜寻的。tags 后端不分析语法的,写任何一个单词都会尝试补全。
要说卡的话倒不一定,您可以检查一下 citre-capf-optimize-for-popup
是否为 t
,如果是的话,在 readtags 找补全的时候用户输入可以打断它,这样就不会卡,我在内核里面实测也不会卡。但这个我也不敢保证,因为进程相关的东西好像在不同机器上表现不一样,这块一直有用户报告一些我怎么也复现不了的问题。
每一次 peek
或者 complete 操作,都回去 tags 整个文件中做 binary search 吗?
会的。
我的 tags 文件很大(6GiB),我怕太频繁的查询会消耗硬盘过度
如果找定义的话,正因为使用了 binary search,搜索大小 tags 文件的代价不会差别很大;补全的话可能会有些影响,因为大 tags 文件里同一前缀的符号可能更多。
其实我个人更建议的方案是弹出式补全只用 dabbrev 这类的后端,需要补全其他的符号时用 completion-at-point
(默认快捷键是 C-M-i
)。这样用起来干扰比较少,什么时候读 tags 文件也可控。
2 个赞
奥我懂了,关键点是补全的形式选择,即 popup 还是 manually trigger,这个我倒是没考虑。之前一直用 Clion,它是 popup 形式,里面内容感觉是精心准备筛选过的的(感觉很准确)。
另外一个问题是,你提到“弹出式补全只用 dabbrev 这类的后端”,那 company 插件是如何判断弹出式补全使用那些 backends 呢?(假如我配置了 company-capf company-keywords company-dabbrev company-files
)
以及一个衍生问题,citre 如果使用 tags 补全,它是如何和 company mode 配合的呢? 是 company 问 company-capf backend 获取补全列表,而后者通过 capf 机制调用completion-at-point-functions
吗(其中 citre 去修改 completion-at-point-functions
变量?)
kinono
17
那 company 插件是如何判断弹出式补全使用那些 backends 呢
company 会逐个尝试 company-backends
里面的后端。
是 company 问 company-capf backend 获取补全列表,而后者通过 capf 机制调用completion-at-point-functions
吗(其中 citre 去修改 completion-at-point-functions
变量?)
就是这样的。
1 个赞
kinono 你好。看到 Citre 的介绍,觉得这就是我一直在寻找的代码浏览工具。可是,在把 Citre 用在我工作的项目里时遇到一个问题。
我的本地工作区里有许多库,我不需要修改的库是以符号链接的形式指向远程的目录(每日构建的文件),我要修改的库是本地的文件。下面是个例子。
[project_root]
- TAGS
- @lib1 --> /remote/dailybuild/lib1
- @lib2 --> /remote/dailybuild/lib2
- [lib3]
- a.h
- a.c
我在项目根目录下生成 TAGS 文件,用 Citre 浏览 lib3 下的代码没有问题。可是如果浏览 lib1、lib2 下的代码,Citre 不找项目根目录下的 TAGS 文件,而是尝试到 /remote/dailybuild 下找,结果找不到。
我对 Emacs Lisp 不太了解,勉强分析了 Citre 的代码,好像是通过 file-truename 函数把符号链接的路径转成实际路径了。但是,我把 file-truename 删除了也是不行,不确定这是否根本原因。
能否给 Citre 加上一个设置变量,让用户选择对符号链接的处理方法?
谢谢!
kinono
20
我把 file-truename 删除了也是不行,不确定这是否根本原因。
首先要确定通过符号链接打开文件的时候,(buffer-file-name)
返回的是否是实际路径。如果不是,按我对代码的理解,应该像你说的去掉 file-truename
就可以工作了。如果是的话,可能是 vc-follow-symlinks
变量影响。
能否给 Citre 加上一个设置变量,让用户选择对符号链接的处理方法?
我也不是完全清楚 Emacs 怎么处理符号链接,有哪些配置会影响,所以也不敢贸然做这样的改动。根据你的需要,建议通过修改 citre-tags-file
的方式手工指定 tags 文件的路径。这是一个「directory-local variable」,请参考 Emacs 文档。这里给出一个示例,把以下的代码添加到你的 init.el
:
(dir-locals-set-class-variables
'project-name-here
'((nil . ((citre-tags-file . "/path/to/tags/file")))))
(dir-locals-set-directory-class
"/remote/dailybuild/lib1" 'project-name-here)
(dir-locals-set-directory-class
"/remote/dailybuild/lib2" 'project-name-here)
最后我想确认一下你的 TAGS
文件是否确实由 ctags 生成?因为 ctags 默认生成的文件名是 tags
,TAGS
文件通常是 etags
生成的,这是和 ctags
不同的程序,二者的格式也不同。
首先要确定通过符号链接打开文件的时候,(buffer-file-name)
返回的是否是实际路径。如果不是,按我对代码的理解,应该像你说的去掉 file-truename
就可以工作了。如果是的话,可能是 vc-follow-symlinks
变量影响。
我今晚继续研究,发现的确用错 ctags 文件了。昨晚折腾了很久,试验了很多次,重新生成了许多次 ctags 不同的输出,最后糊涂了,用了 TAGS 文件。
这里总结一下。
我研究了 citre-ctags.el
文件里的源代码,发现里面有些代码使用 file-truename
函数处理路径。下面是 citre-tags-file-path
函数里的一行代码。
(_ (let* ((current-dir (file-truename (citre-current-dir)))
假设我在 Emacs 中打开符号链接 lib1 下面的一个文件 lib1.c。这时通过 M-:
执行 (citre-current-dir)
,得到的路径是 [project_root]/lib1/lib1.c
。如果通过 M-:
执行 (file-truename (citre-current-dir))
,得到的路径就变成了 /remote/dailybuild/lib1/lib1.c
。remote
没有 tags 文件,当然找不到。
于是,我推测 file-truename
是造成问题的原因,于是尝试手工把这行代码里的 file-truename
删除(不知道这种修改能不能用 advice),然后试验 Citre。
昨晚用错了 ctags 文件,导致试验失败。今天我重新生成 tags 文件,目前试下来能够使用 Citre 在代码之间跳转了。中间有一次出错,但后面没有重现,不知道还有没有潜在的问题,我继续测试。