【问题标题】:Vim auto-generate ctagsVim 自动生成 ctags
【发布时间】:2010-09-14 09:39:58
【问题描述】:

现在我的.vimrc 中有以下内容:

au BufWritePost *.c,*.cpp,*.h !ctags -R

这有几个问题:

  1. 速度很慢 - 为自上次生成标记后未更改的文件重新生成标记。
  2. 我必须在写入文件后再次按 Enter 按钮,因为不可避免的“按 Enter 或键入命令继续”。

当你将这两个问题结合起来时,我最终会过早地按下额外的输入(在ctags -R 完成之前),然后看到烦人的错误消息,不得不再次按下输入。

我知道这听起来没什么大不了的,但是考虑到我在某一天执行的文件写入量,它往往会变得非常烦人。一定有更好的方法来做到这一点!

【问题讨论】:

    标签: vim autocomplete ctags


    【解决方案1】:

    au BufWritePost *.c,*.cpp,*.h silent! !ctags -R &

    缺点是在完成之前您不会有有用的标签文件。只要您在 *nix 系统上,在之前的 ctags 完成之前进行多次写入应该是可以的,但您应该测试它。在 Windows 系统上,它不会把它放在后台,它会抱怨文件被锁定直到第一个 ctags 完成(这不应该在 vim 中引起问题,但你最终会得到一个稍微过时的标签文件)。

    注意,您可以按照 tonylo 的建议使用 --append 选项,但随后您必须禁用 tagbsearch,这可能意味着标签搜索需要更长的时间,具体取决于标签文件的大小。

    【讨论】:

    • 啊...很好的解决方案。我用的是静音! ctags -R 并且不明白为什么什么也没发生...我想您需要第二次爆炸。
    • 在 Windows 上,您始终可以使用“start ctags -R”。它将在背景中启动 ctags。
    • 当文件引起 ctags 警告时,我遇到了这个解决方案的问题,因为这些警告会显示在 vim 中并弄乱屏幕(例如 ctags: Warning: ignoring null tag in public/jquery.form.js)。我只是将 stderr 重定向到 /dev/null 并修复了它,但这意味着我不会知道标签的问题。 autocmd BufWritePost *.rb,*.js silent! !/usr/local/bin/ctags -R 2> /dev/null &
    • @mmrobins 如果重定向到 dev/null 不起作用,有什么建议吗?我明白你在做什么,但 Vim 似乎不尊重这一点,仍然打印到屏幕上。
    【解决方案2】:

    编辑:非常类似于以下内容的解决方案已发布为AutoTag vim 脚本。但是请注意,该脚本需要一个支持 Python 的 vim

    我的解决方案改为使用 awk,因此它应该适用于更多系统。


    au FileType {c,cpp} au BufWritePost <buffer> silent ! [ -e tags ] &&
        \ ( awk -F'\t' '$2\!="%:gs/'/'\''/"{print}' tags ; ctags -f- '%:gs/'/'\''/' )
        \ | sort -t$'\t' -k1,1 -o tags.new && mv tags.new tags
    

    请注意,您只能在脚本中以这种方式编写它,否则它必须单独一行。

    里面发生了很多事情:

    1. 此自动命令在检测到文件为 C 或 C++ 时触发,并依次添加由 BufWritePost 事件触发的缓冲区本地自动命令。

      李>
    2. 它使用% 占位符,它在执行时被缓冲区的文件名替换,以及:gs 修饰符用于对文件名进行shell引用(通过将任何嵌入的单引号转换为quote-escape -quote-quote)。

    3. 1234563只是刚刚保存的文件,然后结果是sorted 并放回原处。

    Caveat 实现者:假设所有内容都在同一个目录中,并且这也是缓冲区本地当前目录。我没有考虑过路径修改。

    【讨论】:

      【解决方案3】:

      我写easytags.vim 就是为了做到这一点:自动更新和突出显示标签。该插件可以配置为仅更新正在编辑的文件或正在编辑的文件目录中的所有文件(递归)。它可以使用全局标签文件、文件类型特定标签文件和项目特定标签文件。

      【讨论】:

      • easytags 很好很酷,但是默认的 vim 事件触发时间太急迫了.. 为了防止烦人的停顿,将此添加到您的 f.e. /etc/vim/vimrc.local:set updatetime=30000 "trigger autocmds after half a minute of user inactivity instead of after 4 secondsset swapsync="" "disable swap file sync on user inactivity trigger
      • @eMPee584:我同意它有时会变得非常烦人(中断用户的工作流程)。我目前正在处理a branch where Exuberant Ctags is executed in an asynchronous subprocess。一旦它在所有平台上可靠地开箱即用,我可能会将其设为默认值。
      【解决方案4】:

      我注意到这是一个旧线程,但是... 在支持 inotify 的 *nix 类环境中使用 incron。每当目录中的文件更改时,它总是会启动命令。即,

      /home/me/Code/c/that_program IN_DELETE,IN_CLOSE_WRITE ctags --sort=yes *.c
      

      就是这样。

      【讨论】:

        【解决方案5】:

        也许可以使用 ctags 的 append 参数,如下所示:

        http://vim.wikia.com/wiki/Autocmd_to_update_ctags_file

        我不能真正保证这一点,因为我通常使用 source Insight 进行代码浏览,但使用 vim 作为编辑器......看图。

        【讨论】:

          【解决方案6】:

          如何安排 ctags 通过 crontab 运行?如果您的项目树的结构相当稳定,那应该可行吗?

          【讨论】:

          • +1,亲吻。这是我刚刚设置为每天凌晨 2:10 运行的 cronjob 示例。显然,您可以拥有任意数量的。我的 ctags 选项适用于 drupal,这可能超出了您的需要。 10 2 * * * cd /var/www/yoursite/ && /usr/bin/ctags --langmap=php:.engine.inc.module.theme.install.php --php-kinds=cdf --languages=php --递归
          【解决方案7】:

          要禁止“按回车”提示,请使用:silent

          【讨论】:

            【解决方案8】:

            在 OSX 上,这个命令不能开箱即用,至少对我来说不是。

            au BufWritePost *.c,*.cpp,*.h silent! !ctags -R &
            

            我找到了post,它解释了如何获取包含 -R 选项的标准 ctags 版本。仅此一项对我不起作用。我必须将 /usr/local/bin 添加到 .bash_profile 中的 PATH 变量中,以便获取 Homebrew 安装程序的 bin。

            【讨论】:

              【解决方案9】:

              在我看来,插件 Indexer 更好。

              http://www.vim.org/scripts/script.php?script_id=3221

              可以是:

              1) project.tar.gz 的附加组件

              2) 一个独立的插件

              • 背景标签生成(ctags 工作时您无需等待)
              • 支持多个项目

              【讨论】:

                【解决方案10】:

                有一个名为 AutoTag 的 vim 插件非常有效。

                如果您安装了 taglist,它也会为您更新。

                【讨论】:

                  【解决方案11】:

                  --append 选项确实是要走的路。与grep -v 一起使用,我们只能更新一个标记 文件。例如,这里是解决此问题的unpolished plugin 的摘录。 (注意:它需要一个“外部”library plugin

                  " Options {{{1
                  let g:tags_options_cpp = '--c++-kinds=+p --fields=+imaS --extra=+q'
                  
                  function! s:CtagsExecutable()
                    let tags_executable = lh#option#Get('tags_executable', s:tags_executable, 'bg')
                    return tags_executable
                  endfunction
                  
                  function! s:CtagsOptions()
                    let ctags_options = lh#option#Get('tags_options_'.&ft, '')
                    let ctags_options .= ' '.lh#option#Get('tags_options', '', 'wbg')
                    return ctags_options
                  endfunction
                  
                  function! s:CtagsDirname()
                    let ctags_dirname = lh#option#Get('tags_dirname', '', 'b').'/'
                    return ctags_dirname
                  endfunction
                  
                  function! s:CtagsFilename()
                    let ctags_filename = lh#option#Get('tags_filename', 'tags', 'bg')
                    return ctags_filename
                  endfunction
                  
                  function! s:CtagsCmdLine(ctags_pathname)
                    let cmd_line = s:CtagsExecutable().' '.s:CtagsOptions().' -f '.a:ctags_pathname
                    return cmd_line
                  endfunction
                  
                  
                  " ######################################################################
                  " Tag generating functions {{{1
                  " ======================================================================
                  " Interface {{{2
                  " ======================================================================
                  " Mappings {{{3
                  " inoremap <expr> ; <sid>Run('UpdateTags_for_ModifiedFile',';')
                  
                  nnoremap <silent> <Plug>CTagsUpdateCurrent :call <sid>UpdateCurrent()<cr>
                  if !hasmapto('<Plug>CTagsUpdateCurrent', 'n')
                    nmap <silent> <c-x>tc  <Plug>CTagsUpdateCurrent
                  endif
                  
                  nnoremap <silent> <Plug>CTagsUpdateAll     :call <sid>UpdateAll()<cr>
                  if !hasmapto('<Plug>CTagsUpdateAll', 'n')
                    nmap <silent> <c-x>ta  <Plug>CTagsUpdateAll
                  endif
                  
                  
                  " ======================================================================
                  " Auto command for automatically tagging a file when saved {{{3
                  augroup LH_TAGS
                    au!
                    autocmd BufWritePost,FileWritePost * if ! lh#option#Get('LHT_no_auto', 0) | call s:Run('UpdateTags_for_SavedFile') | endif
                  aug END
                  
                  " ======================================================================
                  " Internal functions {{{2
                  " ======================================================================
                  " generate tags on-the-fly {{{3
                  function! UpdateTags_for_ModifiedFile(ctags_pathname)
                    let source_name    = expand('%')
                    let temp_name      = tempname()
                    let temp_tags      = tempname()
                  
                    " 1- purge old references to the source name
                    if filereadable(a:ctags_pathname)
                      " it exists => must be changed
                      call system('grep -v "  '.source_name.' " '.a:ctags_pathname.' > '.temp_tags.
                        \ ' && mv -f '.temp_tags.' '.a:ctags_pathname)
                    endif
                  
                    " 2- save the unsaved contents of the current file
                    call writefile(getline(1, '$'), temp_name, 'b')
                  
                    " 3- call ctags, and replace references to the temporary source file to the
                    " real source file
                    let cmd_line = s:CtagsCmdLine(a:ctags_pathname).' '.source_name.' --append'
                    let cmd_line .= ' && sed "s#\t'.temp_name.'\t#\t'.source_name.'\t#" > '.temp_tags
                    let cmd_line .= ' && mv -f '.temp_tags.' '.a:ctags_pathname
                    call system(cmd_line)
                    call delete(temp_name)
                  
                    return ';'
                  endfunction
                  
                  " ======================================================================
                  " generate tags for all files {{{3
                  function! s:UpdateTags_for_All(ctags_pathname)
                    call delete(a:ctags_pathname)
                    let cmd_line  = 'cd '.s:CtagsDirname()
                    " todo => use project directory
                    "
                    let cmd_line .= ' && '.s:CtagsCmdLine(a:ctags_pathname).' -R'
                    echo cmd_line
                    call system(cmd_line)
                  endfunction
                  
                  " ======================================================================
                  " generate tags for the current saved file {{{3
                  function! s:UpdateTags_for_SavedFile(ctags_pathname)
                    let source_name    = expand('%')
                    let temp_tags      = tempname()
                  
                    if filereadable(a:ctags_pathname)
                      " it exists => must be changed
                      call system('grep -v "  '.source_name.' " '.a:ctags_pathname.' > '.temp_tags.' && mv -f '.temp_tags.' '.a:ctags_pathname)
                    endif
                    let cmd_line = 'cd '.s:CtagsDirname()
                    let cmd_line .= ' && ' . s:CtagsCmdLine(a:ctags_pathname).' --append '.source_name
                    " echo cmd_line
                    call system(cmd_line)
                  endfunction
                  
                  " ======================================================================
                  " (public) Run a tag generating function {{{3
                  function! LHTagsRun(tag_function)
                    call s:Run(a:tag_function)
                  endfunction
                  
                  " ======================================================================
                  " (private) Run a tag generating function {{{3
                  " See this function as a /template method/.
                  function! s:Run(tag_function)
                    try
                      let ctags_dirname  = s:CtagsDirname()
                      if strlen(ctags_dirname)==1
                        throw "tags-error: empty dirname"
                      endif
                      let ctags_filename = s:CtagsFilename()
                      let ctags_pathname = ctags_dirname.ctags_filename
                      if !filewritable(ctags_dirname) && !filewritable(ctags_pathname)
                        throw "tags-error: ".ctags_pathname." cannot be modified"
                      endif
                  
                      let Fn = function("s:".a:tag_function)
                      call Fn(ctags_pathname)
                    catch /tags-error:/
                      " call lh#common#ErrorMsg(v:exception)
                      return 0
                    finally
                    endtry
                  
                    echo ctags_pathname . ' updated.'
                    return 1
                  endfunction
                  
                  function! s:Irun(tag_function, res)
                    call s:Run(a:tag_function)
                    return a:res
                  endfunction
                  
                  " ======================================================================
                  " Main function for updating all tags {{{3
                  function! s:UpdateAll()
                    let done = s:Run('UpdateTags_for_All')
                  endfunction
                  
                  " Main function for updating the tags from one file {{{3
                  " @note the file may be saved or "modified".
                  function! s:UpdateCurrent()
                    if &modified
                      let done = s:Run('UpdateTags_for_ModifiedFile')
                    else
                      let done = s:Run('UpdateTags_for_SavedFile')
                    endif
                  endfunction
                  

                  这段代码定义:

                  • ^Xta 强制更新当前项目中所有文件的标签库;
                  • ^Xtc 强制更新当前(未保存)文件的标签库;
                  • 每次保存文件时更新标签库的自动命令; 它支持和许多选项来禁用我们不想要的自动更新,根据文件类型调整 ctags 调用,... 这不仅仅是一个提示,而是一个插件的一小段摘录。

                  HTH,

                  【讨论】:

                    【解决方案12】:

                    Auto Tag 是一个 vim 插件,可以在保存时更新现有的标签文件。

                    我多年来一直在使用它,没有任何问题,除了它对标签文件强制执行最大大小。除非你有大量代码都在同一个标​​签文件中被索引,否则你不应该达到这个限制。

                    请注意,自动标记需要 vim 中的 Python 支持。

                    【讨论】: