Vim で C++ の宣言箇所へ飛ぶ
上記のサイトで初めて知ったんですが、llvm/clang に含まれている c-index-test という tools を使用すれば C++ で宣言位置を取得する事が出来るみたいなので、Vim script で簡単に実装してみました。
c-index-test.exe は、llvm/clang をビルドすれば出力されています。
clang 3.1(trunk) でテストしましたが多分他のバージョンでも問題ないと思います。
(ただし、C++11 のコードの場合は clang のバージョンに依存するので注意して下さい。
[動作環境]
- clang
- c-index-test
- llvm/clang をビルドすれば生成されます
- neocomplcache
- インクルードされているヘッダーファイルを取得するために使用
[インクルードディレクトリ]
必要なインクルードディレクトリは set path に追加して下さい。
s:cpp_declared_options に直接 -I を書いても問題ありませんが、その際は neocomplcache 側の設定もおこなって下さい。
[ソース]
" c-index-test.exe のパス let s:cpp_declared_c_index_test_cmd="c-index-test.exe" " c-index-test のオプション let s:cpp_declared_options = " -std=gnu++0x " " 1 にすると c-index-test.exe の結果をコマンドに出力します let s:cpp_declared_debug=0 function! s:c_index_test(filename, lnum, col) let file = a:filename let file_path = fnamemodify(a:filename, ":p") let options = join(map(split(&path, ","), "'-I'.v:val"), " ") let cmd = s:cpp_declared_c_index_test_cmd." -cursor-at=".file_path.":".(a:lnum).":".(a:col)." ".options." ".s:cpp_declared_options." ".file return system(cmd) endfunction function! s:parse_declared_pos(str) let line = a:str let mx='\(\f\+\):\s*\(\d\+\):\s*\(\d\+\)' let l = matchstr(line, mx) let file = substitute(l, mx, '\1', '') let lnum = substitute(l, mx, '\2', '') let col = substitute(l, mx, '\3', '') if empty(lnum) || empty(col) return {} endif return { "lnum" : lnum+0, "col" : col+0 } endfunction function! s:cpp_declared(filename, lnum, col) let result = s:c_index_test(a:filename, a:lnum, a:col) if s:cpp_declared_debug echo result endif let declared_pos = s:parse_declared_pos(result) if empty(declared_pos) return {} endif let func_name = expand("<cword>") let includes = neocomplcache#sources#include_complete#get_current_include_files() for header in includes+[a:filename] let lines = readfile(header) if len(lines) >= declared_pos.lnum \ && len(lines[declared_pos.lnum-1]) >= declared_pos.col \ && match(lines[declared_pos.lnum-1], func_name, declared_pos.col-1) == declared_pos.col-1 return { \ "funcname" : func_name, \ "lnum" : declared_pos.lnum, \ "col" : declared_pos.col, \ "filename" : header \ } endif endfor return {} endfunction function! s:cpp_declared_open(open_cmd, filename, lnum, col) echo "Search declared..." let declared = s:cpp_declared(expand("%"), getpos(".")[1], getpos(".")[2]) if empty(declared) echo "Not found" return endif if a:filename != declared.filename execute a:open_cmd." ".declared.filename endif call cursor(declared.lnum, declared.col) endfunction " コマンド " 自分が使いやすいように open_cmd を追加したりして下さい command! -nargs=0 CppDeclaredOpen :call <SID>cpp_declared_open("buffer", expand("%"), getpos(".")[1], getpos(".")[2]) command! -nargs=0 CppDeclaredOpenTabDrop :call <SID>cpp_declared_open("tab drop", expand("%"), getpos(".")[1], getpos(".")[2]) " Visual Studio っぽいキーマッピング nnoremap <silent> <F10> :CppDeclaredOpenTabDrop<CR>
とりあえず、生のソースコードです。
vimrc あたりにコピーして使用して下さい。
:CppDeclaredOpen でカーソル下の単語の宣言位置を開きます。
動作は多少遅いですが、精度はそこそこ。
まだコードは荒削りなので、そのうちちゃんとした Vimプラグインとして書きなおす予定。
と、いうか c-index-test で宣言の位置は出力されるんだけど宣言のヘッダーファイル名は出力されないんですかね…。
[注意]
コンパイルが通るソースコードでなければ動作しません。
例えば、
func( // これだけで func の位置へは飛べない
ではコンパイルに失敗してしまい動作しないので引数までちゃんと定義する必要があります。
let s:cpp_declared_debug=1
で、c-index-test の出力結果を確認する事が出来るので、動作しない場合はこれで確認をおこなって下さい。
その他、わからないことがあればコメントか Twitter までお願いします。
あと今気づいたけどプリプロセッサを使って関数を生成している場合だと検索に引っかからないので動作しないですね…。
うーん、どうしようかな。