Spacemacsで Clojure を書くためのメモ。
CIDER を使いたくて『Spacemacs入門』した件の続き。
まずはSpacemacsにClojure layerを導入し[ evil-lisp-state ] (https://github.com/syl20bnr/evil-lisp-state)によるS式操作とCIDERの基本的な使い方を学ぶ。
Clojure layerの追加
Clojure layerを導入する。
Spacemacsを起動。
SPC f e d でドットファイルを編集。
dotspacemacs/layers
関数内dotspacemacs-configuration-layers
リストにclojure
を追加。
SPC f e R でコンフィグレーションを反映しClojure layerを有効化。
ちなみに レイヤーの構成パッケージ を確認すると以下のようになっている。
パッケージ名 | 説明 |
---|---|
cider | Clojure(Script)のためインタラクティブプログラミング環境 |
cidier-eval-sexp-fu | 評価したS式が光るちょっとしたギミック |
clj-refactor | Clojure用リファクタリング関数集 |
clojure-mode | Clojure(Script)編集のためのモード |
clojure-snippets | Clojure用のスニペット集 |
company | 入力補完フレームワーク |
ggtags | GNU Globalのフロントエンド |
helm-gtags | GNU Globalのhelmインタフェース |
popwin | バッファのポップアップ表示 |
smartparens | 括弧の編集支援 |
S式の操作
Spacemacsでは標準でevil-lisp-stateによるLispコードのナビゲーション、編集支援機能が提供されておりClojure layerの有無に関係なく利用できる。
以下はSpacemacsドキュメント Editing Lisp codeから一部抜粋。
ナビゲーション
- 対応する括弧へ移動
- % or SPC k %
- S式のはじめに移動
- SPC k 0
- S式の終わりに移動
- SPC k $
- 次の右括弧へ移動
- SPC k j
- 前の左括弧へ移動
- SPC k k
- 次のシンボルへ移動
- SPC k l
- 次のS式へ移動
- SPC k L
追加
- 前にS式を挿入
- SPC k (
- 後にS式を挿入
- SPC k )
削除
- 次のシンボルを削除
- SPC k d s
- 前のシンボルを削除
-
SPC k D s
- 次のS式の削除
- SPC k d x
- 前のS式の削除
- SPC k D x
その他
- S式を括弧で囲う
- SPC k w
- S式から括弧を剥がす
- SPC k W
- S式をコピー
- SPC k y
- evil-lisp-stateへ遷移
- SPC k .
evil-lisp-stateへ遷移すると以降のコマンド SPC k を省略し連続的に操作ができる。
ESC で復帰。
S式の整形
SPC m f l でclojure-modeによるフォーム要素の垂直方向位置揃えが行われる。
(def a-map
{:foo 1
:foo-bar 2
:foo-bar-hoge-fuga 3})
(def a-map
{:foo 1
:foo-bar 2
:foo-bar-hoge-fuga 3})
map以外にも let
や cond
等のフォームも揃えてくれるが対象は決まっておりそれ以外の関数では整列されない。
また *SPC* *m* *f* *b* or *SPC* *m* *=* で 後述のCIDERによるREPLを介したコード整形が行われる。
CIDER
CIDERはClojureのインタラクティブな開発を支援するEmacs拡張。
nREPLサーバと接続しコードのナビゲーション、実行、デバッグ、テストやドキュメントの参照などの機能を提供する。
これら機能は SpacemacsからLeiningen / Boot プロジェクトのソースを開き、nREPLサーバへ接続することで利用可能となる。
CIDER用 nREPL middleware
CIDERが提供する多くの機能はCIDER用 nREPL middlewareに依存している。
CIDERから新規にnREPLサーバを起動する際は自動的にCIDER用 nREPL middlewareが組み込まれるが、シェル等からlein repl
タスクなどでnREPLサーバを起動する場合は個別の設定が必要となる。
例えばLeiningenであれば、以下のように ~/.lein/profiles.clj
に CIDER用 nREPLミドルウエアのプラグイン定義が必要となる。
{:repl {:plugins [[cider/cider-nrepl "0.16.0"]]}}
evil-modeとCIDERのキーバインドについて
CIDERではネームスペースブラウザやマクロ展開など機能に応じたメジャー/マイナーモードでキーバインドが定義されているが、evil-modeなSpacemacsからはそのまま利用することができない。
例えば SPC m h n で ネームスペース一覧表示し、ネームスペース選択後の関数一覧で可能なキー操作はInsertステート側へバインドされるためNormalステートから利用することはできない。
この例では関数一覧で関数名にカーソルを当て d でドキュメントが表示されない。i d と一度Insertステートにしてからキーを押す必要がある。
対応方法は以下の3つ。
- C-z で一時的にevil-modeを解除しemacs-modeを利用する
- 一度Insertステートにしてからキー入力する(ことを受け入れる)
- Normalモードで動くようにキーバインドを追加する
Emacs力に乏しい自分にとってevil-modeの解除は死と等しい為1は無し。
基本は2のまま利用しどうしても使いにくいところは3で対応することにした。
例えばネームスペースブラウザをNormalステートのまま利用する場合は以下ように HOME/.spacemacs.d/init.el
の dotspacemacs/user-config
で明示的にNormalステートでのキーを再定義することで対応する。
(with-eval-after-load 'cider-mode
(evil-define-key 'normal cider-browse-ns-mode-map "d" #'cider-browse-ns-doc-at-point)
(evil-define-key 'normal cider-browse-ns-mode-map "s" #'cider-browse-ns-find-at-point)
(evil-define-key 'normal cider-browse-ns-mode-map (kbd "RET") #'cider-browse-ns-operate-at-point)
(evil-define-key 'normal cider-browse-ns-mode-map "^" #'cider-browse-ns-all))
evil-modeとS式評価
CIDERでは SPC m e e 等でカーソル位置手前のS式を評価する事ができるが evil-mode
にとってこの カーソル位置の手前 というのが曲者。
S式が行末にある場合、emacs-mode
であれば行末の右括弧の右隣である改行部分にカーソルを置けるので問題無いが evil-mode
のNormalステートの場合は行末の右括弧までしかカーソル移動できない。
対応方法は以下の3つ。
evil-move-cursor-back
またはevil-move-beyond-eol
を有効にし改行部分までカーソルを移動できるようにする- 行末でInsertステートにして C-x C-e
cider-eval-last-sexp
関数等のアドバイスを追加してカーソル位置が右括弧の上でもS式評価できるようにする
1は以下のように HOME/.spacemacs.d/init.el
の dotspacemacs/user-config
で設定することで evil-mode
のNormalステートでも改行までカーソル移動できるようにする方法。
(setq evil-move-cursor-back nil) ;; または(setq evil-move-beyond-eol t)
確かに改行までカーソルを移動できるようになるが、行末で文字を削除した時の挙動が若干従来と異なるため見送り。
2は毎回Insertステートにするのが面倒な上、Leaderキーを起点としたキーバインドが使えなくなってしまうためこれも見送り。
今回は以下のように dotspacemacs/user-config
にcider-eval-last-sexp
関数等のアドバイスを追加する方法で対応した。
(with-eval-after-load 'cider-mode
(let ((f (lambda (command &rest args)
(if (or (evil-normal-state-p) (evil-motion-state-p))
(save-excursion
(unless (or (eobp) (eolp)) (forward-char))
(apply command args))
(apply command args)))))
(advice-add 'cider-eval-last-sexp :around f)
(advice-add 'cider-eval-last-sexp-and-replace :around f)
(with-eval-after-load 'cider-eval-sexp-fu
(advice-add 'cider-esf--bounds-of-last-sexp :around f))))
上記アドバイスによりS式評価時、一時的に(そして内部的に)カーソルを一文字分進めてから評価が実行される。
これによS式の末尾の右括弧にカーソルを置いて評価できるようになった。
REPL
以下はClojure layer Leader REPLから一部抜粋。
- nREPLサーバを起動し接続する
- SPC m s i
(ClojureScriptの場合は SPC m s I) - 起動済みのnREPLサーバへ接続
- SPC m s c
- nREPLサーバと切断 / 終了
- SPC m s q
- REPLバッファとの切り替え
- SPC m s s
コードとREPLのバッファを切り替える。 - クラスパス上の全てのファイルを全てリロード
- SPC m s x
clojure.tools.namespaceを使用したコードのリロードが行われる。
リロード前後のタイミングで任意の関数を実行することもできる。
詳細はCIDER Code reloadingを参照。 - バッファを評価
- SPC m s b
バッファ全体を評価する。
これはバッファ上のコードがREPLにロードされるという意味合いになる。
最後を大文字にすると( SPC m s B ) 評価後にREPLバッファへ移動する。 - 関数定義を評価
- SPC m s f
カーソル位置の関数定義を評価する。
これは関数がREPLのカレントネームスペースにロードされるという意味合いになる。
最後を大文字にすると( SPC m s F ) 評価後にREPLバッファへ移動する。 - カーソル手前のS式を評価
- SPC m s e 最後を大文字にすると( SPC m s E ) 評価後にREPLバッファへ移動する。
- 範囲選択したS式を評価
- SPC m s r 最後を大文字にすると( SPC m s R ) 評価後にREPLバッファへ移動する。
- バッファのnsフォームを評価
- SPC m s n
これはバッファのネームスペースをREPLのデフォルトネームスペースに設定する意味合いになる。
最後を大文字にすると( SPC m s N ) 設定後にREPLバッファへ移動する。
ブラウズ
以下はClojure layer Leader Gotoから一部抜粋。
- 関数 / 変数定義へ移動
- SPC m g g
カーソル下の関数 / 変数の定義へ移動。 - 前に戻る
- SPC m g b
- リソースへ移動
- SPC m g r
カーソル下のリソースファイルパスを開きバッファへ移動。
現状は動かない。原因はClojure layerがCIDER 0.9.0で消されたcider-jump-to-resource
を使っているため。
取りあえずプルリクエストは出した。fix: Rename deprecated cider function #10303 - エラーが発生している行へ移動
- SPC m g e
- ネームスペースを検索して詳細表示
- SPC m g n
ネームスペースを検索し当該ネームスペースの変数、関数の一覧を表示する。 - ネームスペース一覧から詳細表示
- SPC m g N
- クラスパス一覧を表示
- SPC m g C
評価
以下はClojure layer Leader Evaluationから一部抜粋。
バッファ、関数、選択範囲単位の評価など REPL(SPC m s 〜)側と同様の機能があるが差異は無くエイリアスとして存在している。
- バッファを評価
- SPC m e b
- 関数定義を評価
- SPC m e f
- カーソル手前のS式を評価
- SPC m e e
- カーソル手前のS式を評価して置換
- SPC m e w
- 範囲選択したS式を評価
- SPC m e r
- マクロ展開
- SPC m e m
- マクロ全展開
- SPC m e M
ドキュメンテーション
以下はClojure layer Leader Documentationから一部抜粋。
- ロード済みネームスペース一覧
- SPC m h n
ネームスペースブラウザを表示しロード済みネームスペース一覧を表示する。
Spacemacsの場合はキー操作が効かない問題がある。
詳細はEvil modeのキーバインド問題を参照。 - apropos
- SPC m h a
文字列を入力しロード済みの全てのネームスペースの中から該当するパブリックな変数、関数を検索。
(clojure.repl/apropos相当) - grimoire
- SPC m h g
カーソル位置の関数または関数を指定してgrimoireのドキュメントを表示。 - doc
- SPC m h h
カーソル位置の関数または関数を指定して公式のドキュメントを表示。
(clojure.repl/doc相当) - javadoc
- SPC m h j
カーソル位置のクラスまたはクラスを指定してブラウザでjavadocを表示。
(clojure.java.javadoc/javadoc相当)
コード整形
- バッファのコードを整形する
- SPC m f b or SPC m =
CIDERのポップアップバッファ
ドキュメントの表示時などのCIDERポップアップバッファは q でクローズできる。
最後に
以上でSpacemacsでClojureのコードを書く準備が整った。
今回はテスト、デバッグ、リファクタリングについては触れていないが、これらについては実際に使い込んでみて有用だったらメモにまとめようと思う。