このエントリーは、aratana Adventカレンダー13日目のエントリーです。
はじめまして!!
新卒一年目、Vim一年生の田村です。
前日は、@mt-kageさんの「はじめてのServerless ✕ Webpack ✕ TypeScript」というエントリーでした。
サーバーレスと聞いて、「サーバーはいらないけど、筋肉は必要」という迷言を思い出したのは私だけでしょうか。
私日々のコーディングは、Vimで行っており、操作や動作などで不満が出れば、解決しvimrcを育成しております。(vimrc Breederってやつです。聞いたこと無い)
コードの処理を追っている中で、必ずと言ってもよいほど利用する定義元へのジャンプ。
主要言語はだいたいプラグインを導入すればジャンプできますが、
マイナー言語のほとんどに、プラグインが作られておらず、定義元ジャンプができません。
grep使えばいいじゃんって声も聞こえて来そうですが、色々工程省ける定義元ジャンプのほうが(ry...
そこで、定義元ジャンプを使えるようにするCtags
を利用していきます。
やること
- 標準で対応していない定義元ジャンプの設定
- Vimで定義元ジャンプを実感!!
- 楽するためのVim設定(関数とか書いてみる)
事前準備
- Vim(まー標準で入っているでしょう)
- Ctags(パッケージマネージャーなどで入れましょう)
- ソースコードNaspSampleをダウンロード(DL直リンク)
- 解凍したNaspSampleディレクトリにいる状態 In 黒い画面
- あ、対象は、Linux, Macとなっております(Windows翻訳できる方を除く)
- ちょっとVimを触れる
ちなみに、Ctagsは
の2種類あります。
exuberant-ctags
の方は、更新が途絶えており、universal-ctags
は、現在もメンテナンスされ続けています。
違いとしては、設定ファイルの場所とか、対応言語などです。
やっぱりオススメは、メンテナンスされているuniversal-ctags
ですね。
Macなら、
$ brew install ctags
$ brew install --HEAD universal-ctags/universal-ctags/universal-ctags
universal-ctags
の主張が激しい。
事前vimrc
$HOME/.vimrc
ファイルに以下を追加設定しておいてください。
(文字コードを正しく認識して、動作不良を起こさないため)
※ 既に設定済みって方は不要です。
setfileformats=unix,dos,macsetfileencodings=utf-8,sjis
Ctagsというすごいやつ
Ctagsは、ソースファイルを解析してインデックスファイルを作成するプログラムです。
簡単に言い換えれば、関数や変数などの定義元へジャンプするためのファイルを作成するプログラムです。
標準でいくつかのプログラム言語に対応しています。
Ctagsを使ってみよう
実際に標準のCtagsを使ってみましょう!!
NaspSampleディレクトリで以下のコマンドを実行してから、
インデックスファイル(以後タグファイル)を作成できます。
$ ctags -R -f .tags
(.tags
ファイルをエディタで開いて中身確認しても良いかも)
コマンド解説
ctags
でctags
実行。(そのまんま)-R
は、現在ディレクトリから再帰的にファイルを解析していくオプション指定。 つまり、全ファイル見ますモード。-f .tags
が出力ファイル名を設定するオプション。(デフォルトは、tags
)
たった、これだけで便利な機能が使えるようになっちゃいます。
Vimでジャンプしてみる
では、先程作成したタグファイルを元にVimで定義元ジャンプを実感しましょう!!
やり方
vim script/@ImgList.asp
で該当ファイルをVimで開く:set tags=.tags
でタグファイルのパスを指定して50gg
OR50G
で、50行へ飛ぶ- 文字列
rst
にカーソルが乗った状態で、Ctrl-]
でジャンプします。
定義元のdim rst:
へ移動していると思います。
(そんなんgd
でもいけるやんは、聞こえなかったことにします)
正直このレベルでは、大したことないですね〜。
標準の限界
今回、ジャンプさせたい定義元を確認してみます。script/@ImgList.asp
ファイルの28行(つまり、28gg
OR 28G
)
function OnDraw_Images(oNasp, vParam)
呼び出し元も確認してみます。wwwroot/ImgList.asp
の23行
(#%Images|
です。ここで、OnDraw_Images
を呼び出しています。
Vimを一回落としている場合は、:set tags=.tags
をしましょう。
Images
にカーソル合わせて、Ctrl-]
でジャンプ!?
ん?なんかエラー出ていますね。E426: tag not found: Images
Imagesというタグ無いですよって言われています。
Ctagsの標準では対応していないようですね。
では、Ctagsの設定をカスタマイズして飛べるようにしましょう!!
Ctagsカスタマイズ
Ctagsの設定ファイルを変更して、カスタマイズできます。
Ctagsの設定ファイルは、
- exuberant-ctagsの場合は、
$HOME/.ctags
- universal-ctagsの場合は、
$HOME/.ctags.d/configure.ctags
※configure
部分はなんでも良いっぽいです。
となっています。インストした方に合わせましょう。
考えられる解決方法は、
OnDraw_Images
をタグファイルへ登録Images
とOnDraw_Images
を関連付ける
Ctags設定追加
では、設定ファイルに以下の一行を追加しましょう。
--regex-asp=/^[ \t]*(function)[ \t]+OnDraw_([a-zA-Z0-9_]+)/\2/r,ondraw/
この処理の意味をまとめると、
asp
という言語に対してパターン追加function OnDraw_hogehoge
に一致する行をhogehoge
としてタグファイルに追加ondraw
というタグ種類で登録(識別子r
)
\2
は、([a-zA-Z0-9_]+)を指しており、つまりhogehoge
です。
これで、OnDraw_Images
の箇所をタグファイルへ登録でき、Images
で関連付け完了しました。
カスタマイズ後の確認
では、先程と同じことしてジャンプできるか確認してみましょう。
ctags -R -f .tags
でタグファイル更新vim wwwroot/ImgList.asp
:set tags=.tags
23gg
で23行へ飛ぶImages
上でCtrl-]
でジャンプ!!
パっと変わったと思いますパっと!!
ちなみに、Ctrl-t
で元に戻れます。
Vimのタグジャンプは、スタックとして履歴残る感じなので、Ctrl-]
でpushして、Ctrl-t
でpopする的な感じです。
無事ジャンプさせることができましたね!!
これを応用していけば、飛ばしたい場所に自由に飛べます。
IDEにも劣らぬジャンプ機能を手に入れることができました!!
ちなみに
インサートモードで、<Ctrl-x><Ctrl-]>
を入力すると、タグリストの補完できます。
例えば、OnDraw
まで入力した後に、<Ctrl-x><Ctrl-]>
で、補完一覧がでてきます。
設定次第では、WordPressのアクションフック名の補完もできたりします。
問題点
ただ、いくつか面倒なところがありますね。。。
- Vimを立ち上げる度に
:set tags=.tags
- ソースコードを変更する度に、タグファイル生成コマンド実行
さらに、極めつけには、ターミナル上でwwwroot
へ移動した後、VimでImgList.asp
を立ち上げ、同じようにすると、ジャンプ出来ません。set tags=.ctags
と記入しているので、カレントディレクトリ直下の.tags
を探しており、wwwroot
直下には、見つからないのでこのような現象になっています。こんなの使い物にならないじゃん!!
課題
とりあえず、現状の課題をまとめて見ました。
- Vim立ち上げるたび
set tags=.tags
- タグファイルがあるディレクトリで、Vim立ち上げないとタグファイル読み込めない
- ソース変更するたび、手動でインデックスファイル作り直し
では、これらすべて解決しましょう!
課題の解決
課題に対して一つずつ解決していきます!
vimrc設定
$HOME/.vimrc
に記述すれば、Vim立ち上げ時に読み込まれるので、.vimrc
に、set tags=.tags
を追加しましょう。
これで、立ち上げと一緒に実行されます。
が、しかし、カレントディレクトリにタグファイルがないといけません問題。
解決できそうな動作としては、
親ディレクトリを遡り、.tags
があるか探してから、あればそれを読み込む的な動作ができれば完璧ですね。
この動作は、Vimの標準でUpward search
あるので、それ使いましょう。
詳しくは、:h file-searching
の項目に書いてます。
以下の記述で、カレントディレクトリから、ホームディレクトリまで.tags
を探します。便利〜。
settags=.tags;$HOME
OR
settags=.tags;~
に変更しましょう。お好きな方で。
自動コマンド設定
次は、毎回タグファイル作るの面倒問題。
Vimには、指定イベントが行われたら自動的に実行されるコマンドを指定できる、autocmd
なるコマンドがあります。
これ使いましょう。
augroup ctags
autocmd!
autocmd BufWritePost * silent!ctags -R -f .tags
augroup END
augroup
とautocmd!
は、簡単に説明すると、複数回autocmd
が登録されるのを防ぐやつです。 詳しくは、おさらい autocmd/augroup- ファイル書き込み後に
silent !ctags -R -f .tags
実行しますという意味 silent
は、メッセージ出力させないよ。出力いらない。!ctags -R -f .tags
先頭にビックリマークで、外部プログラム実行するという意味
さらなる問題とその解決
これで、保存するたびインデックスファイル更新されるーっと思ったけど、
カレントディレクトリが変わったら、その直下に.tags
できちゃう問題発生。
これじゃ、ディレクトリ移動して保存するたびに.tags
生まれちゃう!!
安心して作業できませんね。
読み込んでる.tags
ファイルパスと同じ場所で生成できるようにしたいですね。
てことで関数作りました。
function! s:execute_ctags() abort " 探すタグファイル名let tag_name ='.tags' " ディレクトリを遡り、タグファイルを探し、パス取得let tags_path = findfile(tag_name,'.;') " タグファイルパスが見つからなかった場合if tags_path ==# ''returnendif " タグファイルのディレクトリパスを取得 " `:p:h`の部分は、:h filename-modifiersで確認let tags_dirpath = fnamemodify(tags_path,':p:h') " 見つかったタグファイルのディレクトリに移動して、ctagsをバックグラウンド実行(エラー出力破棄)
execute 'silent !cd' tags_dirpath '&& ctags -R -f' tag_name '2> /dev/null &'endfunction
これを、保存毎に実行するようにすれば良いので、autocmd BufWritePost * call s:execute_ctags()
に変更します。
これで、快適にタグジャンプ生活を送れます。
現状のままだと、execute_ctags
関数は、.ctags
無かったら何もしないようになっています。
解決方法として、.ctags
が見つからなかった場合、
- プロジェクトのルートに自動生成するようにする(
.git
と同じ階層とか) - カレントディレクトリに生成するようにする(以降、生成したタグファイルを更新)
など、自分好みに改良していただければと思います。
.vimrc
setfileformats=unix,dos,macsetfileencodings=utf-8,sjissettags=.tags;$HOMEfunction! s:execute_ctags() abort " 探すタグファイル名let tag_name ='.tags' " ディレクトリを遡り、タグファイルを探し、パス取得let tags_path = findfile(tag_name,'.;') " タグファイルパスが見つからなかった場合if tags_path ==# ''returnendif " タグファイルのディレクトリパスを取得 " `:p:h`の部分は、:h filename-modifiersで確認let tags_dirpath = fnamemodify(tags_path,':p:h') " 見つかったタグファイルのディレクトリに移動して、ctagsをバックグラウンド実行(エラー出力破棄)
execute 'silent !cd' tags_dirpath '&& ctags -R -f' tag_name '2> /dev/null &'endfunction
augroup ctags
autocmd!
autocmd BufWritePost * call s:execute_ctags()
augroup END
ちなみに...
Tagbarというプラグインを入れると、右とかにタグ登録されたリストが表示され、選択することでその場所へ飛べます。
つまり、CtagsとTagbarの設定をカスタマイズすることで、表示させたいリストを自分好みに変更することができるようになります。
CSSなら、セレクタ一覧とか。
さらに画像の左下に、現在カーソルがどこの関数に含まれるか表示させることもできます。
便利ですね。
最後に
思っていたよりもたくさん書いてしまいました。
CtagsとVimの素晴らしさを少しでも感じて頂けましたでしょうか。
Ctagsをカスタマイズして快適なVimジャンプライフを送りましょう。
これからも、さらにvimrcを育成していきます。
明日(14日目)のaratana Advent Calendar 2017のエントリーは、期待の新人@sakochiさんのエントリーです!お楽しみに!