Vim作为Linux下的标准文本编辑器,在日常工作中必不可少。而作为程序员,我们可能要花很多的时间和vim打交道。Vim虽然提供了语法高亮等方便编程的动能,但用惯了Source Insight和kscope等图形化IDE的人,还是不喜欢vim的简单。其实vim并不简单,如果稍稍发掘一下vim的潜在功能,你会发现,它比Kscope毫不逊色,而且和Kscope、Eclipse等IDE相比,它还有自己的先天优势,那就是快!下面我们就尝试用vim+ctags+taglist+cscope来打造Linux文本模式下的Kscope吧!我也是边学边练,如有不对之处,望不吝指正。
本文中使用如下格式表示命令行输入:
$cat /etc/hosts
其中$为命令行提示符。没有命令行提示符的表示命令行输出:
127.0.0.1 localhost.localdomain localhost localhost
使用前加冒号的方式表示vim命令输入:
:help
vim命令输出,会在正文中说明。
另外,对可以缩写的命令,我们会采用如下格式:
:cs[cope] a[dd] cscope.out
“[]”内的字符是缩写时可以省略的,所以上面的命令用缩写给出就是:
:cs a cscope.out
我们用”~”指当前登录目录,如果你切换到root用户,请注意使用相关用户的$HOME或绝对路径代替此处的"~”。
ctags是一种能为多种语言源程序的函数,变量,类,宏定义等生成标签文件的应用程序。有了这些标签文件,文本编辑器或其他应用程序就可以简单快速的找到相关标签的定义,从而有效的提高编程效率。就像给每家住户贴上门牌,邮递员就可以很方便的找到投递地点一样。
标签(tag),有时也称为索引,但为了描述方便和避免混淆(在cscope中我们用索引),我们这里统一称tag为标签。
Vim作为Linux的标准编辑器,大都提供了对ctags的支持,要查看你使用的vim是否支持ctags,可以尝试获得ctags的帮助:
:help ctags
如果支持ctags,vim会给出有关ctags的帮助,否则vim会提示出错,很不幸,那你就要自己安装ctags了。
下面就简单说一下vim中ctags的使用。
使用ctags,首先要生成标签文件:
$ctags -R
使用-R选项递归的为当前目录生成标签文件,ctags生成的标签文件默认为tags,这也是vim启动时会默认搜索并加载的标签文件。当然,我们也可以使用-f选项指定自己的标签文件:
$ctags -f ~/myproject/mytags -L myctags.files
但是如果指定的标签文件名不再是tags,vim启动时标签文件将不会自动加载。
这时我们可以使用如下方式来加载标签文件:
:set tags=~/myproject/mytags
或者在~/.vimrc中添加一行:
set tags=~/myproject/mytags
如果有多个标签文件,我们也可以一次加载多个,标签文件之间用逗号隔开:
:set tags=~/myproject/mytags, ./mylocaltags
或者追加加载标签文件:
:set tags+=./mylocaltags
还可以让vim去自动搜索并加载标签文件:
:set tags=./tags, ./../tags, ./*/tags
这时vim会查找当前文件所在目录及其父目录和所有子目录下的标签文件,如果想让vim查找整个工程目录树下的所有标签文件,可以使用如下方式:
:set tags=~/myproject/**/tags
当然,为了避免每次启动vim重复输入这些命令,我们也可以把它们放在~/.vimrc里,但注意不能使用相对路径。
你可能已经注意到,在上例中我们使用了ctags的-L选项,此选项允许我们指定需要生成标签的文件列表。有时可能并不是项目里所有的文件都需要生成标签,这样我们只需把要生成标签的文件放在一个文件列表里:
$find ~/myproject -name “*.h” -o -name “*.c” -o -name “*.cpp” > mytags.files
然后使用-L指定该文件列表,ctags就会只为列表里的那些文件生成标签了。
注意,我们在这里使用了绝对路径。为什么要使用绝对路径呢?因为当我们由于工作需要,在不同的目录下来回切换时,在原先路径下有效的相对目录,在当前目录可能就无效了。这样vim可能就找不到需要的标签,因为标签文件里保存的相对路径此时失效了,而绝对路径可以保证在任何目录下都不会失效。
知道了如何生成和加载标签文件之后,接下来我们就来看看怎样使用vim的tag功能吧。
启动vim时自动跳转到指定标签:
$vim -t
vim启动时会自动搜索标签文件,找到定义指定标签的文件,打开它,并将光标定位到指定标签所在的那一行。
跳转到指定标签:
:ta
:tag
跳转到指定标签定义的地方,如果有多个相同的标签,vim会给出一个列表,从中可以选择要跳转到哪里。
在分割窗口中查看标签:
:stag
Vim已经映射了几个用于标签之间跳转的快捷键,我们可以使用这些快捷键在标签之间来回跳转。
Ctrl-] 跳转到当前光标所在的标签;
Ctrl-O 返回到跳转前的位置;
Ctrl-T 沿着经过的标签列表向回跳转,也可以在其前面输入一个数字,如N-Ctrl-T会向回跳转N步,相当于按了N下Ctrl-T;
:tags 显示所有经过的标签列表;
当我们不想输入完整的标签时,可以只输入要找标签的前几个字符,然后按
:tag prefix_
默认会得到第一个匹配,如果它不是你想要的,可以重复按
ctags支持模式匹配查找,当我们无法记清楚要找的标签,或只知道标签的一部分时,就可以使用模式匹配来搜索。
:tag /pattern
匹配指定模式,按
我们可以使用有关查找模式匹配的所有特性,如只查找以foo开始的标签:
:tag /^foo
:tselect /^foo
或只查找以bar结尾的标签:
:tag /bar$
:tselect /bar$
你同样可以用
有时我们可能不想跳转到指定标签,只是想查看或确认一下相关标签的定义,然后就返回。用Ctrl-]跳转然后再用Ctrl-O或Ctrl-T返回当然可以,但vim提供了一种更简单的方式,那就是预览窗口。预览窗口就是把当前编辑窗口分割出一小部分,用来显示预览,活动光标依然会停留在编辑窗口,不会影响编辑。
在预览窗口里显示标签定义:
:ptag
对于ptag,前面所讲的模式匹配同样适用,可以使用:
:pclose
关闭预览窗口,而无论当前活动窗口是哪一个。
在预览窗口中编辑指定文件:
:pedit
查找当前文件和任何包含文件中的单词并在预览窗口中显示匹配:
:psearch
这在使用没有标签文件的库函数时十分有用,它可以找到库函数的声明,并在预览窗口显示,这样我们就可以查看传给函数的参数是否正确。psearch会根据vim中指定的path选项搜索包含的头文件,path选项的默认值为:
.,/usr/include,,
即psearch默认搜索当前目录和/usr/include目录。如果你的头文件放在其他地方,可以用:
:set path=~/myproject/include,./subdir/include
或
:set path+=~/myproject/include,./subdir/include
指定搜索路径。前一种方式会覆盖之前的path,后一种方式是在之前path上追加。当然,为了避免每次启动vim重复输入这些命令,我们可以把它们放在~/.vimrc里,但注意不能使用相对路径。
如果你不知道当前path是什么,可以用如下命令查看:
:set path ?
我们也可以用此等命令查看vim其它选项的当前值,其格式为:
:set option ?
其中option为要查看的选项。例如,如果我们想知道当前tags是什么,可以输入:
:set tags ?
Vim会给出形如:
tags=./tags,./TAGS,tags,TAGS
的信息。这样我们就知道vim默认会在当前目录搜索并使用tags或TAGS作为标签文件。
要想更详细的了解ctags,请参考在线帮助:
:help ctags
:help tags
PS: 可以用Ctrl-W加方向键或Ctrl-W-W在vim不同窗口之间切换。
虽然使用ctags我们可以在各个标签之间搜索和跳转,但是我们并不能看到这些标签,他们都隐藏在后台。就像一瓶化学药品,只有当我们知道它含有什么成分时才知道能从中提取出什么,对于我们不知道的成分,我们无从知道,哪怕其他人知道它还有很多其它成分。但是如果我们把所有成分的列表贴在瓶子上,其他人就能一目了然的知道瓶子到底装了什么东西。taglist就是就是把隐藏的标签贴出来。
Taglist是基于ctags的vim小插件,所以它只能在已经支持ctags的vim上使用。Taglist插件可以分组显示ctags生成的标签,使标签的查看更加方便和快捷。Taglist会从vim分割出一个窗口用来显示标签,在标签窗口中的标签上按<Enter>或鼠标单击/双击,该标签就会在另一窗口中显示,非常方便。
首先到Vim Taglist Plugin下载taglist插件,当前最新版本是taglist_45.zip。解压后会得到两个文件:
./plugin/taglist.vim
./doc/taglist.txt
把这两个文件放到相应目录下:
$cp ./plugin/taglist.vim ~/.vim/plugin/
$cp ./doc/taglist.txt /usr/share/vim/vim71/doc/
我的vim版本是7.1,所以这里是vim71。重启vim,用:Tlist或:TlistToggle就可以打开关闭标签列表了。
当然,我们还可以按如下方式安装Taglist:
把下载的zip文件解压到~/.vim,然后到~/.vim/doc目录下启动vim,用:helptags安装Taglist帮助就可以了。
另外,taglist需要ctags的支持,vim要支持system(),并且filetype选项是打开的。
如果ctags是自己安装的,Taglist可能找不到ctags程序。例如,我们把ctags安装在/usr/local/bin目录下,如果Taglist插件找不到ctags,可以采用如下方式:
1. 把/usr/local/bin加到搜索路径PATH里。在~/.bashrc里加一行:
PATH=$PATH:/usr/local/bin
2. 指定Tlist_Ctags_Cm。修改~/.vim/plugin/taglist.vim,在if !exists('loaded_taglist')前设置Tlist_Ctags_Cmd:
set cpo&vim
let Tlist_Ctags_Cmd=”/usr/local/bin/ctags”
if !exists('loaded_taglist')
可用如下命令检查vim是否支持system():
:echo exists('*system')
如果支持,vim应该显示1,否则你可能就要重新编译安装vim了。
如要查看vim的filetype是否打开,可用:
:filetype
如果已经打开vim会显示”filetype on",否则需要在~/.vimrc中加上”filetype on"来打开filetype支持。
我们还可以为taglist设置一个快捷键,这样就可以用快捷键打开和关闭taglist了。例如我们要设置F8为taglist的快捷键,在~/.vimrc中加入如下一行就可以了:
nnoremap
:TlistToggle
要想更详细的了解taglist,请参考在线帮助:
:help taglist
虽然我们已经知道了瓶里药品的成分,但怎么才能知道有多少成分的分子,各成分含有哪些元素呢?就像在一个项目里,我们怎么知道那些文件使用了某个关键字,一个函数都被那些函数调用了呢?cscope,它能把整个项目的代码连成一个整体,让你可以在代码的世界里面自由驰骋……
cscope是一个基于索引的代码查看工具,它可以帮助你更详细的搜索和查看代码。有了cscope的支持,vim不仅可以查看函数,变量,宏的定义,还可以更详细的搜索函数调用,文件包含,变量引用,甚至对一个符号进行全局搜索!有了cscope,vim简直就是文本模式下的Kscope!
目前很多Linux发行版的vim都提供了cscope的支持。我们可以使用如下方式查看自己的vim是否支持cscope:
$vim –version | grep cscope
+cryptv +cscope +cursorshape +dialog_con +diff +digraphs -dnd -ebcdi
如果有+cscope,说明你的vim已经支持cscope,否则你可能需要打开cscope支持,重新编译安装vim了。
使用cscope,我们首先要生成索引文件:
$cscope -Rbkq
-R 递归搜索当前目录
-b 只生成索引文件,不进入cscope界面
-k 在生成索引文件时,不搜索/usr/include目录
-q 生成cscope.in.out和cscope.po.out文件,加快cscope的索引速度
你也可以不用-b选项,这样cscope会打开一个界面显示索引状态,索引完成后可以用Ctrl-D退出cscope界面。
cscope生成索引文件时默认只会搜索有限类型的文件(.c, .h, .l, .y),如果要搜索其他类型的源文件如c++,就要先产生一个搜索文件列表,然后再对列表里的文件生成索引:
$find ~/project/ -name “*.c” -o -name “*.h” -o -name “*.cpp” > cscope.files
$cscope -bkq -f ~/project/cscope.out -i cscope.files
-f选项告诉cscope要生成的指定的索引文件,而不是默认的cscope.out,-i选项是让cscope从文件中读入要生成索引文件的列表。
如上面介绍ctags时所说,如果要对一个项目使用cscope,就需要使用绝对路径建立索引,否则将无法根据索引正常跳转。
要把cscope生成的索引文件链接进vim之后,才能正常使用cscope的功能:
:cs[cope] a[dd] cscope.out
可以分多次add单个索引文件,也可以一次add多个索引文件:
:cs a cscope.out ./others/othercscope.out
注意,这里使用空格区分不同的索引文件,不像vim中的:set那样使用逗号区分。
查看已链接的索引文件:
:cs[cope] s[how]
删除链接的索引文件:
:cs[cope] k[ill]
重新初始化所有的链接:
:cs[cope] r[eset]
vim会重新搜索并链接索引文件。
下面我们看看怎样用cscope进行搜索:
:cs[cope] f[ind]
type是搜索类型,cscope支持以下类型的搜索:
c: 查找有那些函数调用了指定的函数
d: 查找指定函数调用了哪些函数
e: 查找指定的正则表达式,此时不宜用键盘映射
f: 查找指定的文件
g: 查找指定标示符的定义位置,此时用tag会好一点
I: 查找指定文件被哪些文件包含
s: 查找指定标示符的使用位置
t: 查找指定的文本字符串,会全局搜索所有包含此字符串的匹配,所以会比较慢
每次查找都要输入庸长难记的cscope命令有时是一件很头痛的事情,那么有没有一种简单有效的方法提供搜索功能呢?有,那就是cscope键盘映射插件cscope_maps。它把cscope查找命令映射成vim快捷键,这样你就不用每次都要输入繁琐的cscope命令了。
cscope_maps有三种快捷键映射模式,可以分别在不同方式下打开搜索窗口:
Ctrl-/-cmd 相当于 :cs f
普通模式,在当前窗口打开指定索引; Ctrl-Space-cmd 相当于 :scs f
它会在水平分割窗口内打开指定索引; Ctrl-Space-Space-cmd 相当于 :vert scs f
它会在垂直分割窗口内打开指定索引;
在启动vim时,你可能会碰到如下错误:
Error detected while processing /home/xingang/.vim/plugin/cscope_maps.vim:
line 42:
E568: duplicate cscope database not added
Press ENTER or type command to continue
这是由于重复装载cscope索引文件导致的,其实索引文件已经装载,所以可以不必对这个错误过分担心。但如果你觉得不爽,你也可以把~/.vim/plugin/cscope_maps.vim中链接索引文件的命令注释掉:
" add any cscope database in current directory
"if filereadable("cscope.out")
" cs add cscope.out
" else add the database pointed to by environment variable
"elseif $CSCOPE_DB != ""
" cs add $CSCOPE_DB
"endif
然后启动vim时,自己链接索引文件。
另外一种方法是设置环境变量CSCOPE_DB:
CSCOPE_DB=/project/cscope.out
export CSCOPE_DB
vim启动时就会自动搜索并链接$CSCOPE_DB指定的索引文件,所以我们只要把上面两行放到~/.bashrc里就可以了,注意要使用绝对路径。
要想更详细的了解cscope,请参考在线帮助:
:help cscope
十年磨一剑,在Unix/Linux多年的发展史中,程序员是没有图形化IDE的,那他们是怎么协调代码之间的关系呢?什么东西都会有一个从无到有的过程,他们为了能使自己的工作更加高效,就自己动手开发出了这些有用的小程序。所以我辈们才有幸拿着先辈的利剑,披荆斩棘……
Ctags Manual
Taglist Manual
The Vim/Cscope tutorial
ctags和vim
使用vim + cscope/ctags实现Source Insight的功能
声明:
仅用作学习和交流,转载请注明作者和出处,谢谢!