c ++, cscope, ctags и vim: поиск классов, которые наследуются от этого

В довольно большой базе кода с несколькими слоями есть ли способ в vim или из командной строки найти все классы, которые являются производными от базового класса? grep является опцией, но может быть медленным, поскольку grep не индексирует.

5

Решение

Ни cscope, ни ctags не позволяют нам иметь дело с наследованием напрямую, но это относительно легко обойти это ограничение, потому что производные классы также индексируются.

Cscope

В cscope ищу «символ C» Foobar обычно перечисляет оригинальный класс а также классы, наследующие от него. Поскольку поиск выполняется по базе данных, это молниеносно.

В качестве альтернативы, вы можете использовать возможности поиска cscope egrep с помощью шаблона :.*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
3

Другие решения

Если вы создаете файлы тегов с Exuberant CTags, используя информацию о наследовании (см. опцию —fields), тогда будет работать следующий скрипт. Добавляет :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>')
2

В LH-CPP, Я определяю команду :Children, Он опирается на базу данных ctags и, как следствие, он довольно ограничен.

Он принимает два необязательных параметра: пространство имен, где искать (я не нашел способа избежать этого), и имя родительского класса -> :Children [!] {namespace} {parent-class},

Команда пытается кэшировать как можно больше информации. Следовательно, когда соответствующая информация изменяется в базе данных ctags, кэш должен обновляться. Это делается с помощью команды -> :Children!

2

Я не думаю, что vim является правильным инструментом для перечисления всех дочерних классов. Вместо этого нам лучше использовать doxygen для генерации документации для исходного кода. Хотя doxygen требуется некоторое время, мы можем использовать документ / диаграммы для всех классов, что понятно и быстро.

1