はじめに
vimユーザの皆さん、ctagsって知ってますか?使ってますか?
ctagsを知らない人は前提知識を読んでみて下さい。
ctags。とても便利ですが毎度毎度タグファイルを生成したり更新したり削除したりするのってめんどくさいですよね。中には何かしらの設定をしたりプラグインを入れたりでこの煩わしさを解決している人もいるのではと思います。私は最初は「alpacatags」というプラグインで解決しようとしたのですが上手く行かなくて、色々やっているうちに私なりの解決方法を見つけたので紹介しようと思います。
本記事で紹介する解決方法を簡単に説明すると、
- 起動時に*/tmp*にタグファイル生成(この時のタグファイル名にプロセスIDの情報を加える)
- 起動時にタグファイルのパスを設定
- バッファ更新時にタグファイル更新
- 終了時に*/tmp*以下のタグファイル削除
この方法で、タグファイルの事を意識することなく定義元にジャンプできるようになります。
前提知識
ctagsというのは
コマンドラインプログラムです。このツールはソースコード中のオブジェクト(関数とか変数とか)の索引(タグファイル)を生成することができ、生成したタグファイルはテキストエディタやその他のユーティリティツールで目的のオブジェクトを素早く見つける際に利用されます。ちなみに「テキストエディタ」の中にはvimも含まれます。
vimで使うには
実は特に何もする必要はありません。vimではデフォルトでctagsを利用できるようになっていて、タグファイルがあるパスでvimを起動すれば良いだけです。ctagsには色々なオプションがありますが、私はctags -R
を多用しています。RはrecurciveのRです。とりあえずこれだけ覚えているだけでも何かと役に立つかと思います。さて、
実際に使ってみましょう。
sampleというディレクトリを作って、その中に以下のファイルを作ってみて下さい。まあ言語も中身も何でも良いんですが。
def say_hello(name):
print("hello! " + name)
def say_bye(name):
print("bye! " + name)
import greet
me = "Sho"
you = "Hogesuke"
greet.say_hello(me)
greet.say_hello(you)
greet.say_bye(me)
greet.say_bye(you)
そしてsampleに移動してctags -R
を実行して下さい。tagsというファイルが生成されてますね?(どんな情報が含まれているのか知りたければctags -R -x
を実行してみて下さい。)
次に、sample.pyを起動して、say_helloの上にカーソルを移動して、Ctrlを押しながら]を押すとsay_helloの定義元にジャンプすると思います。
本題
まず、私がvimとctagsを動かしている
環境
ですが、以下のようになっています。
- OS: Ubuntu 18.04
- vim: vim 8.0
- ctags: ctags 5.9
「タグファイルを意識することなく」を実現するには幾つかの
課題
があります。以下に挙げます。
- ユーザはタグファイルを目にすることが無い
- vimの速度に大きな影響を及ぼさない
- いつも通りのvimの使い方で、何故か定義元にジャンプが出来るようになっている
課題1の解決
は、vimのプロセスIDを名前に含んだタグファイルを*/tmpに作成し、起動したvimのプロセス毎に対応するタグファイルを設定することで解決します。仮にvimの異常終了などで/tmpにファイルが残ったとしても、/tmp*以下のファイルはshutdown時に削除されるのでさして大きな問題はありません。
課題2の解決
ですが、タグファイルの生成時には-R
オプションを使用するので、極端な例だとルートでvimを起動すると厄介なことになります。そこで、根本的な解決ではありませんが、カレントパス内に特定のディレクトリを含んでいる時だけ自動でタグファイルを生成するようにします。私の場合はdevが含まれている時にしています。
課題3の解決
は、vimスクリプトのautocmd
とsilent
というキーワードを使うことで実現しました。
実際の
vimrc
は以下のようになります。
let g:pid = getpid()
let g:tag_file_path = "/tmp/" . g:pid . "_tags"
function! _CtagsUpdate()
exe '!ctags -R -f '.g:tag_file_path.' `pwd` &'
exe 'set tags='.g:tag_file_path
endfunction
command! CtagsUpdate call _CtagsUpdate()
function! _CtagsRemove()
exe '!rm '.g:tag_file_path
endfunction
command! CtagsRemove call _CtagsRemove()
let current_path = expand("%:p")
let match_idx = match(current_path, "/dev")
if match_idx != -1
autocmd VimEnter * silent! :CtagsUpdate
autocmd BufWrite * silent! :CtagsUpdate
autocmd VimLeave * silent! :CtagsRemove
endif
Note: /devの部分を*/hoge*にすればカレントパス中にhogeが含まれていればタグファイルを自動生成するようになります。
また、課題2の解決で特定のパスでしか自動でタグファイルを生成しなくなってしまいましたが、vimのコマンドモードでCtagsUpdateとCtagsRemoveというコマンドが使えるようになっています。
- CtagsUpdate: タグファイル("プロセスID"_tags)を*/tmp*に作成する。すでにあれば更新される。
- CtagsRemove: /tmpにあるタグファイル("プロセスID"_tags)を削除する。
なので、タグファイルが自動で生成されない場合は手動で生成することも可能です。