interactive で任意の動作をさせる list について調べた

  • selection があれば selection の
  • region があれば region の
  • C-u <数字> されてれば、現在行の前後の<数字>行の
  • C-u されてればバッファ全体の
  • それ以外なら現在行の

start と end を受け取る interactive なんてのがあれば便利かなぁ、という気がしたので作ってみる。

また、LISTにて指定子に依らない任意の動作をさせることもできます。
cmds.lのrepeat-complex-commandの定義などを参照。

http://xyzzy.s53.xrea.com/reference/wiki.cgi?p=interactive

で cmds.l の repeat-complex-command を見てみると

(defun repeat-complex-command (sexp)
  (interactive
      (list
       (let ((*minibuffer-default-history* *minibuffer-complex-command-history*))
         (prog1
             (read-from-string (read-string "Redo: "
                                            :default (car *minibuffer-complex-command-history*)))
           (setq *minibuffer-complex-command-history* *minibuffer-default-history*)))))
  (eval sexp))

ヨクワカラナイ。read-from-string で入力された S-Expression を sexp として eval するんだろう、程度しかわからない。

とりあえず試してみる。

(defun test-interactive (from to)
  (interactive (list 0 100))
  (msgbox "from: ~S~%to: ~S" from to))
=> test-interactive
;; M-x test-interactive
from: 0
to: 100

なるほど。インタラクティブ指定子 - 日々ごちゃごちゃと考える のと同様に (list 引数とする値) を返せばいいようだ。しかもその list の各要素が関数の各引数に渡されるようだ。イメージ的にはこんな感じ

`(multiple-value-bind ,arg-list
     (values ,@<interactive-list の返す list>)
   ...)

んじゃ。

(defun test-interactive (from to)
  (interactive
      (multiple-value-call #'list
        (progn (msgbox "*prefix-args*: ~S~%*prefix-value*: ~S" *prefix-args* *prefix-value*)
          (cond ((pre-selection-p)
                 (values (selection-mark) (selection-point)))
                ;; ホントはここで region があったら、としたいのだけど
                ;; region の有無だけだと mark ってあんまり消えないので
                ;; ほぼ常に region はある。
                ;; ac-mode みたいに rv-region で、ならよさげ。だけど
                ;; めんどいので今回はパス
                ;;((mark)
                ;; (values (region-beginning) (region-end)))
                ((and *prefix-value*
                      (eq *prefix-args* 'digit-argument))
                 (values (save-excursion (forward-line (- *prefix-value*)) (point))
                         (save-excursion (forward-line *prefix-value*) (goto-eol) (point))))
                ((eq *prefix-args* 'universal-argument)
                 (values (point-min) (point-max)))
                (t (save-excursion
                     (values (progn (goto-bol) (point))
                             (progn (goto-eol) (point)))))))))
  (msgbox "from: ~S~%to: ~S~%~%~A" from to
          (buffer-substring from to)))
=> test-interactive


ちょっとハマったところとか

*prefix-args* が、C-u ... とか C-u <数字> ... とかのときにどうなるのかはっきり覚えてなかった。

(defun check-prefix-args ()
  (interactive)
  (msgbox "*prefix-args*: ~S~%*prefix-value*: ~S" *prefix-args* *prefix-value*))
=> check-prefix-args
(define-key spec-map #\p 'check-prefix-args)
=> t
;; C-c p
*prefix-args*: nil
*prefix-value*: nil

;; C-u C-c p
*prefix-args*: universal-argument
*prefix-value*: 4

;; C-u 3 C-c p
*prefix-args*: digit-argument
*prefix-value*: 3

いつまでも存在してる region って・・・

普段 region をまったく使わないせいか、region というか mark がずっと存在はしてるなんてことに気付かなかった。ac-mode の「rv-region で反転してたら」てのはそーゆーことだったのか。

(save-excursion (goto-bol) (point)) とか

めんどくね?この式しょっちゅう書いてる気がする。