godefで定義元には飛べますが、関数の呼び出し箇所にも飛べると便利です。
というわけで、golangでGLOBAL互換のタグを生成するツールをつくりました。
https://github.com/juntaki/gogtags
インストール方法
コマンド分はこれだけです。
$ go get github.com/juntaki/gogtags
生成されたタグの参照にはsqlite3対応のGNU GLOBALが必要です。--with-sqlite3
のオプションを付けてビルドします。
https://www.gnu.org/software/global/
$ ./configure --with-sqlite3
$ make
$ sudo make install
使いかた
カレントディレクトリでタグ生成します。オプション-v
をつけると解析したファイル名を出力します。
$ cd <target directory>
$ gogtags -v
生成されたタグを参照するときは普通の使い方でOKです。globalコマンドの出力を使うエディタの拡張は、GTAGSなどのファイル形式は変わっても影響ありません。(helm-gtagsは大丈夫でした)
他の方法もある
「GNU GLOBALの対応言語を大幅に増やすPygmentsパーサーを導入する」の方法があることは知っていました。が、使うにはいろいろ下準備が必要そうなのと、「抽象構文木(AST)をトラバースする #golang」、「ASTを取得する方法を調べる #golang」をみたら、golangだけで簡単にできそうなことが分かったので作ってみました。
Golang用のctags-compatibleの実装はすでにありました。GLOBALなのに世界的にはマイナーなのでしょうか。
参考:GNU GLOBALのフォーマット
フォーマットの説明はソースコードのコメントが一番詳しかったです。これはformat version 6の説明で、Standard formatがGTAGS用、Compact formatがGRTAGS用です。
データベースはデフォルトでBerkeley DBが選択され、最近のバージョンだとsqlite3が使えるようになっています。また、GSYMSは5.9から作られなくなっています(関数呼び出し以外のシンボルの情報もGRTAGSに含まれています)。
* [Specification of format version 6]
*
* Standard format:
*
* This format is the default format of GTAGS.
*
* <file id> <tag name> <line number> <line image>
*
* * Separator is single blank.
*
* [example]
* +------------------------------------
* |110 func 10 int func(int a)
* |110 func 30 func(int a1, int a2)
*
* Line image might be compressed (GTAGS_COMPRESS).
* Tag name might be compressed (GTAGS_COMPNAME).
*
* Compact format:
*
* This format is the default format of GRTAGS.
* It is used for GTAGS with the -c option.
*
* <file id> <tag name> <line number>,...
*
* * Separator is single blank.
*
* [example]
* +------------------------------------
* |110 func 10,30
*
* Line numbers are sorted in a line.
* Each line number might be expressed as difference from the previous
* line number except for the head (GTAGS_COMPLINE).
* ex: 10,3,2 means '10 13 15'.
* In addition,successive line numbers are expressed as a range.
* ex: 10-3 means '10 11 12 13'.
gtagsで生成したsqlite3のフォーマットの解析
これをgtags --sqlite3
してみます。
1 #include<stdio.h>
2
3 int a = 1;
4
5 int func1(){
6 int a = 100;
7 printf("%d", a);
8 return 1;
9 }
10
11 int main(){
12 int a = 10;
13 printf("%d", a);
14 a = func1();
15 return 0;
16 }
仕様にはなかったですが、タグ名の文字列が@n
で置換されています。また、行イメージの前後の空白は除去するようです。
GTAGSには関数の定義が含まれていて、datカラムがformat version6に従って格納されています。keyはタグ名でextraはファイルIDのようです。型は全部textでした。
juntaki@ubuntu ~/g/g/test> sqlite3 -header -column GTAGS "select * from db;"
key dat extra
------------ ----------------------------- ----------
__.COMPRESS __.COMPRESS ddefine ttypedef
__.COMPNAME __.COMPNAME
__.VERSION __.VERSION 6
func1 1 @n 5 int @n(){ 1
main 1 @n 11 int @n(){ 1
func1 2 @n 5 int @n(){ 2
main 2 @n 11 int @n(){ 2
GRTAGSには関数呼び出しと変数の定義、参照が含まれています。
sqlite> select * from db;
key dat extra
--------------- --------------- ----------
__.COMPACT __.COMPACT
__.COMPLINE __.COMPLINE
__.COMPNAME __.COMPNAME
__.VERSION __.VERSION 6
a 1 @n 3,3-1,5-2 1
func1 1 @n 14 1
printf 1 @n 7,6 1
a 2 @n 3,3-1,5-2 2
func1 2 @n 14 2
printf 2 @n 7,6 2
ファイルIDからファイル名と、その逆のエントリがあります。
GPATHはバージョンが独立しているようです。
juntaki@ubuntu ~/g/g/test> sqlite3 -header -column GPATH "select * from db;"
key dat extra
----------- ------------- ----------
__.VERSION __.VERSION 2
./main.c 1
1 ./main.c
./main2.c 2
2 ./main2.c
__.NEXTKEY 3