vim+ctags+cscope的配置与使用

yuanheci 2023年10月19日 750次浏览

安装:
sudo apt install ctags
sudo apt install cscope

问题提出
vim编写程序时,不能跳转到系统头文件定义,不能自动完成自己编写结构成员。需要解决。

ctags在使用vim编程和浏览代码是非常有用。可以用CTRL+]CTRL+t来回跳转关键字。 先生成自己工作目录的tags。最简单粗暴用法:

  1. $cd yourwork
  2. $ctags -R *

这样会生成一个tags文件。 不过,这种有个问题,成员变量没有包含在里面。所以自动完成对象的成员时没有提示。 解决办法:

  1. $ctags -R --fields=+iaS --extra=+q *

–fields=[+|-]flags 指定tags的可用扩展域(extension fields),以包含到tags入口。
i: 继承信息Inheritance information
a: 类成员的访问控制信息 Access (or export) of class members
S: 常规签名信息,如原型或参数表 Signature of routine(e.g. prototype or parameter list)
–extra=[+|-]flags 指定是否包含某种扩展信息到tags入口。
q:包含类成员信息(如c++,java,Eiffel)。 但就算是C语言的结构,也需要这两个参数设置才能获取成员信息。

这样就能自动完成结构和类的成员了。

但是,对于系统的函数,还是没有跳转。如socket定义,inetaddr_in这样的结构没有自动变量完成。 最简单做法:

  1. $ctags --fields=+iaS --extra=+q -R -f ~/.vim/systags /usr/include /usr/local/include

然后在.vimrc里设置

  1. set tags+=~/.vim/systags

这样虽然基本能跳转到系统函数定义,一个问题是某些系统函数并没有加入到systags里。 如/usr/incluce/socket.hsocket系列函数,memset等很多关键函数都没有到tag里:

  1. extern int listen (int __fd, int __n) __THROW;

这是因为__THROW的宏定义让ctags不再认为该系列函数是函数。 同理,如memcpy系列函数: 如/usr/include/string.h

  1. extern int strcmp (__const char *__s1, __const char *__s2)
  2. __THROW __attribute_pure__ __nonnull ((1, 2));

还有attribute_purenonull等属性,都需要忽略。如果需要#if 0里面的定义,可以–if0=yes来忽略 #if 0这样的定义。

  1. $ctags -I __THROW -I __attribute_pure__ -I __nonnull -I __attribute__ --file-scope=yes --langmap=c:+.h --languages=c,c++ --links=yes --c-kinds=+p --c++-kinds=+p --fields=+iaS --extra=+q -R -f ~/.vim/systags /usr/include /usr/local/include

这样.vim/systags里面是全的,但内容过多。一个函数定义的跳转,会有几十个候选。这时我们可以简化一下,将-R去掉,自己指定目录:

  1. $ctags -I __THROW -I __attribute_pure__ -I __nonnull -I __attribute__ --file-scope=yes --langmap=c:+.h --languages=c,c++ --links=yes --c-kinds=+p --c++-kinds=+p --fields=+iaS --extra=+q -f ~/.vim/systags /usr/include/* /usr/include/sys/* /usr/include/bits/* /usr/include/netinet/* /usr/include/arpa/* /usr/include/mysql/*

还可以包含一些自己编程需要的路径。注意后面加*号。 这样生成的系统tags就少多了。不会有太多不相干的定义。


shell脚本lib_ctags.sh一键部署:

  1. #!/bin/bash

  2. #
  3. # File: ctags.sh
  4. # Author: rsh
  5. # Date: 2023-10-19
  6. # Desc: ctags 生成系统库关函数的tags, 存放于 ~/.vim/systags 文件中
  7. #

  8. mkdir -p ~/.vim;

  9. ctags -I __THROW -I __attribute_pure__ -I __nonnull -I __attribute__ --file-scope=yes --langmap=c:+.h --languages=c,c++ --links=yes --c-kinds=+p --c++-kinds=+p --fields=+iaS --extra=+q -f ~/.vim/systags /usr/include/* /usr/include/sys/* /usr/include/bits/* /usr/include/netinet/* /usr/include/arpa/*

  10. printf '\n\n"ctags"
  11. "========================="
  12. set tags+=~/.vim/systags
  13. "========================="' >> ~/.vimrc

在工程根目录下运行cur_ctags.sh

  1. #! /bin/bash
  2. #name--create_cscope.sh
  3. find . -name "*.h" -o -name "*.c" -o -name "*.cpp" -o -name "*.cc" -o -name "*.hh" > cscope.file
  4. cscope -Rbq -i cscope.file
  5. ctags -R --c++-kinds=+px --fields=+iaS --extra=+q -L cscope.file

  6. rm -rf cscope.file
  7. echo create tags file success
  8. echo create cscope file success

vim文件添加以下配置:

  1. "ctags"

  2. set tags=tags;
  3. set autochdir

  4. "========================="
  5. set tags+=~/.vim/systags
  6. "========================="

  7. if has("cscope")
  8. set csto=0
  9. set nocsverb
  10. " add any database in current directory
  11. if filereadable("cscope.out")
  12. "cs add cscope.out
  13. cs add $PWD/cscope.out $PWD
  14. else "子目录打开,向上查找
  15. let cscope_file=findfile("cscope.out", ".;")
  16. let cscope_pre=matchstr(cscope_file, ".*/")
  17. if !empty(cscope_file) && filereadable(cscope_file)
  18. exe "cs add" cscope_file cscope_pre
  19. endif
  20. endif
  21. set csverb
  22. "set cst 这两句会将cscope当作tag,当找不到时会卡住,因此注释掉
  23. "set cscopetag
  24. endif

  25. nmap zs :cs find s <C-R>=expand("<cword>")<CR><CR>
  26. nmap zg :cs find g <C-R>=expand("<cword>")<CR><CR>
  27. nmap zc :cs find c <C-R>=expand("<cword>")<CR><CR>
  28. nmap zt :cs find t <C-R>=expand("<cword>")<CR><CR>
  29. nmap ze :cs find e <C-R>=expand("<cword>")<CR><CR>
  30. nmap zf :cs find f <C-R>=expand("<cfile>")<CR><CR>
  31. nmap zi :cs find i ^<C-R>=expand("<cfile>")<CR>$<CR>
  32. nmap zd :cs find d <C-R>=expand("<cword>")<CR><CR>

最终:
ctrl + ]: 查找跳转定义
z + c: 查找跳转引用
ctrl + t: 返回
ctrl + o: 也能返回


ctags在查找linux kernel某个函数或者变量时也有用(相当于IDE的全局搜索功能)

Tags文件中包括这些对象的列表:
1.用#define定义的宏
2.枚举型变量的值
3.函数的定义、原型和声明
4.名字空间(namespace)
5.类型定义(typedefs)
6.变量(包括定义和声明)
7.类(class)、结构(struct)、枚举类型(enum)和联合(union)
8.类、结构和联合中成员变量或函数
VIM用这个“tags”文件来定位上面这些做了标记的对象。

如果这个变量或函数有多处定义,在vim命令行模式

  • :ts (tagselect)命令就能列出一个列表供用户选择
  • :tp 为上一个tag标记文件,
  • :tn 为下一个tag标记文件。

当然,若当前tags文件中用户所查找的变量或函数名只有一个,“:tp,:tn”命令不可用。

举例:输入:ts kmalloc
image-1739610210834

image-1739610210834