Vim
Linux
vimrc
ctags

vimに, ctagsを導入して, タグジャンプを華麗に(かろうじて)使えるようにした話

vimでも, タグジャンプを使えるようになりたい!

ということで, vim初心者が, ctagsを導入して, タグジャンプを華麗に(かろうじて)使えるようにするための設定を紹介しようと思います.


@h_east さんのコメントがありました.

いまは, exuberant-ctagsは, もうすでに, メンテナンスが終わって, 更新は途絶えています.

ココ最近できた言語に対応していません(go言語とか).

なので, もともとexuberant-ctagsのforkしてできたプロジェクトuniversal-ctagsをインストールすることをおすすめします.

universal-ctagsは, いまもメンテナンスが行き届いています.

最近の記事で, 古い技術を伝えてしまって, 申し訳ないですが, 指摘されることで, いろいろ知らない知識がわかってすごい助かります.

ここで, 感謝の意を示させていただきます.

まさかりLOVE

さて, universal-ctagsのインストール方法は別の記事にします.

aptでは, インストール出来ないので, universal-ctagsのgitHubに行って, buildするためのプログラムを落としてこないといけません.

まだexuberant-ctags使ってるやつちょっと来い(自戒) universal-ctagsのインストール方法でインストールの仕方を紹介しました(Linuxだけですが).


環境

OS

OS: Windows 10

Windows Subsystem for Linux (WSL)で, Ubuntu 16.04を動かしています.

vimのバージョン

VIM - Vi IMproved 7.4 (2013 Aug 10, compiled Nov 24 2016 16:44:48)
Included patches: 1-1689
Extra patches: 8.0.0056

ctagsの導入

インストール

自分の環境では, ctagsが, インストールされていなかったです.

$ ctags --version
The program 'ctags' is currently not installed. You can install it by typing:
sudo apt install exuberant-ctags

なので, エラーメッセージの案内に従って, sudo apt install exuberant-ctagsを叩いて, インストールします.

$ sudo apt install exuberant-ctags

exuberant-ctagsは, ctagsの進化版と思っておけば大丈夫でしょう.

ワシントン大学のCS(Computer Science)のctagsの導入の授業のレジュメにも, 無印のctagsじゃなくて, exuberant-ctagsを使えと書いてあります(超意訳).

If you are not on forkbomb or attu, make sure that the system you are using has "Exuberant Ctags" installed, rather than the original "Ctags," by running ctags --version.

ちなみに exuberantは, full of energyとかcheerfulとかという意味なので, まあより使いやすいんだろうなとはイメージできると思います.

インストールしたら, 確認のために,

$ ctags --version
Exuberant Ctags 5.9~svn20110310, Copyright (C) 1996-2009 Darren Hiebert
  Addresses: <dhiebert@users.sourceforge.net>, http://ctags.sourceforge.net 
  Optional compiled features: +wildcards, +regex

ちゃんとインストールされていますね.

tagsファイルの作成

tagsという名前のtxtファイルで, どこに飛ぶかなどを定義しています.

これを作成します.

まず, プロジェクトのルートディレクトリに行きましょう.

今回は, 擬似的にプロジェクトを作成します.

tag_jump_projectというルートディレクトリを作ります.

その中に, srcディレクトリとlibディレクトリを作ります.

$ mkdir -p tag_jump_project/src tag_jump_project/lib

今回は, PHPで書きます.

自分の好きな言語で書けばいいと思います(オブジェクト指向言語がいいかも).

src/tag_jump.phpでは, lib/util.phpという名前の関数群を定義したphpファイルからhelloという関数をincludeして, hello()を呼び出すプログラムです.

src/tag_jump.php
<?php
include "lib/util.php";
hello();
lib/util.php
<?php
function hello() {
    echo "hello", PHP_EOL;
}

保存しましょう.

そして, プロジェクトのルートディレクトリ(./tag_jump)に行きましょう.

そして, ctags -R *を叩きます.

$ ctags -R *

$ ctags -R

@h_east さんからご指摘いただきましたが, *は不要です(ご指摘ありがとうございます).

-Rオプションは, recursivelyの意味で, 再帰的にディレクトリをたどって, tagをつけてくれます.

これで, プロジェクトのルートディレクトリ全部のファイルを走査してタグが付きました.

ルートディレクトリに, tagsファイルがあるので, ルートディレクトリから, vimを起動しましょう.

image.png

自分の場合は, NERDTreeを入れているので, 左側に, ファイラーが出ます.

srcを押して, tag_jump.phpを開いてみましょう.

image.png

そして, hello()にカーソルを乗せた状態で, Ctrl + ]を押しましょう.

Boooon

一瞬で, lib/util.phphelloを定義した関数のところに移動しました.

image.png

便利なコマンド

ただ, このctagsは, そこまで頭が良くなくて, 継承したクラスファイル先やインポート, インクルードしたファイル先だけでなく, プロジェクトルートにあるファイルを読み込みこんでしまいます.

だから, 同じ名前ものすべてにマッチしてしまいます(インターフェースのメソッドで, そのメソッドの実装がいろんなファイルで, 実装されていると, すべてマッチする. 自動で, 実装先を特定してくれない).

なので, 複数候補がある場合は, 第一候補のページに移ります.

:tnで, 次の候補, :tpで前の候補, で移動します.

:tsで候補を一覧することも可能です.

:ts <tag>tagの名前を検索できます.

この例では, :ts helloと叩くと, helloが定義されている場所をリスト化してくれます.

そこで選択すると, ジャンプしてくれます.

tsは, tab selectの略です.

tj(ump) <tag>は, もっと便利で, 候補が複数ある場合だけ, リスト化して, 一つしか候補がない場合は, 直接ジャンプしてくれます.

また, Ctrl + ] の代わりに, Ctrl + w }で, プレビュー画面で表示してくれます(水平分割で, 開いていたバッファは, 裏に隠れない).

ほかにも g Ctrl + ]を叩くと, 候補がある場合, 第一候補ではなく, 候補のリストを表示します.

一度, ジャンプしたあとに, Ctrl + tでジャンプ前のところに戻れます.

もっと便利に

tagsファイルは, 新しいメソッドや変数を追加しても, 更新されません.

なので, いちいちctagsコマンドを叩かないと, tagsファイルは更新されず, 新しく定義した関数などは, ジャンプしてくれません.

面倒ですよね?

あります, 自動でtagsを更新してくれるプラグインが.

vim-tags

vim-tagsで, インストールできます.

pathogenが入っている前提でやります.

cd ~/.vim/bundle
git clone https://github.com/szw/vim-tags.git

を叩いて終わりです.

あとはファイルが保存されるごとに, tagsファイルが更新されます.

または, :TagsGenerateを叩けば, 更新されます.

vimから離れず, tagsファイルを更新できて便利ですね.

便利なマッピング

Ctrl + ]は, 複数の候補があっても, 第一候補に自動で移動してしまいます.

g Ctrl + ]を使うと, 候補が一つの場合だけ, 一気に移動して, 候補が複数の場合は, リストを表示してくれます.

なので, マッピングしましょう.

.vimrcに以下を追加しましょう.

nnoremap <C-]> g<C-]>

ctagsをちゃんと使うからお借りしました.


nnoremapの意味は, 3つに分解して考えるとわかりやすいです.

n NORMAL MODEのマッピング
nore not recursivelyという意味

map mappingするよって意味

not recursivelyというのは, 再帰的じゃないという意味です.

aというキーに, bというキーをマッピングしたとましょう.

またほかの設定で, bというキーに, cというキーをマッピングしてたとします.

recursiveだと, aを押したら, bを意味して, bには, cがマッピングされているから,

aを押したら, cが押されたことを意味します.

これを禁止するのが, noreの意味です.


また, 移動した先に画面がすべて持っていかれるのが嫌!

分割して表示して!という方には,

Ctrl + w }

これは, 第一候補に一気に飛んでしましますし, tsを叩くとわかりますが, 候補が表示されません.

または,

以下の

nnoremap <C-h> :vsp<CR> :exe("tjump ".expand('<cword>'))<CR>
nnoremap <C-k> :split<CR> :exe("tjump ".expand('<cword>'))<CR>

.vimrcに追加するといいでしょう.

Vimスクリプトで軽やかにタグジャンプしようからお借りしました.

<C-h><C-k>の部分は, 自分の好きなものに変えて大丈夫です.


exeとかexpandとか<cword>とかなんだよって方.

exeは, ()を一度, 変換してから, コマンドを実行します.

tjumpは前回説明したとおりで, は, いまカーソルにいる単語を意味します.

例えば, いま, hello()関数のところにカーソルがあるとします.

すると, expand(<cword>)は, helloに変わります.

そして, "tjump "と"hello"が.で結合して, "tjump hello"というコマンドに変わり, 実行されます.

はEnterを意味します.

これで, helloのところに移動します.


ここらへんまで設定できれば, まあまあvimで華麗にジャンプできるんじゃないかなと思います.

参考文献

Ctags Tutorial

ctagsをちゃんと使う

Vimスクリプトで軽やかにタグジャンプしよう