在一个有几个层的相当大的代码库中,在vim或命令行中是否有方法找到从基类派生的所有类?grep是一种选择,但可能很慢,因为grep不索引。
cscope和ctags都不允许我们直接处理继承,但相对来说,绕过这一限制是很容易的,因为派生类也被索引。
cscope
在cscope中,查找"C符号"Foobar
通常会列出从中继承的原始类和。由于搜索是针对数据库进行的,因此速度非常快。
或者,您可以使用cscope的egrp搜索功能和类似:.*Foobar
的模式,只列出从Foobar
继承的类。
因此,即使我们没有专门的"查找从这个类继承的类"命令,我们也可以毫不费力地完成工作。
ctags
虽然ctags允许您在--fields=+i
中包含继承信息,但该信息不能直接在Vim中使用。不过,inherits
字段是由Vim解析的,因此可以使用taglist()
构建一个快速而肮脏的解决方案。
ack,ag
这两个程序的工作方式或多或少类似于grep,但它们的目标是在源代码中进行搜索,因此它们确实比grep快。
在我的Vim配置中,:grep
被设置为运行ag
程序,而不是默认的grep
,因此,在光标下搜索从类派生的类看起来像:
:grep :.*<C-r><C-w><CR>
以下是我的~/.vimrc
:中的相关行
if executable("ag")
set grepprg=ag --nogroup --nocolor --ignore-case --column
set grepformat=%f:%l:%c:%m,%f:%l:%m
endif
如果使用继承信息(请参阅--fields选项)使用Exuberant CTags构建标记文件,则以下脚本将起作用。它添加了一个:Inherits
命令,该命令采用类的名称(例如:Inherits Foo
)或正则表达式。
与:tag
命令一样,您可以通过在正则表达式前面加一个"\"字符(例如:Inherits Foo.*
)来指示您希望使用正则表达式进行搜索。
结果被放入窗口的位置列表中,您可以使用:ll
、:lne
、:lp
等进行浏览。VIM似乎不允许脚本修改标记列表,这是我更喜欢的。
如果你想知道为什么我不使用taglist()
,那是因为taglist()
在大型标记文件上的速度非常慢。最初的帖子有一个使用taglist()
的版本,如果你好奇,你可以浏览编辑历史。
" Parse an Exuberant Ctags record using the same format as taglist()
"
" Throws CtagsParseErr if there is a general problem parsing the record
function! ParseCtagsRec(record, tag_dir)
let tag = {}
" Parse the standard fields
let sep_pos = stridx(a:record, "t")
if sep_pos < 1
throw 'CtagsParseErr'
endif
let tag['name'] = a:record[:sep_pos - 1]
let tail = a:record[sep_pos + 1:]
let sep_pos = stridx(tail, "t")
if sep_pos < 1
throw 'CtagsParseErr'
endif
" '/' will work as a path separator on most OS's, but there
" should really be an OS independent way to build paths.
let tag['filename'] = a:tag_dir.'/'.tail[:sep_pos - 1]
let tail = tail[sep_pos + 1:]
let sep_pos = stridx(tail, ";"t")
if sep_pos < 1
throw 'CtagsParseErr'
endif
let tag['cmd'] = tail[:sep_pos - 1]
" Parse the Exuberant Ctags extension fields
let extensions = tail[sep_pos + 3:]
for extension in split(extensions, 't')
let sep_pos = stridx(extension, ':')
if sep_pos < 1
if has_key(tag, 'kind')
throw 'CtagsParseErr'
endif
let tag['kind'] = extension
else
let tag[extension[:sep_pos - 1]] = extension[sep_pos + 1:]
endif
endfor
return tag
endfunction
" Find all classes derived from a given class, or a regex (preceded by a '/')
" The results are placed in the current windows location list.
function! Inherits(cls_or_regex)
if a:cls_or_regex[0] == '/'
let regex = a:cls_or_regex[1:]
else
let regex = '<'.a:cls_or_regex.'>$'
endif
let loc_list = []
let tfiles = tagfiles()
let tag_count = 0
let found_count = 0
for file in tfiles
let tag_dir = fnamemodify(file, ':p:h')
try
for line in readfile(file)
let tag_count += 1
if tag_count % 10000 == 0
echo tag_count 'tags scanned,' found_count 'matching classes found. Still searching...'
redraw
endif
if line[0] == '!'
continue
endif
let tag = ParseCtagsRec(line, tag_dir)
if has_key(tag, 'inherits')
let baselist = split(tag['inherits'], ',s*')
for base in baselist
if match(base, regex) != -1
let location = {}
let location['filename'] = tag['filename']
let cmd = tag['cmd']
if cmd[0] == '/' || cmd[0] == '?'
let location['pattern'] = cmd[1:-2]
else
let location['lnum'] = str2nr(cmd)
endif
call add(loc_list, location)
let found_count += 1
endif
endfor
endif
endfor
catch /^OptionErr$/
echo 'Parsing error: Failed to parse an option.'
return
catch /^CtagsParseErr$/
echo 'Parsing error: Tags files does not appear to be an Exuberant Ctags file.'
return
catch
echo 'Could not read tag file:' file
return
endtry
endfor
call setloclist(0, loc_list)
echo tag_count 'tags scanned,' found_count 'matching classes found.'
endfunction
command! -nargs=1 -complete=tag Inherits call Inherits('<args>')
在lh-cpp中,我定义了命令:Children
。它依赖于ctags数据库,因此,它非常有限。
它需要两个可选参数:查找的名称空间(我还没有找到避免这种情况的方法)和父类的名称->:Children [!] {namespace} {parent-class}
。
该命令尝试缓存尽可能多的信息。因此,当ctags数据库中的相关信息发生变化时,必须更新缓存。这是通过敲击命令->:Children!
来完成的
我不认为vim是列出所有子类的正确工具。相反,我们最好使用doxygen为源代码生成文档。尽管doxygen需要一些时间,但我们可以为所有类使用文档/图表,这是清晰而快速的。