Citre: 先进的 Ctags 前端

如果我都设为 emacs.git, 在 src/1.c 运行更新 tags 文件命令的时候, 会报

Can't find tags file for this buffer.  Create one? (y or n) 

怎会如此 :rofl: 我回去也看下吧

1 个赞

windows原生emacs当代码中文路径时,会提示这个,是不是要改一下编码变量。 英文路径就没有这个问题。

citre-core--get-lines: readtags exits 1
d:\DevTools\ctags\readtags.exe: cannot open tag file: Invalid argument: d:/Hello/管理系  ?Codes/ReactApp/.tags/.!.tags

你试试?我不熟悉 Windows :rofl:

对了,我是用 ~/.cache/tags

有些中文字符的coding-system都不知要啥编码,所以算了,自己写个函数生成tag其实也行。

那之后也没法用啊 :rofl: 读 tags 文件用的也是 readtags

现在只能将代码放在没有中文路径的地方。英文路径下就没发现有啥问题。 windows下那个code-system的编码,我也不是很懂。

我使用下面的代码测试了一下

(defun citre-core-write-pseudo-tag (tagsfile name value comment)
  "Write a pseudo tag to TAGSFILE.
TAGSFILE is the absolute path of the tags file.  NAME is the name
of the pseudo tags, without the beginning \"!_\".  VALUE is its
value, and COMMENT is its comment info.

When there's already a pseudo tag with the same name, COMMENT
will not overwrite the original comment."
  (setq comment (concat "/" comment "/;\""))
  (with-temp-file tagsfile
    (insert-file-contents tagsfile)
    ;; Jump over all pseudo tags.
    (while (eq (char-after) ?!)
      (forward-line))
    ;; Record the point position, and don't search beyond it later.
    (let ((end (point)))
      (goto-char 0)
      (if (search-forward (concat "!_" name) end 'noerror)
          (progn
            (dotimes (_ 2)
              (search-forward "\t"))
            (setq comment (buffer-substring (point) (line-end-position)))
            (delete-region (line-beginning-position) (line-end-position))
            (unless (eobp)
              ;; Delete the newline character.
              (delete-char 1)))
        (goto-char 0))
      (insert "!_" name "\t" value "\t" comment "\n")
      (write-region (point-min) (point-max) (format "%s%s" tagsfile (random 10)) nil :silent)
      )))

得到三个文件

  -rw-r--r--  1 feng feng 11308748  7月 15 16:57 !home!feng!emacs!emacs.git!src!.tags
  -rw-r--r--  1 feng feng 11308748  7月 15 16:57 !home!feng!emacs!emacs.git!src!.tags3
  -rw-r--r--  1 feng feng      204  7月 15 16:57 !home!feng!emacs!emacs.git!src!.tags4

  1. tags
!_CITRE_CMD	ctags|-o|%TAGSFILE%|--languages=C|--kinds-all=*|--fields=*|--extras=*|-R	/command line to generate this tags file/;"
!_TAG_FILE_FORMAT	2	/extended format; --format=1 will not append ;" to lines/;"	extras:pseudo
!_TAG_FILE_SORTED	1	/0=unsorted, 1=sorted, 2=foldcase/;"	extras:pseudo
!_TAG_OUTPUT_FILESEP	slash	/slash or backslash/;"	extras:pseudo
!_TAG_OUTPUT_MODE	u-ctags	/u-ctags or e-ctags/;"	extras:pseudo
!_TAG_PATTERN_LENGTH_LIMIT	96	/0 for no limit/;"	extras:pseudo
!_TAG_PROGRAM_AUTHOR	Universal Ctags Team	//;"	extras:pseudo
!_TAG_PROGRAM_NAME	Universal Ctags	/Derived from Exuberant Ctags/;"	extras:pseudo
!_TAG_PROGRAM_URL	https://ctags.io/	/official site/;"	extras:pseudo
!_TAG_PROGRAM_VERSION	0.0.0	//;"	extras:pseudo
../lwlib/lwlib.h	menu.c	/^#include "..\/lwlib\/lwlib.h"/;"	kind:header	line:36	language:C	roles:local	extras:reference
../lwlib/lwlib.h	term.c	/^#include "..\/lwlib\/lwlib.h"/;"	kind:header	line:54	language:C	roles:local	extras:reference
../lwlib/lwlib.h	widget.c	/^#include "..\/lwlib\/lwlib.h"/;"	kind:header	line:43	language:C	roles:local	extras:reference
../lwlib/lwlib.h	xfns.c	/^#include "..\/lwlib\/lwlib.h"/;"	kind:header	line:85	language:C	roles:local	extras:reference
../lwlib/lwlib.h	xmenu.c	/^#include "..\/lwlib\/lwlib.h"/;"	kind:header	line:87	language:C	roles:local	extras:reference
../lwlib/xlwmenu.h	xfns.c	/^#include "..\/lwlib\/xlwmenu.h"/;"	kind:header	line:97	language:C	roles:local	extras:reference
../lwlib/xlwmenu.h	xmenu.c	/^#include "..\/lwlib\/xlwmenu.h"/;"	kind:header	line:79	language:C	roles:local	extras:reference
../lwlib/xlwmenu.h	xterm.c	/^#include "..\/lwlib\/xlwmenu.h"/;"	kind:header	line:93	language:C	roles:local	extras:reference
../oldXMenu/XMenu.h	xmenu.c	/^#include "..\/oldXMenu\/XMenu.h"/;"	kind:header	line:91	language:C	roles:local	extras:reference
A	gmalloc.c	/^#define BLOCK(A)	((size_t) ((char *) (A) - _heapbase) \/ BLOCKSIZE + 1)$/;"	kind:macroparam	line:165	language:C	scope:macro:BLOCK	roles:def
A	image.c	/^#define COLOR(A, X, Y) ((A) + (Y) * img->width + (X))$/;"	kind:macroparam	line:5650	language:C	scope:macro:COLOR	roles:def
A	intervals.c	/^rotate_left (INTERVAL A)$/;"	kind:parameter	line:323	language:C	scope:function:rotate_left	typeref:typename:INTERVAL	file:	roles:def	extras:fileScope
A	intervals.c	/^rotate_right (INTERVAL A)$/;"	kind:parameter	line:272	language:C	scope:function:rotate_right	typeref:typename:INTER

...
  1. tags3
!_CITRE_CMD	ctags|-o|%TAGSFILE%|--languages=C|--kinds-all=*|--fields=*|--extras=*|-R	/command line to generate this tags file/;"
!_TAG_FILE_FORMAT	2	/extended format; --format=1 will not append ;" to lines/;"	extras:pseudo
!_TAG_FILE_SORTED	1	/0=unsorted, 1=sorted, 2=foldcase/;"	extras:pseudo
!_TAG_OUTPUT_FILESEP	slash	/slash or backslash/;"	extras:pseudo
!_TAG_OUTPUT_MODE	u-ctags	/u-ctags or e-ctags/;"	extras:pseudo
!_TAG_PATTERN_LENGTH_LIMIT	96	/0 for no limit/;"	extras:pseudo
!_TAG_PROGRAM_AUTHOR	Universal Ctags Team	//;"	extras:pseudo
!_TAG_PROGRAM_NAME	Universal Ctags	/Derived from Exuberant Ctags/;"	extras:pseudo
!_TAG_PROGRAM_URL	https://ctags.io/	/official site/;"	extras:pseudo
!_TAG_PROGRAM_VERSION	0.0.0	//;"	extras:pseudo
../lwlib/lwlib.h	menu.c	/^#include "..\/lwlib\/lwlib.h"/;"	kind:header	line:36	language:C	roles:local	extras:reference
../lwlib/lwlib.h	term.c	/^#include "..\/lwlib\/lwlib.h"/;"	kind:header	line:54	language:C	roles:local	extras:reference
../lwlib/lwlib.h	widget.c	/^#include "..\/lwlib\/lwlib.h"/;"	kind:header	line:43	language:C	roles:local	extras:reference
../lwlib/lwlib.h	xfns.c	/^#include "..\/lwlib\/lwlib.h"/;"	kind:header	line:85	language:C	roles:local	extras:reference
../lwlib/lwlib.h	xmenu.c	/^#include "..\/lwlib\/lwlib.h"/;"	kind:header	line:87	language:C	roles:local	extras:reference
../lwlib/xlwmenu.h	xfns.c	/^#include "..\/lwlib\/xlwmenu.h"/;"	kind:header	line:97	language:C	roles:local	extras:reference
../lwlib/xlwmenu.h	xmenu.c	/^#include "..\/lwlib\/xlwmenu.h"/;"	kind:header	line:79	language:C	roles:local	extras:reference
../lwlib/xlwmenu.h	xterm.c	/^#include "..\/lwlib\/xlwmenu.h"/;"	kind:header	line:93	language:C	roles:local	extras:reference
../oldXMenu/XMenu.h	xmenu.c	/^#include "..\/oldXMenu\/XMenu.h"/;"	kind:header	line:91	language:C	roles:local	extras:reference
A	gmalloc.c	/^#define BLOCK(A)	((size_t) ((char *) (A) - _heapbase) \/ BLOCKSIZE + 1)$/;"	kind:macroparam	line:165	language:C	scope:macro:BLOCK	roles:def
A	image.c	/^#define COLOR(A, X, Y) ((A) + (Y) * img->width + (X))$/;"	kind:macroparam	line:5650	language:C	scope:macro:COLOR	roles:def
A	intervals.c	/^rotate_left (INTERVAL A)$/;"	kind:parameter	line:323	language:C	scope:function:rotate_left	typeref:typename:INTERVAL	file:	roles:def	extras:fileScope
A	intervals.c	/^rotate_right (INTERVAL A)$/;"	kind:parameter	line:272	language:C	scope:function:rotate_right	typeref:typename:INTERVAL	file:	roles:def	extras:fileScope
ABI_VERSION	comp.c	/^#define ABI_VERSION /;"	kind:macro	line:432	language:C	file:	roles:def	extras:fileScope	end:43

...
  1. tags4
!_TAG_PROC_CWD	/home/feng/emacs/emacs.git/src/	/dir in which ctags runs/;"
!_CITRE_CMD	ctags|-o|%TAGSFILE%|--languages=C|--kinds-all=*|--fields=*|--extras=*|-R	/command line to generate this tags file/;"

@tumashu

  1. 创建完 tags 文件再升级时,提示找不到 recipe 的问题,已在 master 分支修复。

    原因很蠢 :rofl: 就是我写代码的时候考虑 CITRE_CMD 是 Citre 自己加的,TAG_PROC_CWD 是 Ctags 会写的,所以创建完 tags 文件以后就只写了 CITRE_CMD,忘记了部分 ctags 程序并不会写 TAG_PROC_CWD

  2. 另外这个「指定在某目录中使用某 tags 文件,结果在其子目录中找不到」,我这里复现不了。如果你继续碰到这个问题,希望能提供 Citre 的配置,以及生成 tags 文件时的具体操作。

4 个赞

我只能说 666

赞美楼主!!

总算可以快乐地编MATLAB了,之前真小破车一直将就着(啥没有,就Emacs原生功能撑着),撒花~~

3 个赞

两个问题都消失了,可能本身就是一个问题,另外我提交了一个小 pr,更新了一下 prompt,个人感觉更漂亮一点 :crazy_face:

Citre save tags file.
[1] Save to a directory specified.
[2] Save to global cache directory.
[3] Save to project cache directory.
[4] Save to a file specified (modify `citre-tags-file-alist' is required).
==> Please type a number (1-4) to choose: 
1 个赞

我个人现在超喜欢目前的交互状态,不多不少,完美!

我目前工作流程是:对没tagging过的项目,建个空的.tags文件,关掉 -> 运行更新tags命令(第一次会提示写ctags recipe)。搞定,快乐地使用~

个人不是很喜欢lsp那种minibuffer里 prompt满天飞,有种windows式的笨拙感。

这个recipe的设计,简单大方又自由度极大;如需要每个子文件夹都可以来个.tags,完美避免上百M tags文件parsing速度慢的问题(linux kernel 2.6的源文件,如用同一个tags文件,跳转没问题,但用imenu很慢,得有个几秒)

拯救了Emacs下编辑matlab重度用户啊。无跳转好些年了,一直靠Ivy + ag/rg这类搜索方式撑着。

3 个赞

如果补全库函数的功能加强就完美了

这一步应该也不需要。你可以直接 citre-create-tags-file,会有个向导帮你创建,或者 citre-update-this-tags-file,在找不到 tags 文件的情况下也会问你要不要创建。

个人喜欢 ctags 的主要原因还是 hackable :wink: 我也觉得 lsp-mode 有点太打扰用户了。

这个是因为 tags 文件是按 tag 名字的字母顺序排序的,在已知名字(跳转到定义的情况)或名字的开头一部分(补全的情况)时,可以做二分搜索。imenu 的话是不知道名字但知道路径,所以只能把所有 tags 全过一遍。

我之前见过一种做法是运行 imenu 的时候,直接 tag 一下当前文件,在临时路径里生成一个 tags 文件。感觉对大工程来说这样反而快一点。

目前的话你可以在 tags 文件的 recipe 里面增加扫描的路径,把外部库一起扫描了。不知你觉得这样好不好?

将来的话还是打算做一个交互式过滤 tags 文件的工具,这样可以按条件从某个 tags 文件中过滤出所需的符号然后插入当前 buffer。

我个人其实对补全也不是很依赖。我觉得补全主要就是告诉用户「你拼写对了」这么个作用,Emacs 的 dabbrev 也很可以了。

我自己的经验是,真正感觉需要「补全」的时候,其实是已经忘记了那个符号怎么拼,但我知道「它在哪个库里面,或者路径里有哪个单词,然后符号里面有一个什么单词,然后它是个函数/变量/类」之类的信息。这种时候补全没有用,substring 补全还算有点用(就是你可以敲那个符号的一部分来补全,不一定要敲开头。Citre 支持,见文档),真正有用的就是交互式过滤。

1 个赞

话说设计的时候我就考虑到这种情况(虽然我自己完全没想出什么使用场景),然后设计了能给不同文件夹用不同 tags 文件的机制,没想到真的有人这么用 :wink:

1 个赞

谢谢指导!还在学习使用中,不过目前体感已经很赞了~

这个似乎不错啊,因为imenu也就需要个当前的文件的临时tags文件;单体文件都不会大,所以这个方法完全可行。这个想法很赞!

个人感觉哈,纯探讨。毕竟citre是个tagging系统,跳转、列表才是主业。补全只提供个大概(自己代码的)感觉就可以了。更复杂的需求还是尽量交给lsp这类的,要不然太臃肿了,而且也会变慢。我记得针对C/C++有过一个rtags的东东,配出来也没咋用过,太臃肿了(接管大多lsp的活,那时还没lsp),最后导致使用兴趣缺缺。

其实我就是想有的时候看下第三方库的源码,可能和语言有关系,我现在用 go 可以很方便跳进源码,因为本地就有存。

Anyway, 我就是随口一说, ctags 目前我觉得已经挺完美的了

1 个赞

这个的问题是需要预设一个 ctags 命令,那肯定是要写一个对大多数情况适用的 universal ctags 命令(目前我是打算这么做)。这样用户对这个工程调整过的参数就不能反映了,或者如果使用 hasktags 之类其他的 ctags 程序也就不能用了。

我本来想到现在 tags 文件自带 recipe 了,可以直接从里面读命令,但是仔细想一下,被扫描的路径也是写在命令里面的,而且没有可靠的办法把它们分离出来(特别是使用其它 ctags 程序的情况),所以还是没用了。

1 个赞