【问题标题】:Get ctags in vim to go to definition, not declaration在 vim 中获取 ctags 以进行定义,而不是声明
【发布时间】:2020-04-21 09:13:06
【问题描述】:

我遇到的问题是 vim/gvim 中的 ctags 多次将我带到前向声明而不是函数的实际定义。

有什么办法可以解决这个问题?

【问题讨论】:

    标签: vim ctags


    【解决方案1】:

    我认为最简单的方法是使用“g ctrl-]”而不是“ctrl-]”。如果只有一场比赛,它会带你去那里。如果有多个匹配项,它会将它们全部列出,让您选择您想要的一个,就像 :tselect 一样。两全其美的。 :)

    【讨论】:

    • 对我来说,这只是给了我两个共享名称的函数的前向声明(在不同的文件中)。但它没有显示函数的实现。
    • 哇。这改变了生活。
    • 我发现的另一种方法是在头文件而不是所有文件上使用ctags。这对我在使用库时特别有用,我主要只对用作基本文档的声明感兴趣。
    • @VictorZamanian:只有在有头文件时才有效,就像在 C 中一样。
    • 这根本不是解决方案。问题是 ctags 只索引声明,而不是定义。即使您输入 ":tj ",您也只会看到一个条目:错误的条目。这不是 Vim 的问题,而是 ctags 的问题。
    【解决方案2】:

    我相信 Vim 默认会转到标签文件中的第一个标签。如果您愿意,可以选择不同的:使用:tj(类似于:tselect,但如果只有一个匹配项则自动跳转)或Ctrl-] 后跟:tn)。

    更改默认值的唯一方法是更改​​标签文件的顺序,但我不相信 ctags 提供命令行选项来执行此操作。

    这并不像听起来那么难,因为您基本上需要一个脚本来打开标签文件,按标签的“种类”对其进行排序并再次将其写回。标签中的“种类”是单个字符,描述它是函数 (f)、函数原型 (p)、宏、枚举名称等。如果您使用的是 Linux,它可以,理论上,就这么简单:

    #!/bin/sh
    ctags -R -f - . | tac > tags
    

    由于tac 反转文件中的行顺序,这将自动将定义放在首位。但是,由于需要维护标头并且 Vim 更喜欢对标记文件进行排序,所以它变得有点复杂,所以最好通过文件并按正序对第一个条目(标记名称)进行排序,然后种倒序。因此,更复杂的东西可能会更好。

    我为这个无耻的插件道歉,但我已经编写了一个 Vim 插件,它(间接地)满足你的需要。它旨在为函数名称、宏、枚举等添加许多额外的突出显示组。但是,这样做的另一件事是重新排序标记文件,以便函数实现在函数声明之前,从而实现你想要的(我和你有同样的需要)。如果您不想要任何突出显示功能,您可以很容易地将其全部去掉:这是一个相当简单的 python 程序和一个更简单的 Vim 脚本,可从my site 获得。

    【讨论】:

    • 问题是 ctags 有时只对前向声明进行分类,而不对定义进行分类。标记条目(其中只有一个)逐行搜索模式,并且该模式与定义之前的前向声明相匹配。
    【解决方案3】:

    您应该可以使用tntp 跳转到各种匹配的标签。

    • ^] 带您观看第一场比赛。
    • 如果这不是您想要的匹配项,请键入 :tn 转到下一个。
    • 如果您输入:tn 的次数过多,您可以输入:tp 以返回上一个。

    【讨论】:

      【解决方案4】:

      派对迟到了,但对于即将到来的 vim 标签 googlers:

      我发现除了 ctags 之外还可以使用 cscope,至少对于 C/C++ 来说是这样。调用树更智能,如果失败,您可以将其设置为回退到 ctags。每次运行 ctags -R 时只需运行“cscope -b”。你就可以出发了。如果您使用下面的设置,您将能够像往常一样使用 Ctrl-]/Ctrl-T,但您也可以添加漂亮的新跳转,例如跳转到函数声明并显示函数调用者的跳转列表。

      " setup
      if has("cscope")
          set csto=0                                                                             
          set cst
          set nocsverb
          " add any database in current directory
          if filereadable("cscope.out")
          cs add cscope.out
          " else add database pointed to by environment
          elseif $CSCOPE_DB != ""
          cs add $CSCOPE_DB
          endif
          set csverb
      endif
      
      " jump to a function declaration
      nmap <silent> <C-\> :cs find s <C-R>=expand("<cword>")<CR><CR>1<CR><CR>
      " show a list of where function is called
      nmap <silent> <C-_> :cs find c <C-R>=expand("<cword>")<CR><CR>
      

      【讨论】:

      • 每次运行 ctags -R 时都运行“cscope -b”是什么意思?在运行 ctags -R 之前运行 cscope -b?还是之后?
      【解决方案5】:

      这个选项更适合我

      在你的 .vimrc 中加入下面一行,现在你可以双击鼠标(在文件中的 variable/entry 上)跳转到标记的位置。如果找到单个匹配项,它将立即跳转。如果多个条目匹配,它将提示用户输入..

      :map <2-LeftMouse> g< c-]>
      

      【讨论】:

      • 无法编辑,但在第二个 &lt; 之后有一个额外的空格拼写错误。也可以set mouse=nset mouse=a 让鼠标在 Vim 中工作。
      【解决方案6】:

      将以下内容添加到您的 .vimrc 文件中:

      noremap <c-]> 2<c-]>
      

      这一行使 vim 自动跳转到第二个匹配项(而不是第一个匹配项),这通常是函数定义。

      【讨论】:

      • 是什么让你认为匹配的第二次出现是定义?
      • 没有第二个匹配项。
      【解决方案7】:
      :tselect my_little_function 
      

      你会得到一个匹配列表。 或者如果你跳转到一个标签并且你对它不满意,那么输入

      :tselect
      

      你会得到最后一个活动标签的替代列表。

      【讨论】:

      • 也可以是:ts
      【解决方案8】:

      有几种方法可以让 Vim 直接跳转到一个标签,如果只有一个标签匹配,否则呈现一个标签匹配列表。

      您可以使用“tjump”ex 命令。例如,命令“:tjump func1”将跳转到定义 func1,如果它只定义一次。如果 func1 被多次定义,则会显示匹配的标签列表。

      您可以将光标放在标签上,然后按g Ctrl-]

      您可以直观地选择文本并按g Ctrl-] 跳转或列出匹配的标签。

      您可以使用“stjump”ex 命令。这将在新窗口中从标签列表中打开匹配或选定的标签。

      您可以按Ctrl-W g Ctrl-] 进行:stjump。

      Help: :tjump, g_Ctrl-], v_g_CTRL-], :stjump, Ctrl-W_g_Ctrl-]
      

      【讨论】:

      • 这仅在 ctags 实际对前向声明和定义进行分类时才有效。对于 ctags 中而不是 Vim 中的问题,这是一种笨拙的解决方法。
      【解决方案9】:

      这可能是因为存储在标签文件中的模式匹配在定义之前找到了声明。

      当 Exuberant Ctags 为标识符创建条目时,它会将整个包含行添加为匹配项。

      但是,有时前向声明与该模式匹配。这可能在某些编码风格下发生。

      首先,这是一个示例文件,foo.c,它没有这个问题:

      static int foo(int x);
      static int foo(int x)
      {
      }
      

      Ctags 创建一个如下所示的条目:

      foo foo.c   /^static int foo(int x)$/;" f   file:   signature:(int x)
      

      匹配以^$为锚,由于前向声明以;结尾,所以不匹配。

      但是,以下编码约定会触发该问题:

      static int foo(
       int x
      );
      
      static int foo(
        int x
      )
      {
      }
      

      tags 条目现在是:

      foo foo.c   /^static int foo($/;"   f   file:   signature:( int x )
      

      这将找到声明的第一行,与定义的第一行没有区别。

      解决方法是使这两者有所不同,同时保持编码约定。在这种情况下,我们在 C 中,所以我们可以做的一件事是从定义中删除 static

      没有存储类说明符的 C 文件范围声明声明了一个带有链接的名称。链接类型(内部或外部)继承自任何先前指定链接的名称声明。因此:

      static int foo(
       int x
      );
      
      int foo(
        int x
      )
      {
      }
      

      现在标签看起来像:

      foo foo.c   /^int foo($/;"  f   signature:( int x )
      

      只匹配定义; Vim 不再跳转到第一个声明。

      如果这个解决方案不可行,那么可能需要编写一个tags 过滤工具来扫描tags 文件并识别这个问题,修复有问题的标签。例如,对于第二个行不明确的文件,我们可以像这样手动修复标签:

      foo     foo.c   /^static int foo($/;/^static int foo($/;"       f       file:   signature:( int x )
      

      我在标签地址中添加了第二个 Ex 命令,以再次搜索相同的模式。现在它跳到正确的行。 (注意:但是,如果前向声明是文件的第一行,这将被破坏。)

      在文件中查找具有多个匹配项的模棱两可的标签并以上述方式(或其他想法)对其进行编辑可以自动化,每次生成tags 文件时都会进行后处理。但是,文件的额外扫描很昂贵;这确实应该在 Ctags 中完成。

      【讨论】:

        【解决方案10】:

        你可以按2然后CTRL+],这会直接进入第二个匹配,在java中,这通常是实现的一些界面。

        【讨论】:

          猜你喜欢
          • 2023-04-09
          • 1970-01-01
          • 1970-01-01
          • 2018-03-23
          • 2020-03-04
          • 2011-11-26
          • 2016-01-06
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多