まあ、置換とかは replace-buffer とかで一発なんだが、もちょっと凝ったことをしたいときがある。
今まではバッファを1行づつ処理するときは、
(while (null (eobp)) (let* ((beg (save-excursion (goto-bol) (point))) (end (save-excursion (goto-eol) (point))) (s (buffer-substring beg end))) (dbg-msgbox s) ; やりたい処理 (forward-line)))
とかやっていた。
すごく冗長に感じていたし、カーソルを動かしたりするのがやるせなかった。行頭と行末のポイントが欲しいだけなのに、save-excursion するのとか、不必要に let* 使うのもちょっと変だと思っていた。まあ、いろいろキモかった。
もうちょっと読みやすいのを考えてみた。今回はマクロで。
;;; バッファを1行ずつ処理する ;;; e.g. (loop-at-buffer (line) ;;; (dbg-msgbox line)) (defmacro loop-at-buffer ((var &optional buffer) &body body) (let ((gbuf (gensym)) (gstream (gensym))) `(let* ((,gbuf (cond ((null ,buffer) (selected-buffer)) ((bufferp ,buffer) ,buffer) (t (find-buffer ,buffer)))) (,gstream (if ,gbuf (make-buffer-stream ,gbuf) (error "\"~A\"という名前のバッファが見つかりません" ,buffer)))) (loop (let ((,var (read-line ,gstream nil))) (unless ,var (return nil)) ,@body)))))
これを使うと、上の例は↓こう書けるようになる。
(loop-at-buffer (line) (dbg-msgbox line))
わりといい感じになった。
パラメータは下記の2つ。
- 第1パラメータは、バッファの1行を取り出した文字列を入れる変数を指定する。省略不可。
- 第2パラメータは、回すバッファを指定する。省略時は現在のバッファになる。
ちなみに、マクロ内で使ってる make-buffer-stream は xyzzy の関数なんだが、引数に nil を渡すと適当になんかのバッファ(selected-buffer か?)に紐づいたストリームを返すみたいで、具合が悪い。その対策を入れてある。
・・・とここまでやっておいて、実は with-input-from-buffer があったことに気づく。
また車輪やってしまった。