我也编译了一下最新的 uctags,但不能复现。我怀疑你用的 readtags 程序可能不是 uctags 自带的。
试一下 $ readtags -h
看看有没有这么个 option:
-t TAGFILE | --tag-file TAGFILE
Use specified tag file (default: "tags").
Edit: 我刚想到一种可能性,你是不是把 citre-readtags-program
改成了 ctags
的路径?
我也编译了一下最新的 uctags,但不能复现。我怀疑你用的 readtags 程序可能不是 uctags 自带的。
试一下 $ readtags -h
看看有没有这么个 option:
-t TAGFILE | --tag-file TAGFILE
Use specified tag file (default: "tags").
Edit: 我刚想到一种可能性,你是不是把 citre-readtags-program
改成了 ctags
的路径?
感谢大神,ctags我经常用,也知道 Ctags 可以用正则或者 optscript 实现用户自定义 tag 规则
,但实际这么做时又搞不定,想请教一个这方面的问题,比如我写了以下代码:
return array(
"test_func"=>function(string $str="test_func"){
return $str;
}
);
这是一段php代码,返回一个数组其中定义了一个函数,将其保存为org.php
文件,然后调用这个函数时我是这么写的:
$org=require("./org.php");
echo $org["test_func"]("hello");
这种写法在php里应该比较少见,但在其它语言有近似用法的不少(比如nodejs的require等 )
我用universal-ctags生成的tags文件并不识别这种写法,不能在函数调用处$org["test_func"]()
跳转到函数定义,想通过自定义tag规则来支持这种写法,但正则水平不行,没搞定,只好来请教擅长ctags的高手们
对,确实是这个原因,我设置为nil就正常了。
$ cat options.ctags
--langdef=PHPext{base=PHP}
--kinddef-PHPext=a,arrayfunc,function defined in arrays
--regex-PHPext=/"(.*)"=>function/\1/a/
$ cat test.php
return array(
"test_func"=>function(string $str="test_func"){
return $str;
}
);
$ ctags --options=./options.ctags --fields='*' -f - test.php
test_func test.php /^ "test_func"=>function(string $str="test_func"){$/;" kind:arrayfunc line:2 language:PHPext roles:def extras:subparser
您可以把 options.ctags
存在 ~/.ctags.d/
或运行 ctags 时目录的 ./.ctags.d/
文件夹下,ctags 运行时就会自动加载它。
解析:
--langdef=PHPext{base=PHP}
定义一个叫 PHPext
的新语言(取 PHP extended 之意),作为 PHP 的 subparser 使用。
扩展一个 parser 时,定义一种新语言是被鼓励的,这样就不会和语言本身的 kind 冲突。如果运行一下 $ ctags --list-kinds=php
,可以看到已经有一个简写为 a
的 kind,表示 aliases
。
--kinddef-PHPext=a,arrayfunc,function defined in arrays
为 PHPext 定义一个简写为 a
,全名为 arrayfunc
的 kind,含义是 function defined in arrays
。
--regex-PHPext=/"(.*)"=>function/\1/a/
这一行的语法是 --regex-<LANG>=<PATTERN>/<NAME>/[<KIND>/]LONGFLAGS
。我们一段一段看:
"(.*)"=>function
用于匹配的模式。加括号的地方是这个函数的名字。
\1
把加括号的模式匹配到的部分拿出来作为 tag 的名字。
a
规定这个 tag 的 kind 为刚刚定义的 a
。
请参阅 Extending ctags with Regex parser (optlib)
如果还有问题,欢迎随时讨论,这样我可以多点例子来证明 ctags is hackable
大多数 ctags 用户都不知道 readtags 的存在,所以我想有这样的误操作也正常。我想一下怎么在 README 里强调一下这个事情。
readtags 是很有意思的程序,它自带了一个类 Scheme 的 DSL,用来指定对 tags 进行过滤和排序的规则。Citre 大量地使用了这个东西。
我平常是直接用 ctags -e
输出 etags 形式的文件,然后 Emacs 自带的 etags.el 就可以自动识别了,并且 xref 也是直接支持的,补全的话也有 company-etags
。
那么 citre 除了 citre-peek
外的优势在哪里呢?
说一些我现在能想到的。
最重要的是,TAGS 格式本身信息不是很丰富,基本上就只有名字和位置信息。tags 格式更加丰富一些,你看我的截图里那些符号都标明了种类(也就是说一个符号是 macro、function 还是别的什么)和类型。
基于这些信息,Citre 可以实现更准确的过滤和排序。一个很实用的东西是对非全局的符号,ctags 会标明它的 scope
是 file
。Citre 在找定义或补全时会排除具有 file scope,且不在当前文件中的符号。这样可以排除大量不需要的结果。
另外,注意到我自动补全的截图中,由于当前符号在一个 .
的后面,Citre 推测你需要一个结构体成员,因此补全出来的结果中 member
种类是放在其他结果前面的。虽然基于 ctags 的工具做不到 lsp 那么智能,但我们可以提供类似这样的贴心小规则。利用 TAGS 文件是不可能做到这一点的。
总而言之,更丰富的信息可以帮我们更好地理解代码。试想我们要做这样一件事情:我需要某个库里面的一个函数,我不知道它叫什么,但它的功能是插值,所以里面应该有 interpolate
之类的单词。。。我们可以把这些条件扔给 readtags,让它帮我们找我们想要的东西。或许在将来 Citre 会提供一个交互式工具让你做这件事情,而利用 TAGS 文件的话是绝无可能的。
另外,TAGS 格式是按照文件和行号顺序排序的,而 tags 是按照名字的字母顺序排序的,因此 readtags 可以做二分搜索,使得在大工程中补全和找定义也相当快。company-etags
应该做不到这一点。
Edit: 还有一点是 TAGS 文件一般是整个加载到一个 buffer 里使用的,对巨型工程来说不现实。Citre 不需要做这样的事情。
我试用了一下,就算在 LLVM 这种大项目下速度也还是非常快。LZ 不考虑给 Emacs 28 贡献一下嘛,例如把这个功能实现成一个 ctags.el,这样就能把以前 Emacs 自带的那个 etags 给废弃了。
感觉回复,这个问题解决了
还没试用。
既然支持编辑相关的功能(自动补全),那是不是考虑支持自动更新tags文件?大概看了下代码,没发现有这功能。
祝你用得开心
关于进 Emacs 这件事,其实我也考虑过。我想做的是把 citre-core.el
分离出来,作为一个 readtags 抽象层并入 Emacs(masatake 也和我说过这个想法)。这样别人也可以基于这个东西开发各种各样的工具。但 readtags 并不是像 grep 那样流行的程序,我想 Emacs 很可能不会想要这个东西。
而且 Citre 要求使用 Universal Ctags 的 readtags。Exuberant Ctags 其实也带一个 readtags,但缺少利用类 Scheme 表达式进行过滤和排序的关键特性。考虑到仍然有很多人使用 Exuberant Ctags,如果我被要求支持这个 readtags 的话,很多事情就没法做了。
其他的做法,像是做一个利用 Emacs 自带功能读 tags 文件的东西,或者做一个 xref 的 ctags 后端,我觉得意义不大,不如直接用 Citre。
ctags
的-e
选项保证生成的TAGS文件和etags
是兼容的。我建议不要再使用etags
了。etags
生成的TAGS文件的行可能有两种格式。一种通用的格式是ctags也会生成的。另一种老的格式ctags不会生成。麻烦在于emacs插件为了兼容老的格式,导致搜索速度慢4倍。
我认为这个没有必要。
利用 tags 文件,可以基于行号和 search pattern 来定位 tag 所在位置。search pattern 保证了即使文件被编辑过,我们仍然能找到需要的那一行。Citre 甚至会尝试在这一行本身被编辑过的情况下找到它。
我在使用中的感觉也是,基本上文件编辑过以后也不影响跳转的准确性。
我建议在项目的 Makefile(或者自己再新建一个 makefile 并在版本管理系统中排除)中添加一个生成 tags 的 target,然后包装一个命令来运行它。需要的时候手动升级就好。
如果你认为自动更新很重要的话,我想听听你的理由
主要是为了方便。
编辑可能会删/增/改,一是会导致已有的tag失效,二是新增/被修改的tag不能及时反映到自动补全/跳转中。我的理解是这两点是你说的“基于行号和search pattern”无能为力的。
当然,因为有你说的利用makefile或者直接执行命令来更新tags,所以在Citre里添加支持的必要性和急迫性都不高。
这种情况确实需要更新 tags。但一般一个项目大到需要索引的程度的时候,改一会也不会造成显著的准确性失效。注意到出问题的时候,执行一个命令更新一下也不麻烦。
赞一个,日常偶尔也会需要阅读c代码。不过我是使用gtags+ggtags。不知道跟ctags哪个好用点?
我不熟悉 gtags,就随便说说我自己的认识,有错误请多包涵
Ctags 的优势:
gtags 的优势:
另外,gtags 的索引是二进制格式,tags 文件是纯文本,但可以做二分搜索。我也不太清楚二者搜索起来哪种比较快,但我个人的经验(还有楼上的体验)是 ctags 在巨型项目里是足够快的。
可以提供一些命令方便用户操作,projectile就有 projectile-regenerate-tags
。
试了一下,不知道怎么让自动补全工作?jump是可以的,LZ真的强啊