本节所用命令的帮助入口:
:help 'tags' :help :tag :help :tags :help CTRL-] :help CTRL-T :help vimgrep :help cw :help pattern
尽管相关的文章已经很多了,但tag文件实在是太有用了,所以还是啰嗦一次。
Tag文件(标签文件)无疑是开发人员的利器之一,有了tag文件的协助,你可以在vim查看函数调用关系,类、结构、宏等的定义,可以在任意标签中跳转、返回……相信使用过Source Insight的人对这些功能并不陌生,而在vim中,此功能的实现依赖于tag文件。
对于程序来说,Tag文件中保存了诸如函数、类、结构、宏等的名字,它们所处的文件,以及如何通过Ex命令跳转到这些标签。它是一个纯文本文件,因此你可以手工的编辑它,也可以使用脚本对其进行操作。
通常我们使用名为ctags的程序来生成这样的tag文件。vim能直接使用ctags程序所生成的tag文件。在UNIX系统下的ctags功能比较少,所以一般我们使用Exuberant Ctags(在大多数Linux系统上,它是缺省的ctags程序),它能够支持多达33种程序语言,足以满足我们开发的需要了。这里是它的中文手册。如果你的系统上未安装此程序,请到http://ctags.sourceforge.net下载。
emacs则使用etags来生成tag文件,如果希望vim也能支持etags的tag文件格式,需要编译vim时加入”+emacs_tags“选项。在这篇文章介绍了编译vim的方法。
Tag文件需要遵循一定的格式,由Exuberant Ctags生成的tag文件,缺省是如下格式:
{tagname} {TAB} {tagfile} {TAB} {tagaddress} {term} {field} ..
- {tagname} – 标识符名字,例如函数名、类名、结构名、宏等。不能包含制表符。
- {tagfile} – 包含 {tagname} 的文件。它不能包含制表符。
- {tagaddress} – 可以定位到 {tagname}光标位置的 Ex 命令。通常只包含行号或搜索命令。出于安全的考虑,vim会限制其中某些命令的执行。
- {term} – 设为 ;” ,这是为了兼容Vi编辑器,使Vi忽略后面的{field}字段。
- {field} .. – 此字段可选,通常用于表示此{tagname}的类型是函数、类、宏或是其它。
在{tagname}、{tagfile}和{tagaddress}之间,采用制表符(TAB符,即C语言中的”\t”)分隔,也就是说{tagname}、{tagfile}的内容中不能包含制表符。
Tag文件的开头可以包含以”!_TAG_”开头的行,用来在tag文件中加入其它信息。vim能够识别两种这样的标记,经常用到的是”_TAG_FILE_SORTED”标记,例如:
!_TAG_FILE_SORTED<Tab>1<Tab>{anything}
上面这个标记说明tag文件是经过排序的,并且排序时区分了大小写,对排序的tag,vim会使用二分法来进行查找,大大加快了查找速度;如果值为0,则表示tag文件未经排序;如果值为2,则表示tag文件是忽略大小写排序的。
之所以在这里介绍tag文件的格式,是因为我们在后面提到的lookupfile插件中,会自己生成tag文件。
虽然ctags有为数众多的选项,但通常我们所使用的非常简单。还是以vim 7.0的代码为例,我们执行:
cd ~/src/vim70 ctags –R src
上面这条命令会在~/src/vim70/目录下生成一个名为tags的文件,这个文件中包含~/src/vim70/src/目录下所有.c、.h文件中的标签。它一个文本文件,你可以用vim打开它看一下。此文件缺省按区分字母大小写排序,所以直接可以被vim使用。
现在我们进入vim,执行下面的命令:
:cd ~/src/vim70 "切换当前目录为~/src/vim70 :set tags=tags "设置tags选项为当前目录下的tags文件
现在,我们设置好了tags选项,接下来我们使用它:
:tag VimMain
你会看到vim打开了src/main.c文件,并把光标定位到第167行VimMain上。
我们知道,一般主程序的函数名为main,如果你尝试下面的命令:
:tag main # pri kind tag file 1 F f main src/xxd/xxd.c main(argc, argv) 2 FS d main src/if_python.c 46 Choice number (<Enter> cancels):
这里并没有src/main.c文件,怎么回事呢?这是因为ctags并不是编译器,它在处理编译预处理指令受到局限,因此并没有生成src/main.c中main()函数的标签。你可以在生成tag文件时给ctags指定参数来解决这个问题。见ctags手册。
或者你可以用”:grep“或”:vimgrep“来查找main(这篇文章讲解grep及vimgrep的用法):
:cd ~/src/vim70 :vimgrep /\<main\>/ src/*.c :cw
这时下面的quickfix窗口将显示出来,在quickfix窗口中找到我们想跳转的位置(本例中是src/main.c),按回车,就可以跳到对应的位置了。
如果你只记得部分的标签名,那么可以使用”tag“命令的搜索模式,你可以输入一个vim正则表达式来表示你所查找的名字,如:
:tag /\C\<\k\+ain\> # pri kind tag file 1 F f VimMain src/main.c VimMain 2 F d bindtextdomain src/vim.h 483 3 F d bindtextdomain src/vim.h 502 4 F d bindtextdomain src/vim.h 504 5 F f main src/xxd/xxd.c main(argc, argv) 6 F d textdomain src/vim.h 488 7 F d textdomain src/vim.h 510 8 F d textdomain src/vim.h 512 9 FS d bindtextdomain src/gui_gtk.c 54 10 FS d bindtextdomain src/gui_gtk_x11.c 37 11 FS f cmdsrv_main src/main.c cmdsrv_main(argc, argv, serverName_arg, serverStr) 12 FS d main src/if_python.c 46 13 FS d textdomain src/gui_gtk.c 51 14 FS d textdomain src/gui_gtk_x11.c 34 Choice number (<Enter> cancels):
这表示我想查找一个以一个或多个keyword开始的标签,此标签以ain做为结尾,在查找时区分大小写。要读懂这个正则表达式,请”:help pattern“。
vim会保存一个跳转的标签栈,以允许你在跳转到一个标签后,再跳回来,可以使用”:tags“命令查找你处于标签栈的哪个位置。
我们经常用到的tag跳转命令见下(一般只需要知道CTRL-]和CTRL-T就可以了):
:tag {ident} "跳转到指定的标签
:tags "显示标签栈
CTRL-] "跳转到当前光标下的标签
CTRL-T "跳到标签栈中较早的标签
如果想了解更多命令,可以”:help 29.1“(强烈建议程序员完整的阅读usr_29.txt和usr_30.txt)。
如果想更深入了解tag命令和相关知识,可以”:help tagsrch“。
我之前写的一篇关于ctags和cscope的文章,参见:Vim + Cscope/Ctags
[参考文档]
- vim手册
- vim中文手册
原创文章,请阅读页脚的许可方式,转载请注明:转载自易水博客 [ http://easwy.com/blog/ ]
本文链接地址: http://easwy.com/blog/archives/advanced-vim-skills-use-ctags-tag-file/
文章的脚注信息由WordPress的wp-posturl插件自动生成
文本处理

12条评论 添加评论
(以下为CSDN评论的转帖)
#zvane 发表于2007-03-10 22:41:52 IP:
写的很不错,小弟正在学习用vim写程序方面的东西.十分感谢作者的这几篇文章.收益真是太大.
#flyincosmic 发表于2007-05-03 00:11:42 IP: 220.192.42.*
如何获得多匹配结果显示的窗口,如果找到2个以上的结果,只会在底部有一行1/n……这样的通知,甚至都没办法移动到下一个匹配的结果,
vimgrep也是如此
#easwy 发表于2007-05-04 12:44:10 IP: 219.133.160.*
你的VIM是不是工作在兼容模式下?执行一下:set nocompatible,然后再试
移动到下一个tag,使用命令:tn,移动到上一个,使用命令:tp
如果想列出vimgrep找到的所有行,使用:cw命令,你可以看一下quickfix那篇文章。
#flyincosmic 发表于2007-05-07 00:00:10 IP: 220.192.41.*
一直是nocp的
#flyincosmic 发表于2007-05-09 00:06:40 IP: 220.192.40.*
lz是否弄错了,ctags并不支持直接recurse目录,还是得使用文件名或者通配符啊
我试了–language-force=c++
和–langmap=c++,奇怪的是后者却不行
而list language和map的时候都有c++的
#easwy 发表于2007-05-09 09:14:38 IP: 213.70.90.*
ctags的用法可以参考我翻译的ctags手册:
http://easwy.com/blog/20090211/111.html
(以下为CSDN评论的转帖)
#wen727 发表于2008-05-29 14:17:06 IP: 119.0.34.*
因为自动补全要先设置好编程语言才能补全,但是我想在HTML中嵌套PHP语言,这样就不能完成PHP自动补全功能。请问应该如何设置,能实现在编辑HTML中实现PHP代码自动补全功能谢谢!
2008-06-10 13:16:39作者回复
你可以把文件类型设成PHP试一下。就是定义两个快捷键,在编辑HTML时把filetype设为HTML,编辑PHP时把文件类型设为PHP (未经测试)
#ajee 发表于2008-06-14 01:14:25 IP: 116.227.119.*
确实是只出现1/n,无法显示所有的,怎么回事
2008-06-16 09:53:48作者回复
我没有遇到过这个问题,你用:version命令看一下vim的版本,把这些信息连同你的vimrc一起放上来看看。
#wen727 发表于2008-06-14 23:55:31 IP: 119.0.36.*
谢谢!
#xxx 发表于2008-09-06 21:44:28 IP: 219.139.246.*
楼主感觉你的文章贴一些啥的命令的介绍太多了 ,看的人真叫一个不耐烦,其实完全没必要求多求全,把常用的整理出来就行了,说句老实话,没人会用到所有功能,看别人总结的ctags就几句,感觉实用多了,如下:
熟练的使用ctags仅需记住下面七条命令:(很简单吧,呵呵)
1. $ ctags –R * ($ 为Linux系统Shell提示符)
2. $ vi –t tag (请把tag替换为您欲查找的变量或函数名)
3. :ts (ts 助记字:tags list, “:”开头的命令为VI中命令行模式命令)
4. :tp (tp 助记字:tags preview)—此命令不常用,可以不用记
5. :tn (tn 助记字:tags next) —此命令不常用,可以不用记
6. Ctrl + ]
7. Ctrl + T
[...] 首先是Taglist,这是一个能让vim具备类似Aptana的Outline View的功能,还能像JAVA的IDE那样通过类名和方法名直接跳转到相应的源码,它借助了一个叫ctags的工具,可以解析源码生成标签文件(在同一个名录下,叫tags),详细的介绍请参考Easwy老爷的文章,记得务必安装最新版的Exuberant Ctags……我是用MacPorts装的:sudo port install ctags [...]
Hi! I like your srticle and I would like very much to read some more information on this issue. Will you post some more?
[...] 使用会话和viminfo vim使用进阶: vimrc初步 vim使用进阶: 保存项目相关配置 vim使用进阶: 使用标签(tag)文件 vim使用进阶: 使用taglist插件 vim使用进阶: 文件浏览和缓冲区浏览 [...]
你好,
我按照你的方法安装了vim72(和vimgdb72)。但是发现:在gdb的command-line里面输入命令如”file test”后,并没有相关的程序加载进来,我看不见任何的程序代码出现在窗口里面,但是输出窗口是有的,里面显示内容为:
Vim |+gdb| level 3 mode
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type “show copying”
and “show warranty” for details.
This GDB was configured as “i486-linux-gnu”.
(gdb) file test
Reading symbols from /usr/bin/test…(no debugging symbols found)…done.
(gdb)
请问,如何调用出相关的程序代码出来?谢谢!
@Randy
Reading symbols from /usr/bin/test…(no debugging symbols found)…done.
这句话显示,你在编译时,没有把调试信息编译进来
在用gcc编译时,加上”-g”选项试一下
@Easwy
不好意思,低级错误了。
我刚刚试了下,发现还是不行,看不见源代码,输出窗口为:
Vim |+gdb| level 3 mode
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type “show copying”
and “show warranty” for details.
This GDB was configured as “i486-linux-gnu”.
(gdb) file t
Reading symbols from /home/randyqiu/桌面/t…done.
(gdb)
我找了下config.h,发现有错误,不知道是不是这个引起的:
conftest.c:9:28: error: ac_nonexistent.h: No such file or directory
configure:3024: $? = 1
configure: failed program was:
| /* confdefs.h. */
| #define PACKAGE_NAME “”
…………………………………………………还有:
/usr/bin/ld: cannot find -lsocket
collect2: ld returned 1 exit status
configure:5887: $? = 1
configure: failed program was:
| /* confdefs.h. */
| #define PACKAGE_NAME “”
| #define PACKAGE_TARNAME “”
| #define PACKAGE_VERSION “”
| #define PACKAGE_STRING “”
| #define PACKAGE_BUGREPORT “”
| #define UNIX 1
| #define STDC_HEADERS 1
| #define HAVE_SYS_WAIT_H 1
| #define HAVE_SELINUX 1
| #define FEAT_NORMAL 1
| #define USE_XSMP_INTERACT 1
| #define FEAT_CSCOPE 1
| /* end confdefs.h. */
| #define PACKAGE_TARNAME “”
| #define PACKAGE_VERSION “”
| #define PACKAGE_STRING “”
| #define PACKAGE_BUGREPORT “”
| #define UNIX 1
| /* end confdefs.h. */
……………………………………………………还有:
/usr/bin/ld: cannot find -lXpm
……………………………………………………还有;
conftest.c:55:21: error: X11/xpm.h: No such file or directory
………………………………………………………………………………还有:
conftest.c:79:28: error: sys/systeminfo.h: No such file or directory
……………………………………………………………………………….还有:
conftest.c:80:24: error: sys/stream.h: No such file or directory
…………………………………………….还有几个我没列出来,,
为什么有这么多的错误还能编译通过?而且我也手动./configure,也没问题,都很顺利的通过了,,
谢谢,麻烦你了!
@Randy
Reading symbols from /home/randyqiu/桌面/t…done.
这句话显示,你已经加载了调试文件,接下来就可以调试了。configure的错误不用管,只要你能编译通过就行了
你还没开始调试,当然看不到源代码窗口了 ^_^
@Easwy
555,总觉得和你帖出来的窗口有点不一样。
1.我首先进入vim;
2.然后空格,调出command-line;
3.输入file test(test是我的可执行文件);
(我觉得你是这个时候就可以看到command-line下面有个窗口来显示代码,然后我好方便移动到具体的代码行去设置断点之类的)
4.如果我接着设置一个断点:b main,然后:r 进行调试的话,还是没有代码在command-line下面的窗口显示出来。
当然,如果我在command-line里面输入list的话,gdb输出窗口是有代码出来的。
不晓得我有没有说明白^-^:
我看到你的教程里面的窗口有如下几个(从上到下):
gdb输出窗口;
command-line;
代码窗口(你的教程里面,其窗口名是sample.c,我的就显示No Name,当然也没有内容);
最后还有一个状态栏。
请老大指教指教!是不是我哪里理解错了,还是什么没添加?
@Randy
明白你的问题了。
在用file命令加载被调试程序时,此时不会自动显示源代码的。我一般是自己打开相应文件来设置断点。
当断点停下来以后,再接着单步跟踪时,vimgdb会根据所执行的代码来打开相应的源文件,把它显示在vi窗口中。
@Easwy
哦,是这样的。。
谢谢啦,耽误你时间了,再次感谢你的vim教程,让我这样的菜鸟可以少走点弯路!