ちょびっと便利なコマンド

息抜きとか思ってちょっとだけ xyzzy lisp。やっぱり「こーゆーことさせたい」から実装するまでのフリクションが低い。慣れの問題だろうか。

行頭とインデントのとこ行ったり来たりする C-a

(defun beginning-of-line+ ()
  (interactive)
  (if (and (bolp)
           (eql *last-command* *this-command*))
      (back-to-indentation)
    (goto-bol)))

(global-set-key #\C-a 'beginning-of-line+)

これ書いててデフォルトで M-m が back-to-indentation に bind されてることを知った。

#\( や #\{ を入力したら #\) や #\} を後ろに挿入

ただしてきとーにサボる

;prototyping してるときは、defvar よりも
;書き直して eval できる defparameter が便利
(defparameter *match-chars*
  '((#\( . #\)) (#\{ . #\}) (#\[ . #\])))

(defun insert-open/close-char ()
  (interactive "*")
  (let* ((oc *last-command-char*) ;open char
         (cc (cdr (assoc oc *match-chars*)))) ;close char
    (insert oc)
    (when (or (parse-point-syntax)
              (null cc))
      (return-from insert-open/close-char t))
    (save-excursion
      (let ((opoint (point)))
        (while (up-list -1 t)
          (let ((p (point)))
            (unless (and (ignore-errors (goto-matched-parenthesis))
                         (ignore-errors (goto-matched-parenthesis))
                         (= (point) p))
              (goto-char opoint)
              (insert cc)
              (return))))))
    t))

(define-key ed::*lisp-mode-map* #\( 'insert-open/close-char)

最初 *last-command-char* を思い出せなくてちょっと苦戦した。
syntax-table を参照してこの開き括弧に対応する閉じ括弧を取得するとかいう機能が欲しかった。
up-list でその場を含む括弧の先頭へ行って goto-matched-parenthesis x2 すると、ちゃんと対になってれば元の位置に戻ってこれる。これをトップレベルまで見てって、どっかで合ってなかったら閉じ括弧が足りない。
そういへば前置引数に対応してない。

#\" や #\' を入力したらすぐ閉じる

これもてきとーにサボる

(defun open/close-string ()
  (interactive "*")
  (insert *last-command-char*)
  (save-excursion
    (let ((opoint (point)))
      (when (and (forward-paragraph)
                 (eql (parse-point-syntax) :string))
        (goto-char opoint)
        (insert *last-command-char*))
      t)))

(define-key ed::*lisp-mode-map* #\" 'open/close-string)

文字列が閉じてないとその後ずっと文字列になるので、てきとーにそのパラグラフの最後まで飛んで文字列になってたら閉じられてないものとみなす。paragraph-separate の設定によるけど、空行を含む文字列があると上手くいかない気がするが、所詮文字列が閉じられないだけなのであんまり気にしないことにした。