VimでClojureプログラミング

前置き

Clojureプログラミングするとき、REPLをいつでも使える状態にしておくと、ちょっとしたコード断片を動かしてみたいときに重宝します。更に、エディタとREPLが連動してくれて、エディタ上のコードを簡単にREPLへ投入できたら便利ですね。そんなことができるエディタやIDEには、Vim、Emacs(+cider)、Eclipse(+Counterclockwise)、LightTableといった選択肢があります。本記事では、その一つ、Vimとvim-fireplaceの組み合わせについて解説します。

セットアップ

本記事ではWindowsを使います。VimとClojure用プラグインのセットアップについては、Vim for Windows セットアップメモを参照して下さい。少なくとも、vim-clojure-staticとvim-fireplaceプラグインが必要です。

無事セットアップが終われば、vimとREPLを接続するのは簡単です。

  1. Leiningenでプロジェクトを作る(lein new)
  2. LeiningenでREPLを起動する(lein repl)
  3. vimでソースを開く

編集しようとしているプロジェクト上でREPLを起動することが重要です。

先にvimでソースを開いておいて、vimからREPLを起動しても構いません。例えば、vim上で以下のコマンドを実行すれば、REPLが起動します(Windows用です)。

カレントファイルに対応したREPLを起動
:!start cmd.exe /c cd /d "%:h"&& lein.bat repl

長いコマンドなので、カスタムコマンドとして _vimrc($MYVIMRC)に登録しておくと便利です。

$USERPROFILE\_vimrc
command Repl !start cmd.exe /c cd /d "%:h"&& lein.bat repl

お好みで、:headlessオプションを付けても良いでしょう。何かの理由でデフォルトポートを使いたくない場合は、:portオプションで任意のポート番号を指定できます。

9999番ポートでREPLサーバのみ(headless)を起動
:!start cmd.exe /c cd /d "%:h"&& lein.bat repl :headless :port 9999

ちょっとだけMacネタを。MacVimでは !startが使えませんが、AppleScript(osascriptコマンド)でターミナルを操作してやれば何とかなります。

~/.vimrc
function! Repl(dir)
  let script = "!osascript"
    \ . " -e 'tell application \"iTerm\"'"
    \ . " -e '  make new terminal'"
    \ . " -e '  tell the current terminal'"
    \ . " -e '    activate current session'"
    \ . " -e '    launch session \"Default session\"'"
    \ . " -e '    tell the last session'"
    \ . " -e '      write text \"cd " . a:dir . "; lein repl\"'"
    \ . " -e '    end tell'"
    \ . " -e '    end tell'"
    \ . " -e 'end tell'"
  execute script
endfunction

command! Repl silent! call Repl(expand("%:p:h"))

vim-fireplaceは、./target/repl-portを見てポート番号を判断します。独自のREPLサーバと接続したいときは、:Connect コマンドを使います。

別マシン上で動いているREPLサーバと接続
:Connect nrepl://192.168.1.100:53203

ソースのロード(require)

Normalモードで cpr と打てば、カレントファイルを (require :reload) します。cpR なら、(require :reload-all) になります。ロードするのはファイルであってバッファではありません。編集中のバッファをロードしたいなら、一旦セーブしてから cpr しましょう。または、:%Eval でもOKです(「バッファ全体を評価する」の意味)。

フォームの評価

適当なフォーム上にカーソルを置いて、cpp と打てば、当該フォームの評価結果がコマンドラインに表示されます。フォームがネストしている場合は、最外フォームが評価されます。

cpp

任意の範囲を評価したいなら、cp{motion} のようにvimのモーション機能と組み合わせます。例えば、評価したいフォームの括弧の上にカーソルを置いて、cp% と打てば、そのフォームが評価されます(%は対応する括弧へジャンプするモーション)。

cp%

またシンボルの先頭で cpw とやれば、そのシンボルが評価されます。

cpw

便利そうだと感じたものを以下に挙げます。

  • cp% →対応する括弧で囲まれたフォームを評価
  • cpw →カーソル上の単語を評価(単語の先頭で)
  • cpiw →カーソル上の単語を評価(単語の中ならOK)
  • cpab →カーソル上の単語を含む最内フォームを評価(abは、a blockの意味)

評価時に例外が発生した場合は、ロケーションリストウィンドウでスタックトレースを見ることができます(vim標準の :lopen コマンド)。また、評価結果の履歴を見るには :Last や :{n}Last を使います。

マクロ展開

cmm と c1mm は、マクロ展開結果を表示します。それぞれ、macroexpandとmacroexpand-1に対応します。cppが最外フォームに作用するのに対し、cmmとc1mmは、カーソル上の単語を含む最内フォームに作用します。

cp{motion} と同様、モーション機能と組み合わせた cm{motion} と c1{motion} も使えます。

任意の式の評価

実際のところ、ソースファイル上に書くほとんどのコードは defn フォームなので、それを評価しただけでは、あんまり面白くは無いです。やはり、それを呼んでみたいですよね。それには、Normalモードで cqp と打ちます。するとコマンドラインにREPLプロンプトが表示されるので、任意の式を入力できます。

cqp

同様なコマンドが他にもあります。

  • :Eval expr →exprを評価
  • :{range}Eval →{range}で指定した範囲を評価
  • cqc →コマンドラインウィンドウに式を入力して評価
  • cq{motion} →{motion}が示す式をコマンドラインウィンドウで編集して評価

評価結果をバッファへ

評価するだけでなく、評価結果をバッファへ入力したり、バッファを評価結果で置換するコマンドもあります。例えば c!! は、カーソル上の単語を含む最内フォームを、その評価結果で置き換えます。

c!!before

ここで c!! とやれば、以下のようになります。

c!!after

同様なコマンドが他にもあります。

  • c!{motion} →モーション対象を評価結果で置換
  • :{range}Eval! →{range}で指定した範囲を評価結果で置換
  • :{range}Eval! expr →{range}で指定した範囲の下に評価結果を挿入
  • Insertモードで<C-R>(...) →評価結果を挿入

ジャンプ

ジャンプ系のコマンドも各種揃っています。

キー機能
[<C-D>カーソル上のシンボルの定義箇所へ
<C-W>dカーソル上のシンボルの定義箇所へsplitで
gfカーソル上のシンボルのnamespaceのファイルへ
<C-W>fカーソル上のシンボルのnamespaceのファイルへsplitで
:Djump symsymの定義箇所へ
:Dsplit symsymの定義箇所へsplitで
:Aソースファイルとテストファイル間を行ったり来たり
:AS:Aのsplit版
:AV:Aのvsplit版
:AT:Aのタブ版

調べる

Clojure標準関数の仕様が分からないときなどに便利なコマンドが、K です。例えば、seqオペレータにカーソルを置いて K と打つと、以下のようになります。

K

他にも調べる系のコマンドが、いろいろあります。

キー機能
[dカーソル上のシンボルの定義箇所のソースを表示
:Doc symdocマクロ呼び出し
:Source symsourceマクロ呼び出し
:FindDoc kwfind-doc関数呼び出し(クオート不要)
:Apropos kwapropos関数呼び出し(クオート不要)

まとめ

と、まぁ、こういった感じです。コマンドを覚えるまでは、自分用のチートシートを作って手元に置いておきましょう。

cheat

おまけ

vim-fireplaceとは関係ないネタですが、下記コードを _vimrc($MYVIMRC)に書いておくと、トップレベルのフォームに対して折り畳みがかかります。

$USERPROFILE\_vimrc
function! GetClojureFold()
    if getline(v:lnum) =~ '^('
        return ">1"
    else
        return "="
    endif
endfunction
function! EnableClojureFolding()
    setlocal foldexpr=GetClojureFold()
    setlocal foldmethod=expr
    nnoremap <Space> za
    vnoremap <Space> za
endfunction
autocmd FileType clojure call EnableClojureFolding()

キーアサインは、以下の通りです。

  • ziで、折り畳み機能のトグル
  • <Space>で、カーソル行のコードの折り畳みをトグル(ノーマルモードとビジュアルモード)
  • zMで、すべて折り畳む
  • zvで、カーソル行が見えるところまで折り畳みを開く
Last modified:2014/04/18 10:54:32
Keyword(s):
References:[FP: 関数型プログラミング] [Vim for Windows セットアップメモ]
This page is frozen.