lisp-repl-mode for xyzzy

Lisp処理系には付き物のREPLですが、xyzzy lisp用の物はありそうで見つからなかったので作りました。

標準の*scratch*では面倒だった*package*の切り替えが簡単になったり、色々捗ります。
http://gyazo.com/457ac8f8b6067f151ccdefb0a7cdad72.png

標準の*scratch*に比べて良い点

  • カレントパッケージを切り替えられる
    • *buffer-package*の更新も行うので、ac-modeやldocを導入している場合色々便利に
  • REPL変数が使える
  • 便利なREPLコマンド (自作可)

注意点

rx.lを導入している場合、rx.l内で使用されているシンボル'ed::**と
xl-repl.l中でlispパッケージよりexportしようとしている'lisp:**がコンフリクトします。
rx.lより先(ni/setupより先)にxl-replをロードするか、siteinit.lの先頭に

(export '(lisp::** lisp::*** lisp::++ lisp::+++ lisp::// lisp::///) :lisp)

というコードを書くなどして回避してください。

インストール

NetInstallerにhttp://youz.github.com/xyzzy/package.lを登録して*scratch*よりインストールし、.xyzzy (or siteinit.l) に

(require "xl-repl")

と追記してください。

設定

ac-modeやldoc2を導入している場合、以下の設定を.xyzzyに加えておくと便利です。

; ldoc2 を使っている場合
(push 'ed:lisp-repl-mode ed::*ldoc-activated-mode-list*)
; ac-mode を使っている場合
(push 'ed:lisp-repl-mode ed::*ac-mode-lisp-mode*)

REPLバッファ上で、パッケージ修飾子無しでもカレントパッケージからシンボル名補完ができたり、ldoc2での説明表示ができるようになります。

起動

M-x start-repl

キーマップ

REPLバッファ用キーマップ repl:*keymap* は、 ed:*lisp-mode-map* をベースにして以下のキーバインドを追加/上書きしています。

  • RET -- repl::newline-or-eval-input (入力した式を評価 / 書きかけならlisp-newline-and-indent)
  • C-l -- repl::clear-repl (REPLバッファをクリア)
  • M-C-a -- repl::beginning-of-input (入力中の式の先頭へ移動)
  • M-C-e -- repl::end-of-input (入力中の式の最後へ移動)
  • C-p -- repl::previous-input (入力履歴 - 戻る)
  • C-n -- repl::next-input (入力履歴 - 進む)

REPLコマンド

以下のキーワードシンボル(と引数)を入力するとREPLコマンドを実行します。

:cd (&optional dir)
    ; default-directoryをdirへ移動 (dir省略時はdefault-directoryを表示)
:describe (symbol-or-package-name)
    ; パッケージ/変数/定数/関数の説明を表示
:dir (&optional wildcard)
    ; default-directoryのファイルを列挙
:expand (form)
    ; formをmacroexpandして表示
:help (&optional pattern)
    ; REPLコマンドの説明を表示
:load (name)
    ; *load-path*にdefault-directoryを含めて(load-library 'name)を評価
:ls (&optional pat (pkg *package*))
    ; パッケージ内の変数/定数/関数シンボルを列挙
:lsall (&optional pattern)
    ; 全パッケージの変数/定数/関数シンボルを列挙
:lsext (&optional pattern (pkg *package*))
    ; パッケージよりexportされている変数/定数/関数シンボルを列挙
:lspkg (&optional pattern)
    ; パッケージ名を列挙
:mkpkg (name &rest options)
    ; (make-package 'name [options])を評価し、*package*を作成したパッケージに変更
:package (name)
    ; (in-package 'name)を評価
:require (name)
    ; *load-path*にdefault-directoryを含めて(require 'name)を評価
:time (form)
    ; formを評価し、実行時間(秒)を表示

適当に省略してもOK。

user> :pa repl.command     ; :packageコマンド
#<package: repl.command>

repl.command> :de package  ; :describeコマンド
<Function> repl.command::package (name)
    ; (in-package 'name)を評価

:cd コマンドは今の所引数に文字列しか受け付けないので注意

user> :cd site-lisp
不正なデータ型です: site-lisp: pathname

user> :cd "site-lisp"
c:/usr/xyzzy/site-lisp

user> :cd "C:/Users/yz"
C:/Users/yz

他のコマンドはシンボルを渡してもOK。

user> :dir *x*
[C:/Users/yz/]
.VirtualBox/
Dropbox/
lynx.rc
lynx_bookmarks.htm
VirtualBox VMs/

REPLコマンドの自作

パッケージrepl.command内に関数を定義すると、その関数がREPLコマンドとして使えるようになります。
定義例はソースを見てください。
repl.commandパッケージはlispパッケージをuseしていないので、(in-package :repl.command)と書いてから追加コマンドを定義するのは色々面倒です。
lispやeditorをuseしている適当なパッケージから(defun repl.command::hoge () ...)と書く方が良いでしょう。
(repl.commandパッケージがlispパッケージをuseしていないのは、loadやrequireといったlispパッケージにあるような名前のコマンドを簡単に作成したかった為です)

不具合等

見つかりましたら githubのissues@Yubeshiまで。