Bootstrap

ctags使用教程

Ctags
ctags在http://ctags.sourceforge.net/下载源码,编译后安装。常规的标记命令为 ctags -R 。"-R"表示递归创建,也就包括源代码根目录下的所有子目录下的源程序。
绝大多数Unix系统上都会预装这个程序。
ctags的功能:扫描指定的源文件,找出其中所包含的语法元素,并将找到的相关内容记录下来。

1.ctags可以识别哪些语言,是如何识别的
ctags识别很多语言,可以用如下命令来查看:
ctags --list-languages
注记:下图没截全
在这里插入图片描述

ctags是可以根据文件的扩展名以及文件名的形式来确定该文件中是何种语言,从而使用正确的分析器。可以使用如下命令来查看默认哪些扩展名对应哪些语言:
ctags --list-maps
在这里插入图片描述

还可以指定ctags用特定语言的分析器来分析某种扩展名的文件或者名字符合特定模式的文件。例如如下命令告知ctags,以inl为扩展名的文件是c++文件。
ctags --langmap=c++:+.inl -R

并不十分清楚ctags使用何种技术来解析内容,估计包括正则表达式、词法分析、语法分析等等。但ctags不是编译器也不是预处理器,它的解析能力是有限的。例如它虽然可以识别宏定义,但对于使用了宏的语句的识别还是有缺陷的,在一些稍微正规点的代码(例如ACE的库或VC的头文件等)中的某些常规的宏使用方式会导致ctags无法识别,或者识别错误,从而使得ctags没有记录user想记录的内容,或者记录下的信息不准确。另一方面ctags也有聪明的一面,例如在cpp文件中扫描到static的全局变量时,ctags会记录这个变量,而且还会标明说这个变量是局限于本文件的,同样的定义,如果放在h文件中,ctags则不会标明说这个变量是局限于本文件的,因为ctags认为h文件是头文件的一种,会被其他文件include,所以在其他文件中可能会用到该h文件里定义的这个全局变量。

2.ctags可以识别和记录哪些语法元素
可以用如下命令查看ctags可以识别的语法元素:
ctags --list-kinds

或者单独查看可以识别的c++的语法元素
ctags --list-kinds=c++

ctags识别很多元素,但未必全都记录,例如“函数声明”这一语法元素默认是不记录的,可以控制ctags记录的语法元素的种类。如下命令要求ctags记录c++文件中的函数声明和各种外部和前向声明:
ctags -R --c+±kinds=+px

一个例子
ctags -R --languages=c++ --langmap=c++:+.inl -h +.inl --c+±kinds=+px --fields=+aiKSz --extra=+q --exclude=lex.yy.cc --exclude=copy_lex.yy.cc

在这里插入图片描述

可以指定用于生成tag的文件
ctags *.c 为当前目录下的所有C程序文件生成对应的tags文件

在这里插入图片描述

尽管有很多可用的选项,但仍设置了默认值,以使ctags最常执行而没有任何选项(例如“ ctags *”或“ ctags -R”),这将在当前目录中为所有可识别的源文件创建一个标记文件。 。 提供以下描述的选项仅是为了允许定制剪裁以满足特殊需求。
请注意,将单字母选项与其参数分隔的空格是可选的。(这是linux中通用的语法)
另请注意,可以省略长格式选项(以“-”开头并带有“ [= yes | no]”参数的布尔值)的布尔参数,在这种情况下,意味着“ = yes”。 (例如–sort等价于–sort = yes)。 还要注意,“ = 1”和“ = on”被认为是“= yes”的同义词,而“ = 0”和“ = off”被认为是“ = no”的同义词。如-R(–recurse)参数
在这里插入图片描述

有些选项仅在以etags模式运行时使用时才被忽略或有用(请参阅-e选项)。 这些选项将被注意。
在这里插入图片描述

大多数选项可能会出现在命令行的任何位置,仅影响该选项后的那些文件。 但是,一些选项必须出现在第一个文件名之前,并且将这样指出。
带有语言名称的选项将接受大小写的名称。
有关内置语言名称的完整列表,请参见–list-languages选项。

-R
表示扫描当前目录及所有子目录(递归向下)中的源文件。并不是所有文件ctags都会扫描,如果用户没有特别指明,则ctags根据文件的扩展名来决定是否要扫描该文件——如果ctags可以根据文件的扩展名可以判断出该文件所使用的语言,则ctags会扫描该文件。
在这里插入图片描述
在这里插入图片描述

默认对当前目录进行递归遍历,符号链接也会递归进去遍历
可以使用L参数来指定文件列表

–languages=c++
只扫描文件内容判定为c++的文件——即ctags观察文件扩展名,如果扩展名对应c++,则扫描该文件。反之如果某个文件叫aaa.py(python文件),则该文件不会被扫描。
在这里插入图片描述

指定生成标记的语言,列表包含逗号分隔的语言名称列表(不区分大小写;内置的或用户自定义语言类型)。
如果列表的第一种语言没有以“ +”或“-”开头,则将在添加或删除列表中的语言之前清除当前列表。在遇到“-”之前,列表中的每种语言都将添加到当前列表中。由于在列表中遇到“ +”或“-”时,其后的语言将被添加到当前列表中或从当前列表中删除。因此,用新列表替换当前列表,或从当前列表中添加或删除语言变得很简单。
因此–languages=c++为先把当前列表清空,然后添加c++进当前列表

将要生成标签的文件的实际列表取决于有效的语言扩展名映射(请参见–langmap选项)。
因为是先根据选中的语言类型,决定有那些文件后缀
如–languages=c++有如下后缀,再根据这些后缀决定哪些文件用来生成tags
在这里插入图片描述

请注意,除非使用此选项明确禁用某些语言,否则将启用所有语言,包括用户定义的语言。列表中包含的语言名称可以是任何内置语言,也可以是以前使用–langdef定义的一种语言。默认值为“ all”,也被接受为有效参数。
有关内置语言名称的完整列表,请参见–list-languages选项。

–langmap=c++:+.inl
告知ctags,以inl为扩展名的文件是c++语言写的,在加之上述2中的选项,即要求ctags以c++语法扫描以inl为扩展名的文件。
在这里插入图片描述

控制文件名如何映射到语言(请参见–list-maps选项)。每个逗号分隔的映射均由语言名称(内置或用户自定义的语言),冒号以及文件扩展名和/或文件名pattern的列表组成。通过在扩展名前加句点(例如“ .c”)来指定文件扩展名。通过将文件名pattern放在括号中来指定文件名模式,例如 ([Mm] akefile) 。
如果C编译器的运行时库(runtime library)提供了适当的支持,则文件名模式可能包含Unix上常见的常规Shell通配符,在传递给ctags之前,一定要把参数用双引号括起来(quote the option parameter)以保护通配符不被shell展开。
您可以通过检查–version选项的输出来确定shell通配符在您的平台上是否可用,该选项在已编译的功能列表中将包括“ +通配符”;否则,使用简单的文本比较将文件名模式与文件名进行匹配。
在这里插入图片描述

使用该选项映射文件扩展名到语言时,将首先将其与任何其他语言解除映射。

在这里插入图片描述

如果映射中的第一个字符是加号,则该映射中的扩展名和文件名模式将附加到该语言的当前映射中;例如,要指定仅将扩展名为.c和.x的文件视为C语言文件,请使用 --langmap = c:.c.x ;还要将扩展名为.j的文件添加为Java语言文件,请指定 --langmap = c:.c.x,java:+.j 。
要将生成文件(例如,文件名为Makefile、makefile或扩展名为.mak的文件映射到一种名为make的语言,请指定 --langmap = make:([[Mm]akefile).mak。
要映射没有扩展名的文件,请指定一个句点,该句点后不能紧跟非句点字符,例如".", “…x”,".x."
要清除特定语言的映射(从而禁止自动生成该语言的标记),请指定一个空的扩展名列表(例如 --langmap = fortran:)。
要恢复所有特定语言的默认语言映射,请为映射提供关键字“默认”。要指定恢复所有语言的默认语言映射,请指定 --langmap = default。

请注意,在推断文件的语言时,文件扩展名会先于文件名模式进行测试。

-h +.inl
告知ctags,把以inl为扩展名的文件看作是头文件的一种(inl文件中放的是inline函数的定义,本来就是为了被include的)。这样ctags在扫描inl文件时,就算里面有static的全局变量,ctags在记录时也不会标明说该变量是局限于本文件的(见第一节描述)。
在这里插入图片描述

指定以句点分隔的文件扩展名列表,这些文件扩展名将被解释为include文件或header文件。要表示没有扩展名的文件,请使用不带非句点字符的句点(例如".", “…x”, “.x.”)。

此选项仅影响特定类型标记的作用域的解释方式(即,是否将它们视为全局可见或仅在定义它们的文件中可见);它不会将扩展名映射到任何特定语言。位于非包含文件中并且无法从另一个文件中看到(例如链接到)的任何标签都被认为具有文件限制范围。包含文件中出现的任何标记都不会被视为具有文件限制范围。

如果列表中的第一个字符是加号,则列表中的扩展名将附加到当前列表中;否则,列表中的扩展名将附加到当前列表中。否则,该列表将替换当前列表。
另请参见–file-scope选项。默认列表是".h.H.hh.hpp.hxx.h++.inc.def"。要恢复默认列表,请指定-h default。
请注意,如果此选项提供的扩展名尚未映射到特定语言,则还需要使用–langmap或–language-force选项。

–c+±kinds=+px
记录类型为函数声明和前向声明的语法元素。
在这里插入图片描述

指定将哪些类型的tag包含在输出文件中(不同语言有不同类型的tag)
其中不区分大小写,并且是内置语言名称之一(请参阅–list- 语言选项以获取完整列表)。
参数kinds是一组单字母标志,用于指定要包括在输出中或从输出中排除的各种标记(特定于语言)。可以使用–list-kinds选项列出每种语言可识别的特定标志集,其含义和默认值。
有off提示的tag类型默认为不输出,否则默认是输出
在这里插入图片描述

因此–c+±kinds=+px为添加显示p和x两种类型tag,即函数声明和外部声明、前向变量声明的语法元素

字母或字母组前置’+‘来添加到默认集合中,也可以前置’-'将其从默认集合中删除。如下例
in order to add prototypes and external variable declarations to the default set of tag kinds, but exclude macros, use --c-kinds=+px-d;

在没有任何前置的’+‘或’-'符号的情况下,只有明确列出的种类才会包含在输出中,即清空默认集合。 如下例
to include only tags for functions,use --c-kinds=f.

–fields=+aiKSz
控制每行tag entries的字段内容
在这里插入图片描述

指定将包含在tag文件包含在每一行中的字段(有关更多信息,请参见下面的TAGFILE FORMAT)。 参数flags是一组单字母标志,每个标志代表要包括的一种扩展字段(除非另有说明,否则默认禁用)

前置加减号的语法跟前面几个参数一样

–extra=+q
让ctags额外记录一些tag entries
在这里插入图片描述

指定是否为某些类型的信息添加额外的tag entries。 参数标记是一组单字母标记,每个标记代表一种要包括在标记文件中的额外标记条目。还是使用前置 +、- 符号。
每个标志的含义如下:
在这里插入图片描述

f 包括每个源文件(例如“ example.c”)的基本文件名的条目,该条目指向文件的第一行。
ctags除了记录上述的各种内容之外,还可以在tags文件中记录本次扫描的各个文件,一个文件名对应一个tag entry。默认是不记录的,要强制记录可以使用如下命令:
ctags –R --extra=+f

q 为属于类的每个标签包括一个额外的类限定tag entry(对于提取该信息的语言;当前为C ++,Eiffel和Java)。 合格标签的实际形式取决于标签所源自的语言(使用对于语言中指定合格呼叫的方式最自然的形式)。 对于C ++,它的形式为“ class :: member”; 对于Eiffel和Java,格式为“ class.member”。 当在tag文件中多次出现标签名称时,这可以允许更容易地定位特定标签。 但是请注意,这可能使得标记文件变为两倍以上。

如果某个语法元素是类的一个成员,当然ctags默认会给其记录一个tag entry(说白了就是在tags文件里写一行),可以要求ctags对同一个语法元素再记一行。举一个例子来说明:假设语法元素是一个成员函数,ctags默认记录的tag entry中的tag的名字就是该函数的名字(不包括类名作为前缀),而我们强制要求ctags多记的那个tag entry的tag的名字是包含了类名作为前缀的函数的全路径名。这样做有什么好处见下文分析。强制ctags给类的成员函数多记一行的命令为:
ctags -R --extra=+q

估计vi是这样使用tags文件的:我们使用vi来定位某个tag时,vi根据我们输入的tag的名字在tags文件中一行行查找,判断每一行tag entry的tag名字(即每行的开头)是否和用户给出的相同,如果相同就认为找到一条记录,最后vi显示所有找到的记录,或者根据这些记录直接跳转到对应文件的特定位置。
考虑到ctags记录的内容和方式,出现同名的tag entry是很常见的现象,例如函数声明和函数定义的tag名字是一样的,重载函数的tag名字是一样的等等。vi只是使用tag名字来搜索,还没智能到可以根据函数的signature来选择相应的tag entry。vi只能简单的显示tag entry的内容给user,让user自行选择。
ctags在记录成员函数时默认是把函数的名字(仅仅是函数的名字,不带任何类名和namespace作为前缀)作为tag的名字的,这样就导致很多不同类但同名的函数所对应的tag entry的名字都是一样的,这样user在vi中使用函数名来定位时就会出现暴多选择,挑选起来十分麻烦。user可能会想在vi中用函数的全路径名来进行定位,但这样做会失败,因为tags文件中没有对应名字的tag entry。要满足用户的这种心思,就要求ctags在记录时针对类的成员多记录一条tag entry,该tag entry和已有的tag entry的内容都相同,除了tag的名字不同,该tag entry的名字是类的成员的全路径名(包括了命名空间和类名)。这就解释了ctags的–extra=+q这样一条命令行选项

–exclude=lex.yy.cc --exclude=copy_lex.yy.cc
告知ctags不要扫描名字是这样的文件。还可以控制ctags不要扫描指定目录,这里就不细说了。

TAG文件格式
在这里插入图片描述

当不在etags模式下运行时,标记文件中的每个条目都由单独的一行组成,在最一般的情况下,每个条目都看起来像这样:
tag_namefile_nameex_cmd;"extension_fields
这些行的字段和分隔符指定如下:
1.标签名称
2.单个制表符
3.与标签关联的对象所在的文件的名称
4.单个制表符

  1. EX命令用于在文件中定位标签,通常是搜索模式(/ pattern /或?pattern?)或行号(请参见–excmd)。
    这个要解释一下意义:ctags所记录的内容的一个功能就是要帮助像vi这样的编辑器快速定位到语法元素所在的文件中去。前面已经记录了语法元素所在的文件,这条命令的功能就是一旦在vi中打开语法元素所在的文件,并且执行了该“命令”后,vi的光标就能定位到语法元素在文件中的具体位置。所以该“命令”的内容一般分两种,一种是一个正则表达式的搜索命令,一种是第几行的指向命令。默认让ctags在记录时自行选择命令的种类,选择的依据不详,可以通过命令行参数来强制ctags使用某种命令,这里就不多谈了。

对于本tag entry(简称tag)所对应的语法元素的描述,例如语法元素的类型等。具体内容和语法元素的种类密切相关。显示哪些描述,显示的格式等都是可以在命令行指定的。例如如下命令要求描述信息中要包含:a表示如果语法元素的类的成员的话,要标明其access(即是public的还是private的);i表示如果有继承,标明父类;K表示显示语法元素的类型的全称;S表示如果是函数,标明函数的signature;z表示在显示语法元素的类型是使用kind:type的格式。
ctags -R --fields=+aiKSz

如下例,对数据库的课设生成tags文件
在这里插入图片描述
在这里插入图片描述

生成tags文件如下
在这里插入图片描述

Vi如何使用tag
在这里插入图片描述

vi -t tag 打开vi,并定位光标到tag所在的文件和行
vim、vi中底线命令行模式下,使用: ta tag_name(或:tag tag_name)跳转到一个tag的位置
CTRL+] 命令会取当前光标下的word作为tag的名字并直接跳转。

假设你正看函数"write_block",发现它调用了一个叫"write_line"的函数,这个函数是干什么的呢?你可以把光标置于"write_line"上,按下CTRL+] 即可。如果"write_line"函数又调用了 “write_ char”.你当然又要知道这个函数又是什么功能。同时,置光标于"write_char"上按下CTRL+]。现在你位于函数"write_char"的定义处。
注记:底线命令行模式下,支持使用tab键来自动补全,因此可以:tag write_
在这里插入图片描述

CTRL+T命令会跳到你前一次的tag处(往前跳)。在上例中它会带你到调用了"write_char"的"write_line"函数的地方。
:tag命令可以往后跳,根据tags中的记录来往后跳
前面使用CTRL+T往前跳之后,可以再使用:tag来跳回write_char

要分隔当前窗口并跳转到光标下的tag:
:stag tagname
CTRL+W,再按CTRL+]
如下例子,上半窗口是跳转tag时生成的,光标也在新窗口中移动,可以使用:q来退出并关闭该窗口,回到刚才的界面
在这里插入图片描述

在预览窗口显示标签
:ptag TagName 预览窗口显示TagName标签
Ctrl+W + } 预览窗口显示当前光标下单词的标签
:pclose 关闭预览窗口
光标仍然在原窗口移动
另外,多次使用:ptag只会有一个预览窗口显示内容,而每次使用:stag都会打开一个新的窗口

如果存在多个同名的tag,tag命令会给出一个tag的列表,可以通过键入tag的序号来选择tag;也可以通过tselect来过滤tag,如::tselect tagname
如果要在多个tag间移动,可以使用如下命令:

:tfirst go to first match
:[count]tprevious go to [count] previous match
:[count]tnext go to [count] next match
:tlast go to last match

在这里插入图片描述

EMACS中使用tag的方法会不一样,需要再了解

;