Emacsで関数ジャンプ(gtags)＆pygments-parserを使用して色々な言語で関数ジャンプ

Emacs で関数ジャンプを行うために `gtags` を導入した話です。
また `global-pygments-plugin` を使って `gtags` が対応していない言語でも関数ジャンプをやってみた話です。

記事の中では `gtags` のインストール後に `Pygments` を導入しますが、
実際にやってみたら `Python` や `Pygments` を先にインストールしておかないと、
`gtags` の `./configure` でちゃんと設定が行われないようです。ご注意ください。


# 環境

- Ubuntu 14.04

# gtags を試す

`gtags` でソースコードの関数ジャンプをできるようにする
`gtags` は C/C++, Java, PHP などしか対応していないらしい
※まずは通常の `gtags` で色々試してみます。
※後述で `global-pygments-plugin` を適用して対応言語を増やしてみます。

ということで、

1. 事前準備
    2. gtags のインストール(ソースコードから)
    3. Emacsからgtagsを使えるようにする
2. 実践
    3. C++ で試してみる
    4. Java で試してみる

こんな感じで進めていきます。

## 事前準備

### gtags のインストール(ソースコードから)

https://www.gnu.org/software/global/download.html
2016/08/10現在: 6.5.4 が最新

```
$ wget http://tamacom.com/global/global-6.5.4.tar.gz
$ tar -zxvf global-6.5.4.tar.gz
$ cd global-6.5.4
$ ./configure
$ make
$ make install
```

```
$ which gtags
/usr/local/bin/gtags
```

#### ./configureで発生した問題

```bash
configure: checking "location of ncurses.h file"...
configure: error: curses library is required but not found.
If you are not going to use gtags-cscope, please try ./configure --disable-gtagscscope
```

なんか無いよ。とな。

```bash
$ sudo apt-get install ncurses-dev
```

上記でライブラリをインストールしてから再度 `./configure` で問題なくOK

### Emacsからgtagsを使えるようにする

#### gtags.elのパスを通す

```
(add-to-list 'load-path "/usr/local/share/gtags")
```

恐らく上記にあるはず・・・

#### .emacsの設定を追加

```:.emacs
(autoload 'gtags-mode "gtags" "" t)
(setq gtags-mode-hook
    '(lambda ()
        (local-set-key "\M-t" 'gtags-find-tag)    ;関数へジャンプ
        (local-set-key "\M-r" 'gtags-find-rtag)   ;関数の参照元へジャンプ
        (local-set-key "\M-s" 'gtags-find-symbol) ;変数の定義元/参照先へジャンプ
        (local-set-key "\C-t" 'gtags-pop-stack)   ;前のバッファに戻る
        ))
```

#### とりあえず `c-mode` と `c++-mode` の時に `gtags-mode `に入るようにする

```:.emacs
(add-hook 'c-mode-hook 'gtags-mode)
(add-hook 'c++-mode-hook 'gtags-mode)
```

## 実践

### C++ で試してみる

https://github.com/chibi929/hello-gtags
上記の `Cpp` ディレクトリで試しています

#### ソースコードタグの準備

```bash
$ git clone git@github.com:chibi929/hello-gtags.git
$ cd hello-gtags/CppProject
$ gtags -v
[Thu Aug 11 01:01:24 JST 2016] Gtags started.
 Using default configuration.
[Thu Aug 11 01:01:24 JST 2016] Creating 'GTAGS' and 'GRTAGS'.
 [1] extracting tags of Sample.cpp
 [2] extracting tags of Sample.h
 [3] extracting tags of main.cpp
[Thu Aug 11 01:01:24 JST 2016] Done.
```

#### ジャンプ実行

##### 例1-1(gtags-find-tag)

![20160811011006.png](https://qiita-image-store.s3.amazonaws.com/0/53393/522c10f2-a56f-f696-4544-c740a08c9c1a.png)

上記では `main.cpp:L11: hoge()` にカーソルを合わせて `M-t` を実行しています。
`Find tag: (default hoge)` となっており `Enter` を押すと `main.cpp:L4: void hoge()` に飛びます。

##### 例1-2(gtags-find-tag)

![20160811011059.png](https://qiita-image-store.s3.amazonaws.com/0/53393/ad8a85b0-84b7-ab76-aa46-7e2c6c096f0c.png)

上記では `main.cpp:L13: sample.test()` にカーソルを合わせて `M-t` を実行しています。
`Find tag: (default test)` となっており `Enter` を押すと `Sample.cpp:L12: void Sample::test()` に飛びます。

##### 例2(gtags-find-rtag)

![20160811011654.png](https://qiita-image-store.s3.amazonaws.com/0/53393/537d1e87-55f5-801b-bf9c-6a3c2f4b52a4.png)

上記では `Sample.cpp:L12: void Sample::test()` にカーソルを合わせて `M-r` を実行しています。
`Find tag (reference): (default test)` となっており `Enter` を押すと以下のように `Sample::test()` の参照元を表示してくれます。

![20160811011857.png](https://qiita-image-store.s3.amazonaws.com/0/53393/f18b0368-0eff-4ee0-b331-e7a8b18d085f.png)

上記の例では2つのうちどちらかを選ぶとでそのファイルに飛びます。

##### 例3(gtags-find-symbol)

省略。
変数の参照元や参照先へジャンプすることができます。

### Java で試してみる

https://github.com/chibi929/hello-gtags
上記の `Java` ディレクトリで試しています

#### `java-mode` 時の `gtags-mode` の設定が必要

```:.emacs
(add-hook 'java-mode-hook 'gtags-mode)
```

あとは C++ の時と同じなので省略。

# global-pygments-plugin を試す

`global-pygments-plugin` は `GNU GLOBAL 6.3.2` から取り込まれたプラグインらしい
`gtags` の欠点だった対応言語が少ないという点を改善してくれるとのこと
`global-pygments-plugin` を使用するには `Pygments` というものが必要らしい
`Pygments` を使用するには `Python 2.6 以上` が必要らしい

ということで、
1. Python 2.6 以上の準備
2. Pygments の準備
3. `global-pygments-plugin` を利用
といった感じだろうか。

## Python 2.6 以上の準備

せっかくなので `pyenv` を導入
内容は省略

```
$ pyenv versions
  system
* 2.7.12 (set by /home/chibi/.pyenv/version)
  3.5.2

$ python --version
Python 2.7.12

$ pip --version
pip 8.1.1 from /home/chibi/.pyenv/versions/2.7.12/lib/python2.7/site-packages (python 2.7)
```

とりあえずこんな感じ

## `Pygments` の準備

```
$ pip install Pygments
```
```
$ pygmentize -V
Pygments version 2.1.3, (c) 2006-2015 by Georg Brandl.
```

## `global-pygments-plugin` を利用

### `.bashrc` に設定を追加

```bash:.bashrc
export GTAGSCONF=/usr/local/share/gtags/gtags.conf
export GTAGSLABEL=pygments
```

### 上手く動かない！！

#### pygments_parser.py でエラー！！

```
$ gtags --gtagslabel=pygments
Traceback (most recent call last):
  File "/usr/local/share/gtags/script/pygments_parser.py", line 27, in <module>
    import pygments.lexers
ImportError: No module named pygments.lexers
gtags: unexpected EOF.
```

`/usr/local/share/gtags/script/pygments_parser.py` の `#!/usr/bin/python` が、
実際に利用している `python` を指していないせいだとか。
りあえず無理矢理書き換えてみるとエラーが消えた。

#### タグが定義されていない！！

```
$ gtags -v --debug
[Thu Aug 11 23:24:28 JST 2016] Gtags started.
 Using configuration file '/usr/local/share/gtags/gtags.conf'.
 Using configuration label 'pygments'.
 Using plug-in parser.
[Thu Aug 11 23:24:28 JST 2016] Creating 'GTAGS' and 'GRTAGS'.
DBG: Original skip list:
HTML/,HTML.pub/,tags,TAGS,ID,y.tab.c,y.tab.h,gtags.files,cscope.files,cscope.out,cscope.po.out,cscope.in.out,SCCS/,RCS/,CVS/,CVSROOT/,{arch}/,autom4te.cache/,*.orig,*.rej,*.bak,*~,#*#,*.swp,*.tmp,*_flymake.*,*_flymake,*.o,*.a,*.so,*.lo,*.zip,*.gz,*.bz2,*.xz,*.lzh,*.Z,*.tgz,*.min.js,*min.css
DBG: Regular expression of the skip list:
(/\.[^/]+$|/\.[^/]+/|/GTAGS$|/GRTAGS$|/GSYMS$|/GPATH$|/HTML/|/HTML\.pub/|/tags$|/TAGS$|/ID$|/y\.tab\.c$|/y\.tab\.h$|/gtags\.files$|/cscope\.files$|/cscope\.out$|/cscope\.po\.out$|/cscope\.in\.out$|/SCCS/|/RCS/|/CVS/|/CVSROOT/|/\{arch\}/|/autom4te\.cache/|/[^/]*\.orig$|/[^/]*\.rej$|/[^/]*\.bak$|/[^/]*~$|/#[^/]*#$|/[^/]*\.swp$|/[^/]*\.tmp$|/[^/]*_flymake\.[^/]*$|/[^/]*_flymake$|/[^/]*\.o$|/[^/]*\.a$|/[^/]*\.so$|/[^/]*\.lo$|/[^/]*\.zip$|/[^/]*\.gz$|/[^/]*\.bz2$|/[^/]*\.xz$|/[^/]*\.lzh$|/[^/]*\.Z$|/[^/]*\.tgz$|/[^/]*\.min\.js$|/[^/]*min\.css$)
DBG: .[/GPATH] => SKIPPED
DBG: .[/GTAGS] => SKIPPED
DBG: .[/GRTAGS] => SKIPPED
./Main.rb
 [1] extracting tags of Main.rb
[Thu Aug 11 23:24:28 JST 2016] Done.

$ gtags -d GTAGS
 __.COMPNAME	 __.COMPNAME
 __.COMPRESS	 __.COMPRESS ddefine ttypedef
 __.VERSION	 __.VERSION 6
```

すごく苦しんだ。なぜダメなのか。
どうやら `gtags` インストール時の `./configure` が影響しているようだ。
つまり `Pygments` インストール前に `./configure` を実行しているため設定が何か漏れている気がする。
`gtags` をインストールしなおすといけた。
ちなみに上記で引っかかっていた `#!/usr/bin/python` の部分も `pyenv` 導入前に `./configure` をしていたせいのようだ。

更に **追記** を行いましたので `まとめ` をご確認下さい。

### Ruby で試してみる

https://github.com/chibi929/hello-gtags
上記の `Ruby` ディレクトリで試しています

```:.emacs
(add-hook 'ruby-mode-hook 'gtags-mode)
```
C++ , Java と同様に `gtags-mode` に入るようにする

```
$ gtags -v
[Thu Aug 11 23:56:50 JST 2016] Gtags started.
 Using configuration file '/usr/local/share/gtags/gtags.conf'.
 Using configuration label 'pygments'.
 Using plug-in parser.
[Thu Aug 11 23:56:50 JST 2016] Creating 'GTAGS' and 'GRTAGS'.
 [1] extracting tags of Sample.rb
 [2] extracting tags of Main.rb
[Thu Aug 11 23:56:50 JST 2016] Done.

$ gtags -d GTAGS
 __.COMPNAME	 __.COMPNAME
 __.COMPRESS	 __.COMPRESS ddefine ttypedef
 __.VERSION	 __.VERSION 6
Sample	1 @n 1 class @n
hoge	2 @n 3 def @n
test	1 @n 2 def @n
```

Emacs での実行は C++ と同じなので省略

# 最後に

`helm-gtags-mode` を導入した内容も書こうと考えていましたが、
操作は `gtags-mode` と変わらないと思いますので書きませんでした。
実際に使う時は `helm-gtags-mode` を導入して使おうと思います！

`色々な言語で関数ジャンプ` というタイトルだけどデフォルト以外だと `Ruby` しか試していません。
まぁどれも同じだと思っています。C++, Javaの流れを見ても1個だけやればよさそう・・・。

# まとめ

`global-pygments-plugin` を使う場合は先に環境を準備しましょう。
今回、まずは通常の `gtags` から使ってみようとしたため、後から `Pygments` を入れました。
その影響で `gtags --gtagslabel=pygments` が上手く動いてくれず、Rubyのソースコードタグが上手く生成されませんでした。

ということで、
1. python & pip の準備
2. Pygments の準備
3. GNU GLOBAL(gtags) の準備
という感じで進めていきましょう。

そもそも `ctags` , `etags` , `gtags` の違いもわかっていませんでしたが、
[Emacsでタグジャンプ機能を使用する](http://qiita.com/sesame/items/27ed005a57796e5f4ffc)
こちらの記事で理解しました。

## 追記

別の環境で入れてみたら `gtags -d GTAGS` でダンプしてもどうにも空っぽ
`Exuberant Ctags` も入れないとダメかも？
[GNU GLOBALの対応言語を大幅に増やすPygmentsパーサーを導入する](http://qiita.com/yoshizow/items/9cc0236ac0249e0638ff)
上記の記事によると `Exuberant Ctags 5.5 以上` が必要とのこと。

```
$ ctags --version
Exuberant Ctags 5.8, Copyright (C) 1996-2009 Darren Hiebert
  Compiled: Aug 15 2016, 11:30:15
  Addresses: <dhiebert@users.sourceforge.net>, http://ctags.sourceforge.net
  Optional compiled features: +wildcards, +regex
```

入れてみたら期待するタグが定義されていた。

# 参考サイト

[Helm-Gtags-Modeを導入してソースコードの読解する](http://blog.10rane.com/2014/09/17/to-reading-comprehension-of-the-source-code-by-introducing-the-helm-gtags-mode)
[GNU GLOBALへのPygmentsパーサー取り込みでソースコード読みが信じられないくらいに捗るはず](http://qiita.com/5t111111/items/c14ac68f762ce71a7760)
こちらの記事を参考にさせていただきました。ありがとうございました。