http://forum.ubuntu.org.cn/viewtopic.php?f=68&t=343460
貌似vim众很少用gnu global啊,都只见着emacs众在用,我觉得有必要在vim众中推广一下。
gnu global是一个类似cscope的工具,也能提供源文件之间的交叉索引。
其独到之处在于,当你生成索引文件以后,再修改整个项目里的一个文件,然后增量索引的过程非常快。
安装过程就不说了,根据各自发行版不同,自个儿装吧。
安装好以后,有global、gtags、gtags-cscope三个命令。global是查询,gtags是生成索引文件,gtags-cscope是与cscope一样的界面。
example:
代码:
$ cd project/
$ gtags
这样就生成了整个目录的索引文件,包括GTAGS、GRTAGS、GPATH三个文件。
你也可以先用find命令生成一个文件列表,叫gtags.files,然后再执行gtags,就会只索引gtags.files里的文件。
example:
代码:
$ cd project/
$ find . -name "*.[ch]" > gtags.files
$ gtags
查询使用的命令是global和gtags-cscope。前者是命令行界面,后者是与cscope兼容的ncurses界面。这里就不多介绍了,重点是如何在vim里查询:
首先进入vim,然后:
example
代码:
:set cscopeprg=gtags-cscope
:cs add GTAGS
然后就可以像cscope一样,用cs find g等命令进行查询了。
当我们更改了某个文件以后,比如project/subdir1/subdir2/file1.c,想更新索引文件(索引文件是project/GTAGS),只消这样:
example:
代码:
$ cd project/subdir1/subdir2/
$ vim file1.c
$ global -u
global -u这个命令会自动向上找到project/GTAGS,并更新其内容。而gtags相对于cscope的优势就在这里:增量更新单个文件的速度极快,几乎是瞬间完成。有了这个优势,我们就可以增加一个autocmd,每次:w的时候自动更新索引文件。
我的设置如下:
代码:
" settings of cscope.
" I use GNU global instead cscope because global is faster.
set cscopetag
set cscopeprg=gtags-cscope
set cscopequickfix=c-,d-,e-,f-,g0,i-,s-,t-
nmap <silent> <leader>j <ESC>:cstag <c-r><c-w><CR>
nmap <silent> <leader>g <ESC>:lcs f c <c-r><c-w><cr>:lw<cr>
nmap <silent> <leader>s <ESC>:lcs f s <c-r><c-w><cr>:lw<cr>
command! -nargs=+ -complete=dir FindFiles :call FindFiles(<f-args>)
au VimEnter * call VimEnterCallback()
au BufAdd *.[ch] call FindGtags(expand('<afile>'))
au BufWritePost *.[ch] call UpdateGtags(expand('<afile>'))
function! FindFiles(pat, ...)
let path = ''
for str in a:000
let path .= str . ','
endfor
if path == ''
let path = &path
endif
echo 'finding...'
redraw
call append(line('$'), split(globpath(path, a:pat), '\n'))
echo 'finding...done!'
redraw
endfunc
function! VimEnterCallback()
for f in argv()
if fnamemodify(f, ':e') != 'c' && fnamemodify(f, ':e') != 'h'
continue
endif
call FindGtags(f)
endfor
endfunc
function! FindGtags(f)
let dir = fnamemodify(a:f, ':p:h')
while 1
let tmp = dir . '/GTAGS'
if filereadable(tmp)
exe 'cs add ' . tmp . ' ' . dir
break
elseif dir == '/'
break
endif
let dir = fnamemodify(dir, ":h")
endwhile
endfunc
function! UpdateGtags(f)
let dir = fnamemodify(a:f, ':p:h')
exe 'silent !cd ' . dir . ' && global -u &> /dev/null &'
endfunction
解释几句:
1. 我增加了一个命令叫FindFiles,是用来生成gtags.files文件的,用法如下:
代码:
:FindFiles {pattern} subdir1 subdir2 ...
然后找到的文件就会都添加到当前buffer的最后。
比如,我们用git刚下下来一整套kernel的源码,放在linux-2.6目录下,然后我想生成列表文件,就可以这样:
代码:
:e gtags.files
:FindFiles **/*.[ch] arch/x86 arch/arm block crypto ...
:w
只增加自己想要的目录,而不要的就不增加。
2. 我添加了三条autocmd,其中:
代码:
au VimEnter * call VimEnterCallback()
au BufAdd *.[ch] call FindGtags(expand('<afile>'))
这两条命令会在你打开一个c文件的时候,自动向上查找GTAGS文件,找到以后就会执行:cs add命令添加这个GTAGS文件。
代码:
au BufWritePost *.[ch] call UpdateGtags(expand('<afile>'))
这条命令会在你修改一个c文件并:w以后,自动进入c文件所在目录并执行"global -u"更新索引文件。
最后,还有一个问题,cscope有一个-f参数,这个参数可以指定cscope.out文件的路径。而gtags-cscope的哲学不一样,它是自己一路向上寻找GTAGS文件,所以没有-f参数。而vim调用:cs add的时候,是会使用-f参数的。这样,当:cs add GTAGS文件的时候,就不能指定当前目录的子目录以外的路径。这也导致:cs add命令只能使用一个GTAGS文件。
针对这个问题,我写了一个vim的patch http://forum.ubuntu.org.cn/viewtopic.php?f=68&t=342099,在fork出gtags-cscope子进程以后会把子进程chdir()到GTAGS文件所在的目录,这样就OK了。
diff -r f33296ed67ea src/if_cscope.c
--- a/src/if_cscope.c Wed Aug 17 20:33:23 2011 +0200
+++ b/src/if_cscope.c Thu Aug 18 10:13:26 2011 +0800
@@ -960,7 +960,27 @@
/* run the cscope command; is there execl for non-unix systems? */
#if defined(UNIX)
- (void)sprintf(cmd, "exec %s -dl -f %s", prog, csinfo[i].fname);
+#include <unistd.h>
+#include <libgen.h>
+
+ if (strcmp(prog, "gtags-cscope") == 0)
+ {
+ char *fname = NULL, *dname = NULL;
+
+ fname = strdup(csinfo[i].fname);
+ if (fname)
+ {
+ dname = dirname(fname);
+ (void)chdir(dname);
+ free(fname);
+ }
+
+ (void)sprintf(cmd, "exec %s -dl", prog);
+ }
+ else
+ {
+ (void)sprintf(cmd, "exec %s -dl -f %s", prog, csinfo[i].fname);
+ }
#else
/* WIN32 */
(void)sprintf(cmd, "%s -dl -f %s", prog, csinfo[i].fname);