EmacsでモダンClojure開発環境構築

  • 66
    いいね
  • 2
    コメント
この記事は最終更新日から1年以上が経過しています。

2016年02月28日追記: こちらの内容は古いです

というわけで、この記事ではなく2016年2月時点の最新情報である以下をご覧ください。
新: Emacs を使うモダンな Clojure 開発環境

ただ、kibitの導入等の一部の設定は上記リンク先では対象外としておりますので、その辺りの設定が必要な場合はこちらに書かれている内容も使える…かも知れません(ただし最新バージョンへのキャッチアップはできておりません)。

事前準備 / Emacsの前に

OSにleiningenがインストールされている必要あり。

https://github.com/technomancy/leiningen

apt-get、brew、yaourt等で入れておくこと。

ClojureやJVMのパッケージは、その依存パッケージとして入ってくる気がするが、そうでなければこれらは先に入れておく。

leiningenの設定

~/.lein/profiles.cljとして以下の内容のファイルを設置する。

{:user {:dependencies [[org.clojure/tools.nrepl "0.2.7"]
                       [slamhound "RELEASE"]
                       [org.clojure/tools.trace "RELEASE"]]
        :aliases {"slamhound" ["run" "-m" "slam.hound"]}
        :plugins [[lein-kibit "RELEASE"]
                  [cider/cider-nrepl "0.10.0-SNAPSHOT"]]}}

これはグローバルに以下のClojureライブラリが必要となるため。

  • slamhound: requireの中身を自動で解決する
  • tools.trace: 関数やマクロに何が渡っているかをトレースする
  • kibit: コードの静的解析
  • tools.nrepl と cider-nrepl は後述の CIDER が依存している模様で、入れていないと警告が出る

Emacs設定

必要なパッケージは以下である。いずれもMELPAで入る。

折角なのでCaskの形式で記載。

http://cask.github.io/

(depends-on "clojure-mode")
(depends-on "cider")
(depends-on "ac-cider")
(depends-on "clojure-cheatsheet")
(depends-on "slamhound")

;; Clojureに用途が限定されたパッケージではないが必要なもの
(depends-on "smartparens")
(depends-on "rainbow-delimiters")

それぞれ順に説明していく。

なお、本家でrequireによる設定が提示されている場合でも、autoloadが使えれば優先的にそちらを使い、Emacsの起動を高速化するようにしている。

基本のclojure-mode

シンタックスハイライトやらインデントやら。

https://github.com/clojure-emacs/clojure-mode

.emacs.d配下の設定は不要である。

REPLおよびIDE的環境 CIDER

Emacs上でのLisp開発で使われる、slime。

かつてはswank-clojureだったりnrepl.elが使われていたが、今はそれらのよりベターなものとしてこれがある模様。

https://github.com/clojure-emacs/cider

自分は.emacs.dにて以下の設定をしている。

(add-hook 'clojure-mode-hook 'cider-mode)

;; mini bufferに関数の引数を表示させる
(add-hook 'cider-mode-hook 'cider-turn-on-eldoc-mode)

;; 'C-x b' した時に *nrepl-connection* と *nrepl-server* のbufferを一覧に表示しない
(setq nrepl-hide-special-buffers t)

;; RELPのbuffer名を 'project名:nREPLのport番号' と表示する
;; project名は project.clj で defproject した名前
(setq nrepl-buffer-name-show-port t)

他にも色々な設定がある。詳細はGitHub上のREADME.mdにてBasic configurationを参照。

使用方法も多岐に渡るが、これもREADMEに譲る。

テストの実行について

clojure-test-modeはCIDER 0.7の時点でCIDERに吸収されてdepricatedとなった。
CIDER上でテストを実行する一番単純な方法は、REPLでテストコードを評価した上で、テスト対象のファイルを開いたバッファにてC-c ,することだ。
そうすると、同ファイルに対応するテストが実行される。

注意点は以下である。

  • Read, Evalをしていないテストコードは実行対象にならない
  • namespaceにルールがある
    • テスト対象ファイルのnamespaceがfoo.barだとすると、foo.bar-testといnamespaceでテストが書いてある必要がある
    • namespaceのルールを変えたい場合は、Emacsの設定にてcider-test-infer-test-nsを書き換える

詳細は、CIDERのREADMEを参照。

inspectorについて

※profile.cljにcider-nreplを含めていないとエラーになるかも。注意。

C-c M-iすると、カーソル位置にある式の中身がインスペクトされる。
具体的に言うと

(def hoge (+ 1 2))
hoge

を評価しておいて、hogeのところで上記コマンドを実行すると、以下のように型や値等の情報が表示される。

Type: java.lang.Long
Value: "3"
---
Fields: 
  "serialVersionUID" = 4290774380558885855
  "SIZE" = 64
  "value" = 3
  "TYPE" = long
  "MAX_VALUE" = 9223372036854775807
  "MIN_VALUE" = -9223372036854775808

READMEによるとcider-inspector-modeというものあるので、上記よりもインスペクトに特化した機能が用意されている模様。

ac-cider

auto-complete.elを使った自動補完。
https://github.com/clojure-emacs/ac-cider

cider-jack-inしていないと有効にならないので注意。

設定は以下。

(autoload 'ac-cider "ac-cider" nil t)
(add-hook 'cider-mode-hook 'ac-flyspell-workaround)
(add-hook 'cider-mode-hook 'ac-cider-setup)
(add-hook 'cider-repl-mode-hook 'ac-cider-setup)
(eval-after-load "auto-complete"
  '(progn
     (add-to-list 'ac-modes 'cider-mode)
     (add-to-list 'ac-modes 'cider-repl-mode)))

clojure-cheatsheet

M-x clojure-cheatsheetで以下のサイトにある情報(同じもの?)を参照できる。
http://clojure.org/cheatsheet

調べたい対象は、helmで絞り込みが可能。

slamhound

必要なpackageを自動でrequrieしてくれるツール。

https://github.com/technomancy/slamhound

M-x slamhoundすると、実行される。

設定は上記すでに行っている以下の2点のみでよい。

  • slamhound.el の導入
  • .lein/profiles.clj の記述

kibit

コードの静的解析を行い、ベターな書き方をサジェストしてくれる。
単純な警告ツールというより、実際に書き直した後のコードが出力される。

https://github.com/jonase/kibit

上記の .lein/profiles.clj の設定に加え、以下を .emacs.d 配下で定義すると、M-x compileM-x kibitM-x kibit-current-fileで結果が別バッファ上に表示される。

;; Teach compile the syntax of the kibit output
(autoload 'compile "compile" nil t)
(add-to-list 'compilation-error-regexp-alist-alist
             '(kibit "At \\([^:]+\\):\\([[:digit:]]+\\):" 1 2 nil 0))
(add-to-list 'compilation-error-regexp-alist 'kibit)

;; A convenient command to run "lein kibit" in the project to which
;; the current emacs buffer belongs to.
(defun kibit ()
  "Run kibit on the current project.
Display the results in a hyperlinked *compilation* buffer."
  (interactive)
  (compile "lein kibit"))

(defun kibit-current-file ()
  "Run kibit on the current file.
Display the results in a hyperlinked *compilation* buffer."
  (interactive)
  (compile (concat "lein kibit " buffer-file-name)))

kibit-modeについて (使用を断念)

kibitの結果をflycheckと連動してくれる kibit-mode.el も存在はしている。
https://github.com/aredington/kibit-mode

だがこれを試してみたところ、Emacsが固まる現象に見舞われた。
加えて、flycheckの結果をpopupで出すように自分はしているだが、イマイチその表示が崩れて見にくかった。
リファクタされたコードが表示されることで、横に長くなったり改行が入ったりするため。

ちなみに、この辺のコミッタである@bbatsov氏によると、「kibitは旧来の静的解析とは違うから、flycheckとはあまり相性が良くない」らしい。

https://twitter.com/lunaryorn/status/341471670378299392

この「相性が良くない」というのは、上記のようなことを指している、と勝手に解釈した。

Compojure用の設定

Clojure用のWebフレームワークであるCompojureにおいて、ルーティングのマクロを使った時のインデントがかっこ良くなるように以下を設定する。

(add-hook 'clojure-mode-hook
          (lambda()
            (define-clojure-indent
              (defroutes 'defun)
              (GET 2)
              (POST 2)
              (PUT 2)
              (DELETE 2)
              (HEAD 2)
              (ANY 2)
              (context 2))))

tracing

tracingが何をするものか、というと以下の記事が分かりやすい。
http://athos.hatenablog.com/entry/tracing_execution_with_tools_trace

使っているツールは以下である。
https://github.com/clojure/tools.trace

実はEmacsとは直接的に連動させていない。
上記ブログ記事で書かれているようなtrace-vars等の式を、直接CIDERのREPLに渡して使ってみている。

トレース周りの連動は茨の道だった。。

茨の道について (断念の記録)

troncleが機能的に優れているらしい。
https://github.com/coventry/troncle

しかし、troncle.elがREADMEに反してMELPAから消えている、設定はしてみたけど動かない、というところで断念した。

仕方ないので、機能的には劣るというcider-tracingを使ってみた。
https://github.com/clojure-emacs/cider-tracing
インストールはできたが、Emacsが固まる…。

括弧の操作を便利にする

smartparens

https://github.com/Fuco1/smartparens

よく使われているであろう、pareditを包括したもの。
pareditとは若干操作感が違うので注意。

rainbow-delimiters

括弧を色分けして見やすくする。
https://github.com/jlr/rainbow-delimiters

デフォルトだとコントラストが低くて見やすくないと個人的に思ったので、コメント欄でいただいたアドバイスを元にuse-package.elとdash.elを使いつつ以下の設定を行っている。

(use-package rainbow-delimiters
  :config
  (use-package color
    :config
    (--each (number-sequence 1 rainbow-delimiters-max-face-count)
      (let ((face (intern (format "rainbow-delimiters-depth-%d-face" it))))
        (callf color-saturate-name (face-foreground face) 90)))))