ctags と neocomplcache と unite-tag を使って関数定義箇所を開く

ちと今まで敬遠しがちだった ctags を真面目に使ってみました。
neocomplcahce と unite-tag を利用します。
ほとんど処理が neocomplcache と unite-tag で完結しているので実際はそんなに難しくなかったですね。

[Vimプラグイン]

NeoBundle "git://github.com/Shougo/neocomplcache.git"
NeoBundle "git://github.com/tsukkee/unite-tag.git"

[vimrc]

" path にヘッダーファイルのディレクトリを追加することで
" neocomplcache が include 時に tag ファイルを作成してくれる
set path+=$LIBSTDCPP
set path+=$BOOST_LATEST_ROOT

" neocomplcache が作成した tag ファイルのパスを tags に追加する
function! s:TagsUpdate()
    " include している tag ファイルが毎回同じとは限らないので毎回初期化
    setlocal tags=
    for filename in neocomplcache#sources#include_complete#get_include_files(bufnr('%'))
        execute "setlocal tags+=".neocomplcache#cache#encode_name('tags_output', filename)
    endfor
endfunction


command!
    \ -nargs=? PopupTags
    \ call <SID>TagsUpdate()
    \ |Unite tag:<args>

function! s:get_func_name(word)
    let end = match(a:word, '<\|[\|(')
    return end == -1 ? a:word : a:word[ : end-1 ]
endfunction


" カーソル下のワード(word)で絞り込み
noremap <silent> g<C-]> :<C-u>execute "PopupTags ".expand('<cword>')<CR>

" カーソル下のワード(WORD)で ( か < か [ までが現れるまでで絞り込み
" 例)
" boost::array<std::stirng... → boost::array で絞り込み
noremap <silent> G<C-]> :<C-u>execute "PopupTags "
    \.substitute(<SID>get_func_name(expand('<cWORD>')), '\:', '\\\:', "g")<CR>

[使い方]

:PopupTags {関数名}

または、g でカーソル下の単語を渡します。


neocomplcache が現在のバッファと include しているヘッダーファイルの tag ファイルを作成してくれるのでそれを利用します。
これで明示的に ctags コマンドを使用する事がなくなります。
あとは unite-tag が tag の出力処理を行ってくれるので、関数名を unite-tag に渡して unite.vim に出力します。
include しているヘッダーファイルのみ出力するので素晴らしいですね。


また unite-tag は最初に非同期でキャッシングを行うの出力されるまでに時間がかかります
上記の設定の場合の時に g で出力が行われなかった場合 :Unite tag でキャッシングが終わるまで待って下さい。
ちなみに neocomplcache の場合は :NeoComplCacheCachingInclude で再キャッシングを行います。
最初の tag ファイルの作成は結構重い処理なので(特に Boost 等を使用する場合は)注意して下さい。


また、この時に同名の関数も一緒に出力されてしまいますが、ファイル名等も出力されるので、まぁ問題ないかな。
もしくは、

" : は \: でエスケープ
Unite tag:boost\:\:array

のように namespace 名を一緒に渡すことで boost::array が絞りこまれます。

[:PopupTags array]

[:Popuptags boost\:\:array]


上記の Vim script であれば G で同様の事が出来ます。

[注意]

同じファイルに同名の関数が存在する場合、名前空間で区切ってもうまく列挙がされないことがあります。

[exmaple]

namespace hoge{

void
func(){}

}  // namespace hoge


namespace foo{

void
func(){}

}  // namespace foo

int
main(){
    // Unite tag:foo\:\:func すると hoge::func が列挙される
    return 0;
}

上記の場合であれば、

:Unite tag:func

といった感じで関数名だけを渡せば foo::func と hoge::func が出力されます。