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まで。

投稿モードバッファ (xyttr Advent Calendar 25日目分)

3日遅れでxyttr Advent Calendar 2011の最終記事です。

xyttrはあまりツイートしない人間が作ってる事もあって投稿機能に力が入ってないのですが、もうちょっと何かあった方が良いよなーという事で今後のために投稿用バッファのプロトタイプを作ってみました。
hatena-haiku-modeの投稿画面を参考にしています。

https://gist.github.com/1522725

このファイルをロードすると投稿系のキーバインド(u, @, S-@, r u, r r)が下図のような投稿用バッファを開くコマンドに変更されます。

http://gyazo.com/885210cbda66ae1d9d67c0c41e60b635.png

C-c C-cで罫線以下のテキストが投稿されます。
今の所特殊な編集機能はありませんが、ミニバッファでの操作制限(undo等)がなくなっただけでも結構便利になったかと思います。というか今までが不自由すぎたか…

年末・年始で色々試して、次のリリースまでに本体へ組み込みたいと思います。

xyttrで画像投稿 (xyttr Advent Calendar 24日目分)

完全に遅刻ですがxyttr Advent Calendar 2011の記事です。

xyttrでやる意味あるの? というと利便性上ほとんど意味ないですが、出来てしまった物はしかたがない。

https://gist.github.com/1521515

xyttr::tweet-with-photoを適当なキーに割り当てるか、M-x xyttr::tweet-with-photoで実行します。

http://gyazo.com/e80dbf64a31d31055ac9428682a307d7.png

実装ですが、connect関数使ってベタで書いてるのはxhrだとこの辺の問題で投稿が失敗(500)してしまうため。
Msxml2.XMLHTTP.6.0を使うようxhrを書き換えて試してみましたが、タイムライン系api等のapi.twitter.comへのリクエストは問題なかったものの画像アップロードは403でした。
ちゃんとパケット調べてないけどやはり余計な0x00で問題起きてそう。

read it laterをxyttrで見る (xyttr Advent Calendar 23日目分)

この記事はxyttr Advent Calendar 2011の記事です。

14日目のタイムラインコマンド自作の応用で、read it laterに貯め込んだURLをxyttrで表示してみます。
read it later viewer for xyttr

ロード後、M-x readitlaterで実行します。
http://gyazo.com/7994e8701b5c3204ad3e776792792469.png

URLの上でmキーを押すと既読マークを付けます、が手抜きなのでステータスバーにメッセージが出るだけで見た目変わりません。起動しなおすとエントリーが消えてます。

もう1つ、read it laterにURLを登録するコマンド ril-focused-url を用意してあります。このコードではr-i-lというキーシーケンスに割り当ててますが適当に変更してお使い下さい。

timeline-draw-statuses (xyttr Advent Calendar 22日目分)

この記事はxyttr Advent Calendar 2011の記事です。

関数xyttr::timeline-draw-statusesを使った小ネタを2つ。

- *function* timeline-draw-statuses (buf statuses &key (point 0))

    ツイートデータを整形してバッファに出力します。

    * buf -- 出力先バッファ
    * statuses -- ツイートデータのリスト
    * point -- 出力先位置

◆ 栞を挟む

(in-package :xyttr)

;; ツイートデータ生成
(defun make-tweet (status-id name text)
  `(("id" . ,status-id)
    ("user" ("screen_name" . ,name))
    ("text" . ,text)
    ("created_at" . ,(format-date-string "%a %b %d %H:%M:%S %Z %Y"))))

;; カーソル下のツイートの1つ前に栞を挟む
(defun kokomadeyonda2 ()
  (interactive)
  (when (eq buffer-mode 'xyttr-timeline-mode)
    (let ((p (1- (entry-point)))
          (tw (make-tweet nil "xyttr_shiori"
                          (format nil "~V@{◇◆~}"
                                  (ash (window-columns) -2) t))))
      (w/buffer-modifying ()
        (timeline-draw-statuses (selected-buffer) (list tw) :point p)))))

(define-key *xyttr-timeline-keymap* '(#\C-k #\C-m) 'kokomadeyonda2)

http://gyazo.com/81afadeac789639f2089865ebaf30262.png
好きな模様を作って使おう。

◆ トレンドを表示

13日目に紹介したtrends apiを使います。

(define-api trends (woeid exclude)
  :path (format nil "/1/trends/~A.json" woeid))

(defun show-trend ()
  (interactive)
  (when (eq buffer-mode 'xyttr-timeline-mode)
    (let ((buf (selected-buffer)))
      (api-trends-async
       :woeid +woeid-tokyo+
       :onsuccess
       (lambda (res)
         (w/json (trends) (car res)
           (let* ((text (format nil "trends:~{ ~A~}"
                                (mapcar #'(lambda (tr) (json-value tr name))
                                        trends)))
                  (tw (make-tweet nil "xyttr_trend_info" text)))
             (save-excursion
               (set-buffer buf)
               (w/buffer-modifying ()
                 ;; バッファ先頭へ出力
                 (timeline-draw-statuses buf (list tw))
                 (refresh-screen))))))))))

(define-key *xyttr-timeline-keymap* #\t 'show-trend)

http://gyazo.com/f94527999d086b92ee3c8f7b8dacac58.png

トレンドの使い途イマイチ分からん

xyttr起動用anything source (xyttr Advent Calendar 1日遅れの21日目)

この記事はxyttr Advent Calendar 2011の記事です。

ランチャーとして使えるxyttr専用のanything sourceを作ってみました。
xyttrの各種コマンド起動用 anything source

※ .xyzzyかsite-lispからロードして下さい。

xyzzy用anythingは配布元のサイト(http://ottt.x0.com/)が死んでるっぽいので、ミラーとして下記URLに配布ファイルをアップしておきました。
http://dl.dropbox.com/u/215714/xyzzy/ottt-mirror/anything0.1.5.lzh
未インストールの場合このファイルをDLして手動でインストールしてください。

M-x anything-xyttr で起動します。
http://gyazo.com/da615a6b7e4b6b038f1b24c99bcfa382.png

グループの順番変えられないのがちょっと残念。
しかしanythingのカスタムソース表示は色々応用が効きそう。リスト選択とかのUIとして組み込んでしまいたい。

xyttr v1.1.2 release (xyttr Advent Calendar 1日遅れの20日目)

この記事はxyttr Advent Calendar 2011の記事です。

◆ v1.1.1 → v1.1.2の変更点

  • タイムラインバッファリロード時に実行するフック *timeline-reload-hook* を追加
  • xyttr-listコマンドに引数としてリスト名(@screen_name/listname)を渡して起動できるように修正
  • search-timelineでin_reply_to_status_idをツイートデータに含めるよう修正

以下詳細

◆ リロードフックの追加

*timeline-reload-hook* はタイムラインバッファのリロード時(バッファ更新完了後)にrun-hook-with-args関数で実行され、フック関数には引数として新着ツイートのリストが渡されます。
昨日紹介したGrowlでの新着通知機能の亜種をこのフックで実装してみると

(require "growl")
(in-package :xyttr)

;; home-timelineの新着を通知
(defun notify-new-tweets (tweets)
  (when (eq :xyttr (timeline-mode buffer-timeline))
    (when (> (length tweets) 10)
      (setq tweets (subseq tweets 0 10)))
    (dolist (tw tweets)
      (w/json (user.screen_name user.profile_image_url text) tw
	(growl:notify (concat "@" user.screen_name) text
		      :name "xyttr" :icon user.profile_image_url)))))

(add-hook '*timeline-reload-hook* 'notify-new-tweets)

こんな感じに。タイムラインバッファを開いている時しか通知できませんがシンプルに書けます。

◆ xyttr-listコマンドの変更

今まではxyttr-listコマンドには引数がなく、処理の中でread-string関数を使ってリスト名の入力を取得していましたが、今回の修正でリスト名を引数として渡せるようになりました。

anythingからリストを起動する例

(require "anything/anything")
(in-package :user)

(add-anything-sources
 :name "xyttr-list"
 :candidates (lambda () (mapcar #'car xyttr::*list-subscriptions*))
 :action (lambda (l) (xyttr-list l)))

http://gyazo.com/8a73059f3b18a94da7d7e8611a178a5b.png

M-x xyttr-list での実行は相変わらずcomplete+導入推奨です。

◆ search-timelineの変更

返信先ツイート表示(pキー)がちょっと速くなりました。




そろそろリスト関係の機能を強化するべきかなーと思いつつ、リストほとんど使ってないのでなかなかやる気が起きない…