雑文発散

«前の日記(2015-01-18) 最新 次の日記(2015-01-20)» 編集
過去の日記

2015-01-19 [長年日記]

[Emacs][JavaScript] Emacs の JavaScript 開発環境を整備する(gtags編)

しばらく JavaScript を記述する期間がありそうなので、割と適当だった Emacs の JavaScript 開発環境を整備しようと思い立った。初めて使うライブラリとかがあって、その動作とか中身とかを手早く知りたいので、まずはタグジャンプを利用できるようにしたい。

PHP では GNU GLOBAL (gtags) + helm-gtags を使ってあちこちジャンプしている。JavaScript でも同様の環境を目標とする。ちなみに helm-gtags のインストールとかはこの日記に含まれない。もし、同様の環境を作りたいと思ったら、helm-gtags で検索すればインストール関係の記事は見つかると思うので、そちらを参照して欲しい。

で、ここから本題。

GNU GLOBAL のホームページを見ると、JavaScript もサポートしているような記述が見つかった。

supports 25 languages by Pygments + Exuberant Ctags plug-in parser. (definition and reference)
Awk, Dos batch, COBOL, C, C++, C#, Erlang, Fortran, Java, JavaScript, Lisp, Lua, Pascal, Perl, PHP, Python, Ruby, Matlab, OCaml, Scheme, Tcl, TeX, Verilog, Vhdl and Vim.

早速、ジャンプ用のタグを生成する gtags コマンドを叩いてみたんだけど、期待の動作にならない。よく見ると「Pygments + Exuberant Ctags plug-in parser」という記述があるので、プラグインを使う必要があるようだ。

GNU GLOBALへのPygmentsパーサー取り込みでソースコード読みが信じられないくらいに捗るはず」によれば、Homebrew の GNU GLOBAL では、プラグインを動作させるための依存関係も解決してくれるそうなので、さくっとインストールしなおした。

% brew uninstall global
% brew install global --with-exuberant-ctags --with-pygments

--with-exuberant-ctags--with-pygments はそれぞれの構文解析パーサを一緒にインストールする指定。どちらか一方でも良いとは思うけど、両方入れちゃっていいんじゃないかな。

プラグインの定義は、Homebrew でインストールされた gtags.conf の中に書かれている。特にインストール先のディレクトリをいじっていなければ、/usr/local/Cellar/global/6.3.2/share/gtags/gtags.conf あたりに存在するはず。6.3.2 の部分はバージョン番号なので、その時々に応じてよしなに。

gtags.conf の中身を見ると、プラグイン定義の部分がある。

#
# Plug-in parser to use Exuberant Ctags.
#
exuberant-ctags|plugin-example|setting to use Exuberant Ctags plug-in parser:\
[snip]
        :langmap=JavaScript\:.js:\
#
# Plug-in parser to use Pygments.
#
pygments-parser|Pygments plug-in parser:\
[snip]
        :langmap=JavaScript\:.js:\

それぞれ、exuberant-ctagspygments-parser がプラグインの名前みたいで、こいつを gtagslabel オプションに指定することでプラグインを利用した構文解析をしてくれるようだ。

% gtags --gtagslabel=exuberant-ctags -v --debug
% gtags --gtagslabel=pygments-parser -v --debug

-v および --debug オプションは動作確認用で付けているだけなので、普段使いの場合は取り除いてしまうのが良いと思う。

どちらのプラグインを利用するかは、その解析内容の質だったり好みだったりで判断するのが良いのかなと思うわけだが、JavaScript においては exuberant-ctags をそのまま使うとちょっと物足りない感じになる。詳しくは「EmacsでJavaScriptソースを快適に読むために:js2-modeとエグズーベラントCtags」に書かれていたので、解説はそちらへお任せするが、定義されているはずのところへジャンプしてくれないパターンが出てきてしまうのだ。

で、何をすべきか。~/.ctags に以下の記述を追加してやれば良いようだ。ネタ元は「ctagsで正しくJsの関数の定義元にジャンプする」。標準では解析できないヤツは、解析用の正規表現を追加することによって解決してしまおうというアプローチのようだ。ふぇぇ。

--langdef=js
--langmap=js:.js
--regex-JavaScript=/([A-Za-z0-9._$\(\)]+)[ \t]*[:=][ \t]*function[ \t]*\(/\1/m,method/
--regex-JavaScript=/([A-Za-z0-9._$\#\(\)]+)[ \t]*[:][ \t]*([A-Za-z0-9._\-\#\'\"]+)[ \t]*/\1/p,property/
--regex-JavaScript=/([A-Za-z0-9._$\#\(\)]+)[ \t]*[:][ \t]*([A-Za-z0-9\'\"._\-\#\(]+)[ \t]*\{/\1/p,property/
--regex-JavaScript=/var ([A-Za-z0-9._$\#]+)[ \t]*[=][ \t]*([A-Za-z0-9._'"\$\#\[\{]+)[,|;]/\1/v,variable/
--regex-JavaScript=/([A-Za-z0-9._$\#]+)[ \t]*[=][ \t]*([A-Za-z0-9._'"\$\#]+)extend\(/\1/c,class/

--regex-js=/([A-Za-z0-9._$]+)[ \t]*[:=][ \t]*\{/\1/,object/
--regex-js=/([A-Za-z0-9._$()]+)[ \t]*[:=][ \t]*function[ \t]*\(/\1/,function/
--regex-js=/function[ \t]+([A-Za-z0-9._$]+)[ \t]*\(([^)])\)/\1/,function/
--regex-js=/([A-Za-z0-9._$]+)[ \t]*[:=][ \t]*\[/\1/,array/
--regex-js=/([^= ]+)[ \t]*=[ \t]*[^"]'[^']*/\1/,string/
--regex-js=/([^= ]+)[ \t]*=[ \t]*[^']"[^"]*/\1/,string/

これで --gtagslabel=exuberant-ctags オプション付きで実行すれば、だいぶ期待通りに動く。ただ、同一の定義元へのエントリが2個になってしまう(ジャンプ先の候補が2つ表示される)場合があるので、上記の ~/.ctags の内容には改善の余地があるのかも知れない。

一方で pygments-parser の方は、特に追加指定をしなくともだいたいうまく動いている。でも、上記の追加を行った exuberant-ctags ではジャンプできていたものが候補に上がってこない場合があるようだ。

つまり、同一候補が複数表示されるストレスはあるけどほぼ漏れ無くジャンプできる exuberant-ctags と、同一候補が出たりはしないけど一部の候補へジャンプできない可能性がある pygments-parser という関係に見える、今のところは。

なんとかすればなんとかなりそうな気がするけど、すぐ解決できるとも思えない(オレのいまの知識だと)。実用上問題なければ pygments-parser が良いのかなあ。まぁ、もう少し使ってみよう。

参考にしたところのまとめ