题外话: 使用VIM之前一直是用source insight来看代码的,作为一个C程序员,source insight基本满足我的要求,有出色的代码著色和分析能力,并且内置小型Macro语言,可以用来进行一些扩展,有一定的可定制性,但是需要不停的在键盘和鼠标见移动手臂,效率不高,而且很累,所以产生抛弃的想法。 从配置VIM到基本利用VIM进行日常工作,大概花了2周时间,刚开始的时候有点小痛苦,但是最终坚持坚持下来,到现在使用接近2年了,早就有把自己的一些经验写下来的想法了。 这篇文章假设读者了解VIM和其基本操作,单单说一些如何使用VIM来进行代码阅读。 |
1. 需求
首先我们来看看阅读一个项目需要哪些辅助功能:
基本功能:
1.1 一个项目有很多的源文件组成的,所以第一个需求肯定是需要通过文件名能够快速的定位并打开文件;
1.2 代码高亮
1.3 代码分析,能够分析函数调用关系,变量定义等,在项目中可以自由的进行navigation
1.4 Symbol搜索
1.5 代码自动补全
辅助功能:
1.6 代码折叠
1.7 工作状态保存
1.8 批量注释
2. 需求实施
上边的需求中,有一些是VIM自带的功能可以实现的,有一些需要借助插件,我们来一个一个解决它。
2.1 通过文件名定位文件 - Lookupfile插件
堪称VIM最经典插件之一,它可以在预先生成的tags中快速搜索到文件,并打开它,支持正则表达式,它的tag文件可以使用ctags生成的,但是效率会比较低,最好是单独为Lookupfile生成tag文件,利用:h lookupfile可以查阅到unixy tool生成tag的命令,我们可以down一个经典Native Win32 port的Unix工具集UnxUtils,在这里有一个问题稍微注意一下,有几个工具的名字和Windows上工具的名字是一样的,这里推荐把UnixUtils的名字改一下,我把sort改成了gsort,find改成了gfind,然后把他们丢到一个目录里边,将这个目录加到环境变量里边就ok了。
需要的工具清单,主要有:
生成Lookupfile插件的tag,主要用到gfind和gsort工具,其他的工具会在后边用到,然后通过batch文件来自动化操作,bat文件源码如下:
echo !_TAG_FILE_SORTED 2 /0=unsorted, 1=sorted, 2=foldcase/ >filename
gfind . -regex ".*\.\(c\|h\|mak\|py\|pl\|bat\|cmd\)" -not -regex ".*\(no_use\).*" -printf "%%f\t%%p\t1\n" >>filename
gsort filename >filenametag
del filename
这段代码会在当前目录下生成一个包含所有子文件夹里边文件的的tag文件,名字为filenametag
有了这个tag文件,我们在vimrc中对lookupfile插件进行一下简单的配置:
let g:LookupFile_MinPatLength = 2
let g:LookupFile_PreserveLastPattern = 0
let g:LookupFile_PreservePatternHistory = 0
let g:LookupFile_AlwaysAcceptFirst = 1
let g:LookupFile_AllowNewFiles = 0
let g:LookupFile_UsingSpecializedTags = 1
let g:LookupFile_Bufs_LikeBufCmd = 0
let g:LookupFile_ignorecase = 1
let g:LookupFile_smartcase = 1
if filereadable("./filenametag")
let g:LookupFile_TagExpr = '"./filenametag"'
endif
" lookup file with ignore case
function! LookupFile_IgnoreCaseFunc(pattern)
let _tags = &tags
try
let &tags = eval(g:LookupFile_TagExpr)
let newpattern = '\c' . a:pattern
let tags = taglist(newpattern)
catch
echohl ErrorMsg | echo "Exception: " . v:exception | echohl NONE
return ""
finally
let &tags = _tags
endtry
" Show the matches for what is typed so far.
let files = map(tags, 'v:val["filename"]')
return files
endfunction
let g:LookupFile_LookupFunc = 'LookupFile_IgnoreCaseFunc'
10-12行的那句话就是load当前目录的filenametag给插件使用
2.2 代码高亮 - VIM自动实现,pass
2.3 代码分析
两个工具ctags和cscope,配合使用,都需要生成数据文件,我在2.1里边列的那个tool list里边就有这两个工具,这两个并不是UnixUtils工具包里边的工具,都可以在sourceforege里边找到的。
同样是样bat来生成数据文件:
del cscope.out
ctags -R --c++-types=+p --fields=+iaS --extra=+q
gfind . -regex ".*\.\(c\|h\|mak\|py\|pl\|bat\|cmd\)" -not -regex ".*\(no_use\).*" -type f -printf "%%p\n" >cscope.file
cscope -Rb -icscope.file
注意一下,第二行那个最好是选择c++-types,因为后边有一个插件会用到,如果设置成c-type,插件将无法使用
这段bat会在当前目录下生成tags(ctags数据文件)和cscope.out(cscope数据文件),然后我们在vimrc里边进行一下简单的配置:
ctags:
set tags=tags;
Cscope:
" set cscope output to quickfix windows
set cscopequickfix=s-,c-,d-,i-,t-,e-
2.4 symbols搜索
前边提到的cscope就可以用来快速搜索各种symbols,可以通过:cs来查看它的各种命令,但是它仅仅能够搜索到c/c++源码的内容,对于有的项目有各种各样的脚本和源码组成,cscope往往会漏掉一些东西,这里推荐大家在cscope的基础上,辅助以强大的grep工具,可以在vim里边通过:h grep来查看external grep的一些帮助。
VIM有一个插件就叫grep.vim,可以在vim.org的官网上下到。因为我们是Windows平台,所以,需要前边提到的那些UnixUtils工具。然后我们继续在vimrc里边进行一下简单的配置:
let Grep_Path = 'C:\Progra~1\Vim\vim73\utility\grep.exe'
let Fgrep_Path = 'C:\Progra~1\Vim\vim73\utility\fgrep.exe'
let Egrep_Path = 'C:\Progra~1\Vim\vim73\utility\egrep.exe'
let Agrep_Path = 'C:\Progra~1\Vim\vim73\utility\agrep.exe'
let Grep_Find_Path = 'C:\Progra~1\Vim\vim73\utility\gfind.exe'
let Grep_Xargs_Path = 'C:\Progra~1\Vim\vim73\utility\xargs.exe'
let Grep_Default_Filelist = '*.c *.h *.mak'
let Grep_Shell_Quote_Char = ""
let Grep_Default_Options = '-rinE '
前边的六项分别指到硬盘上对应的utility的位置,然后我们就可以来使用grep.vim插件了,最常用的就是:Rgrep了,对所有子文件夹里边的文件进行查找,输入命令后会有提示出来,让你输入要搜索的pattern和文件类型,默认的文件类型是,上边配置好的let Grep_Default_Filelist = '*.c *.h *.mak' 。grep.vim会自动将查找结果输出到quickfix窗口。
2.5 代码补全
omnicppcomplete是一个出色的代码补全工具,这个就是我们要用到前边用ctags生成的tag文件的另一个插件,也就是我们为什么要利用c++-types来生成type的原因,否则omnicppcomplete无法进行补全。
2.6 代码折叠
VIM本省支持代码折叠功能,通过:h fold命令来获取帮助,我自己一般就设置一个手动折叠,然后用v模式,选中以后zf将要折叠的东西折起来
2.7 工作区保存
:mks!可以保存工作区信息到工作目录,默认的名字是Session.vim,下次打开vim的时候通过:so Session.vim来恢复工作区
:mkview 可以保存工作区的折叠和窗口情况,下次打开vim的时候使用:loadview来恢复
2.9 批量注释
NERD_commenter这个经典插件可以帮你搞定一切,无需任何配置,安装以后,使用命令n,c<space>就可以在任何语言中进行多行注释,n为行数
2.10 经典的tagbar.vim/A.vim
tagbar这个插件可以列出本源文件的各种信息,包括宏,函数,全局变量,函数原型等等,值得注意的是,最好使用下边的配置语句,将tagbar放置到VIM的左边,否则
先打开tagbar然后如果有命令将输出打印到quickfix窗口,quickfix会出现在tagbar窄条的下边,不是很方便。
let g:tagbar_left = 1
A.vim支持用户通过:A命令来在.c和同名的.h之前来回切换,十分便捷
罗罗嗦嗦讲了不少了,最后想提一下,项目数据文件的组织,在Windows上,我们习惯于在项目文件夹下点击工程文件,对应的IDE将工程打开,我们所有的数据都load到工作区里边,我在使用vim的时候也采用了这种方式,利用了bat脚本自动生成filenametag, tags, cscope.out的同时再生成一个project.bat的脚本文件,project.bat的作用很简单,其实就是在当前目录下启动gvim。最后附上我的tag生成脚本:
@echo off
set path=%path%;C:\Program Files\Vim\vim73\utility;
time /t
echo !_TAG_FILE_SORTED 2 /0=unsorted, 1=sorted, 2=foldcase/ >filename
gfind . -regex ".*\.\(c\|h\|mak\|py\|pl\|bat\)" -not -regex ".*\(no_use\).*" -printf "%%f\t%%p\t1\n" >>filename
gsort filename >filenametag
del filename
del cscope.out
ctags -R --c++-types=+p --fields=+iaS --extra=+q
gfind . -regex ".*\.\(c\|h\|mak\|py\|pl\|bat\)" -not -regex ".*\(no_use\).*" -type f -printf "%%p\n" >cscope.file
cscope -Rb -icscope.file
time /t
echo set path=%path%;C:\Program Files\Vim\vim73\utility; >project.bat
echo set HOME=d:\tmp>>project.bat
echo start C:\Progra~1\Vim\vim73\gvim.exe >>project.bat
echo exit >>project.bat
@echo on