GNU GLOBALでgolangの関数呼び出し行にタグジャンプする

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
~~~

![gogtags_screenshot1.gif](https://qiita-image-store.s3.amazonaws.com/0/69350/68173ec2-6509-f024-a4f4-bc88ef0d5153.gif)


生成されたタグを参照するときは普通の使い方でOKです。globalコマンドの出力を使うエディタの拡張は、GTAGSなどのファイル形式は変わっても影響ありません。（helm-gtagsは大丈夫でした)

![gogtags_screenshot.gif](https://qiita-image-store.s3.amazonaws.com/0/69350/22ab7214-9da3-9a90-8245-134b5507c6e7.gif)


## 他の方法もある

「[GNU GLOBALの対応言語を大幅に増やすPygmentsパーサーを導入する](http://qiita.com/yoshizow/items/9cc0236ac0249e0638ff)」の方法があることは知っていました。が、使うにはいろいろ下準備が必要そうなのと、「[抽象構文木（AST）をトラバースする #golang](http://qiita.com/tenntenn/items/f029425a844687a0e64b)」、「[ASTを取得する方法を調べる #golang]()」をみたら、golangだけで簡単にできそうなことが分かったので作ってみました。

[Golang用のctags-compatibleの実装](https://github.com/jstemmer/gotags)はすでにありました。GLOBALなのに世界的にはマイナーなのでしょうか。

## 参考：GNU GLOBALのフォーマット

フォーマットの説明はソースコードのコメントが一番詳しかったです。これはformat version 6の説明で、Standard formatがGTAGS用、Compact formatがGRTAGS用です。
データベースはデフォルトでBerkeley DBが選択され、最近のバージョンだとsqlite3が使えるようになっています。また、GSYMSは5.9から作られなくなっています（関数呼び出し以外のシンボルの情報もGRTAGSに含まれています）。

~~~:libutil/gtagsop.c
 * [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`してみます。

~~~c:main.c,main2.c
     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でした。

~~~:GTAGS
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には関数呼び出しと変数の定義、参照が含まれています。

~~~: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はバージョンが独立しているようです。

~~~: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
~~~