C ++, cscope, ctags and vim: searching for classes that inherit from this

In a fairly large code base with multiple layers, is there a way in vim or from the command line to find all the classes that are derived from the base class? grep is an option, but can be slow since grep does not index.

+5
source share
4 answers

Neither cscope nor ctags allow us to directly deal with inheritance, but it is relatively easy to circumvent this limitation because derived classes are also indexed.

Cscope

In cscope, searching for the “C character” Foobar usually contains the source class and classes that inherit it. Since the search is performed on the database, it is lightning fast.

Alternatively, you can use the cscope egrep search functions with a pattern such as :.*Foobar to display only classes that inherit from Foobar .

So, even if we don’t have a dedicated “Find classes that inherit this class” command, we can do this work without much effort.

Ctags

While ctags allows you to include inheritance information with --fields=+i , this information cannot be used directly in Vim. The inherits field inherits parsed by Vim, however, you can probably create a quick and dirty solution using taglist() .

ack, ag

These two programs work more or less like grep, but they are aimed at searching in the source code, so they are really faster than grep.

In my configuration, Vim :grep set to run the ag program instead of the standard grep , so the search for classes obtained from the class under the cursor will look like this:

 :grep :.*<Cr><Cw><CR> 

Here are the relevant lines from my ~/.vimrc :

 if executable("ag") set grepprg=ag\ --nogroup\ --nocolor\ --ignore-case\ --column set grepformat=%f:%l:%c:%m,%f:%l:%m endif 
+3
source

In lh-cpp, I define the command :Children . It relies on the ctags database and, as a result, is rather limited.

Two optional parameters are required: the namespace, where to look (I did not find a way to avoid this), and the name of the parent class → :Children [!] {namespace} {parent-class} .

The team is trying to cache as much information as possible. Therefore, when the relevant information changes in the ctags database, the cache must be updated. This is done by pressing the command → :Children!

+2
source

If you create tag files with Exuberant CTags using inheritance information ( see the --fields parameter ), then the following script will Work. He adds the command :Inherits , which accepts either a class name (for example :Inherits Foo ) or a regular expression.

Like the :tag command, you indicate that you want a regular expression search, preceded by a '\', for example. :Inherits \Foo.* .

The results are placed in the location list of the window that you are viewing with :ll :lne :lp , etc. VIM does not seem to allow scripts to modify the tag list, they prefer.

If you're wondering why I'm not using taglist() for this, because taglist() incredibly slow in large tag files. The original post had a version using taglist() , if you're interested, you can view the change history.

 " 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
source

I do not think vim is the right tool to list all child classes. Instead, we better use doxygen to create documentation for the source code. Although doxygen takes some time, we can use document / diagrams for all classes, which is clear and fast.

+1
source

Source: https://habr.com/ru/post/1201782/


All Articles