Citre: 先进的 Ctags 前端

你是指自定义 ctags 命令?

已经修好啦 :wink:

ctags命令好像已经可以自定义了。我是指自定义readtags 命令。因为我想filter out一些tags,因为不想让他们在imenu 或者是 treemacs tag mode中显示不必要的tags。 我对ctags命令不是很了解,但貌似readtags可以有过滤功能。要是ctags可以直接过滤也可以。我想要是readtag也可以自定义命令那就更好了。

这个不好弄啊,我也没有什么好的想法。目前是 Citre 根据环境推断需要过滤掉哪些 tags。你如果觉得自己可以写更好的规则的话,可以 advice citre-definition-default-filtercitre-completion-default-filter

Edit: 不如说说你不想要哪些 tag?

我的项目用systemverilog,用ctags后生成tags, 在imenu里同时显示<类名>.<成员名> 和 <成员名>, 两者指向是一样的所以重复,我想干掉其中一个,这样看上去就会简洁点。我对ctags和readtags都不是很熟悉。可能两个都有过滤功能。对我来说都可以,达到目的就行。写elisp对我来说目前能力和时间都有限所以暂时不考虑哇。ctags有没有相应的option可以在生成tags时直接过滤掉?要是imenu能过滤也行。

首先我假设你是用 Citre 生成 tags 文件。你只需要把 qualified tags 排除掉。编辑那个 tags 文件的 recipe,然后把命令修改一下:

...
--extras=*
--extras=-{qualified}
...

哦, 那就是修改ctags 命令就可以实现啦?那这qualified tags是指的哪个? 带类名的还是不带类名的?过滤掉后会不会影响跳转?

指的是带类名的。据我所知不可能做到排除不带的,留下带的。

假如你原来有一个 foo.bar 的 tag,如果你代码里也有 foo.bar,由于找定义抓的是光标下的符号,所以直接找的话只会帮你找 foobar 的定义,但 Citre 设计你可以先把它整个选中,然后用 jump/peek/xref 之类找到这个 foo.bar 的 tag。

排除之后,你只能从 foo 找到 foo 的定义,bar 找到 bar 的定义。

这样说能理解吗?

1 个赞

明白了!等我试试哈,给你反馈。谢谢啊!

可以了 目的达到!谢谢你啊。 我把问题想复杂了。原来这么简单。。Citre很好哦,祝项目github星标越来越多! :grin:

2 个赞

Centaur Emacs 现在应该可以和lsp/eglot一起工作了,快捷键也进行了更新。

1 个赞

我看了一下,有一点问题。

你的 company 配置同时启用了 capf 和 citre 后端,同时这个 capf 后端又是一个 citre + lsp 的联合后端。这两个只要用一个就行了。

另外就是文件里还是把 tags 叫做 TAGS。Citre 并不支持 TAGS 格式。我倒建议直接把这个模块叫 init-ctags,消除一切可能的误解。

另外建议给 citre-update-this-tags-file 一个快捷键。

1 个赞

有道理,我重构了下。

2 个赞

好问题,我也想寻找这个方案来着…… BTW,宏定义跳转怎么弄的,有方案吗?目前我用了下面的配置,感觉有点硬核

(defun my-config/init-citre()
  (use-package citre
    :init
    (require 'citre)
    (require 'citre-config)
    :defer t
    :config
    (defun my-verilog-get-symbol-atpt ()
      (let* ((sym (symbol-at-point))
             (char (substring (symbol-name sym) 0 1))
             )
        (if (string= "`" char)
            (substring (symbol-name sym) 1)
          (symbol-name sym))
        )
      )
    (defvar citre-lang-verilog-plist
      `(:get-symbol
        my-verilog-get-symbol-atpt
        )
      "Verilog language support for Citre.")

    (setf (alist-get 'verilog-mode citre-language-support-alist)
          citre-lang-verilog-plist)
    ))
1 个赞

我不懂 verilog 啊,但我看你这个应该是正解。要不要给 Citre 写个 PR?

Edit: 建议考虑下有选区的情况,Citre 在有选区时就会抓选区里的符号。参考 citre-get-marked-symbolcitre-get-symbol-at-point

话说你觉得,如果我在 imenu 里,把 qualified tags 单独分成一类,可以解决问题吗?

也问下 @my2817 的意见

1 个赞

在 Rust 项目上试了一下,感觉基本没法用呀,是我姿势不对吗?

在 133 行的 range 上执行的 citre-jump,出来一堆没用的,但是 range 就是在上面几行定义的呀

$ ctags --version
Universal Ctags 5.9.0(p5.9.20210725.0), Copyright (C) 2015 Universal Ctags Team

citre commit: 2bcdddb

你的姿势是对的。

基于 Ctags 的方案无法像 lsp 那样理解程序的语法结构,所以大部分 ctags 前端都是单纯地查找同名的 tag,也就是会给你所有叫做 range 的符号的定义。

Citre 做得稍微好一点,会尽量帮你排除肯定不需要的那些,这里面的大头是「不在当前文件,但具有文件内作用域的符号」。此外也可以根据具体情况,猜测用户想要的结果然后把它们排到前面去,我们对 C 做了一些这样的规则。但终究是不会像 lsp 那么准确的。

就我以往用 Citre 的经验,其实你要找 range 这种一看就到处都是的名字的话,25 个结果真的不算多 :rofl:

解决的方法是允许用户二次过滤。Citre 显示的结果是 种类/类型@作用域 文件(行号): 行内容 这样的格式,既然你用 citre-jump,这个时候可以再输入文件名、种类等信息过滤一下。我也想给 citre-peek 做个这样的东西,但目前没想好怎么弄。

对 Citre 和 lsp 的长短有兴趣的话,可看这个文档

1 个赞

多谢,明白了,等回来写其他语言我再试试吧,目前看还是要依赖 lsp 。

那段关于 my-verilog-get-symbol-atpt的函数,你看看怎么改合适更新到你的仓库里吧(我自己弄感觉肯定会把你代码搞乱)

分类是影响menu tool的菜单排布么?我习惯用 counsel-imenu,无分类,似乎对我没啥影响