読者です 読者をやめる 読者になる 読者になる

(define -ayalog '())

括弧に魅せられて道を外した名前のないプログラマ

Clojure の開発で gtags を使う

Clojure Emacs

えっと Emacs/Cider ユーザーにはあまり関係ないですね。あと他のエディタや IDE で定義ジャンプ出来る人はいらないと思う。

僕はこの前から inf-clojure を使っているので、これが欲しかった。あと例えば他のエディタや IDE で defxxxx なマクロを使っていると定義ジャンプ出来ないシーンがある気がしたので*1、どうせなのでそのへんまとめていい感じにしようかなと思った。

必要なもの

  • Exuberant Ctags
  • GNU GLOBAL v6.3.2 以降

以下の記事が詳しいので、参照してインストールしましょう。
gtags - GNU GLOBALの対応言語を大幅に増やすPygmentsパーサーを導入する - Qiita

ctags の設定

Exuberant Ctags は Clojure に対応していないので、このまま使うと定義タグの生成が出来ずに定義ジャンプが出来ない。シンボルタグだけは上記の記事の通りにやれば生成出来るのだけど、検索対象が山のようになるだけで使い勝手悪いどころの騒ぎではないので、定義タグを作れるようにする。

~/.ctags を作って以下の設定を書く。

--langdef=Clojure
--langmap=Clojure:.clj
--langmap=Clojure:+.cljs
--langmap=Clojure:+.cljx
--regex-clojure=/\([ \t]*create-ns[ \t]+([-[:alnum:]*+!_:\/.?]+)/\1/n,namespace/
--regex-clojure=/\([ \t]*def[ \t]+([-[:alnum:]*+!_:\/.?]+)/\1/d,definition/
--regex-clojure=/\([ \t]*def[a-z]*[ \t]+([-[:alnum:]*+!_:\/.?]+)/\1/d,definition/
--regex-clojure=/\([ \t]*defn[ \t]+([-[:alnum:]*+!_:\/.?]+)/\1/f,function/
--regex-clojure=/\([ \t]*defn-[ \t]+([-[:alnum:]*+!_:\/.?]+)/\1/p,private function/
--regex-clojure=/\([ \t]*defmacro[ \t]+([-[:alnum:]*+!_:\/.?]+)/\1/m,macro/
--regex-clojure=/\([ \t]*definline[ \t]+([-[:alnum:]*+!_:\/.?]+)/\1/i,inline/
--regex-clojure=/\([ \t]*defmulti[ \t]+([-[:alnum:]*+!_:\/.?]+)/\1/a,multimethod definition/
--regex-clojure=/\([ \t]*defmethod[ \t]+([-[:alnum:]*+!_:\/.?]+)/\1/b,multimethod instance/
--regex-clojure=/\([ \t]*defonce[ \t]+([-[:alnum:]*+!_:\/.?]+)/\1/c,definition (once)/
--regex-clojure=/\([ \t]*defstruct[ \t]+([-[:alnum:]*+!_:\/.?]+)/\1/s,struct/
--regex-clojure=/\([ \t]*intern[ \t]+([-[:alnum:]*+!_:\/.?]+)/\1/v,intern/
--regex-clojure=/\([ \t]*ns[ \t]+([-[:alnum:]*+!_:\/.?]+)/\1/n,namespace/

元ネタは以下の設定だけど、元のやつだと defxxxx マクロで定義したシンボルを定義タグとして作成してくれないので少しだけ細工した。
my .ctags(exuberant-ctags) for Clojure

gtags の設定

以下の記事では Lisp の設定を乗っ取る形で実現しているけど、 GLOBAL の v3.6.2 以降だとデフォルトで Clojure サポートがあるはずなのでそちらを修正するようにする。
gtagsでClojureのコードのタグ付け - iwasakimsの日記

$ cp /usr/local/share/gtags/gtags.conf ~/.globalrc

とコピーしたら .globalrc を以下のように修正する

$ diff /usr/local/share/gtags/gtags.conf ~/.globalrc 
28c28
< 	:tc=native:
---
> 	:tc=native:tc=pygments:
170c170
< 	:langmap=Clojure\:.clj:\
---
> 	:langmap=Clojure\:.clj.cljs.cljx:\

ふたつ変更点があって、ひとつは default でも pygments を使う設定、ふたつめは cljs/cljx という拡張子に対応するため。

これだけ設定したら後はプロジェクトルートで gtags と実行するだけでいいわけです*2

おまけ

Emacs ユーザーなら helm-gtags.el を使って幸せになりましょう。
syohex/emacs-helm-gtags · GitHub

むしろこれがあるから gtags を使えるようにしようと思ったくらい。

余談

Exuberant Ctags の後続プロジェクトというか fork した(?)プロジェクトで Universal Ctags というのがあるらしくて、それはデフォルトで Clojure をサポートしているんだけどパーサーが適当すぎて ns, defn, quote しかサポートしてないという。
これを使った場合でも同様に .ctags に設定書けば解決するんだけど、まぁ結局同じような内容書くならどっちでもいいよねという感じはある。
universal-ctags/ctags · GitHub

*1:少なくとも Cursive は defxxxx マクロで定義した変数/関数にアクセス出来ない

*2:このままだと target ディレクトリまでタグ付けの対象になるので避けたければ .globalrc の skip オプションの一番後ろに target/ とか書いておくと良いです