【问题标题】:c++, cscope, ctags, and vim: Finding classes that inherit from this onec++、cscope、ctags 和 vim:查找继承自此的类
【发布时间】:2014-09-04 00:39:00
【问题描述】:

在具有几层的相当大的代码库中,是否有办法在 vim 或命令行中找到从基类派生的所有类? grep 是一个选项,但可能会很慢,因为 grep 不索引。

【问题讨论】:

  • 我不这么认为,cscope 和 ctags 是基于正则表达式而不是像 IDE 那样的语法
  • 您是否考虑过AckAg the Silver Surfergit grep?这些通常都比 grep 快得多。
  • 我也对此感兴趣。我目前使用 Eclipse 和 SlickEdit 来执行此操作。在 Eclipse 中,此功能称为类型层次结构。有一个名为 eclim 的项目将 Eclipse 功能集成到 Vim 中,这是我在 Vim 中看到的最接近此功能的项目,不幸的是,eclim 占用了大量 RAM 并在 Windows 中不断崩溃,很可能是由于 Eclipse 并需要 Eclipse 正常调整内存设置..
  • 有一天,一个聪明的人将使用 libclang 创建一个 CLI 工具,它只适用于所有 C++ 查询(继承/使用):stackoverflow.com/questions/43460605/… 那一天,世界将被拯救。

标签: c++ vim ctags cscope


【解决方案1】:

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

【讨论】:

  • 我注意到使用 fugitive 和 vim 和 :Ggrep 命令可以加快速度。这种改进要求代码是我的帖子中没有提到的 git repo,但这是另一种选择。
  • 感谢您的提示,从您那里得到更好的提示::Ack! '(public|private|protected).*&lt;C-r&gt;&lt;C-w&gt;&lt;CR&gt;'
【解决方案2】:

如果您使用 Exuberant CTags 使用继承信息 (see the --fields option) 构建您的标签文件,那么以下脚本将起作用。它添加了一个: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>')

【讨论】:

    【解决方案3】:

    lh-cpp 中,我定义了命令:Children。它依赖于 ctags 数据库,因此非常有限。

    它有两个可选参数:要查找的名称空间(我还没有找到避免这种情况的方法),以及父类的名称 -> :Children [!] {namespace} {parent-class}

    该命令尝试缓存尽可能多的信息。因此,当 ctags 数据库中的相关信息发生变化时,必须更新缓存。它是通过敲击命令来完成的 -> :Children!

    【讨论】:

      【解决方案4】:

      我不认为 vim 是列出所有子类的正确工具。相反,我们最好使用 doxygen 为源代码生成文档。虽然doxygen需要一些时间,但我们可以将文档/图表用于所有类,清晰快捷。

      【讨论】:

      • 这假设您正在使用图形界面进行开发。有时,我必须通过 ssh 使用 minty(cygwins 终端),这意味着开发框必须有一个 Web 服务器来托管 doxygen 文件。
      • 是的,完全正确。而Linux/Unix服务器已经提供了SSH,很容易部署web服务器,也建议在web服务器中启用PHP,在doxygen页面内提供简单的搜索引擎。
      • Eclipse 和 SlickEdit 都可以列出类的继承树以及覆盖方法的树状列表。这在 Eclipse 中称为类型层次结构,是 IDE 中使用 OO 语言编码时最有用的功能之一。 Doxygen 确实很有用,但是当您只需要快速查看“类型层次结构”时,它就有点过头了。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-10-16
      • 1970-01-01
      • 2011-02-27
      • 1970-01-01
      • 1970-01-01
      • 2011-01-25
      • 2018-11-08
      相关资源
      最近更新 更多