しばらく 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-ctags
と pygments-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
が良いのかなあ。まぁ、もう少し使ってみよう。