minibuffer で何かする

コマンドとか実行中に minibuffer で入力させて文字列を受け取るのに、なんか色々する方法。

単に文字列を入力させるなら `completing-read` 使えばいいんだけど、キーマップを変更したりインクリメンタルに何かしたりするには `*enter-minibuffer-hook*` と `*exit-minibuffer-hook*` を使う。

*enter-minibuffer-hook*

`completing-read` とか `(interactive)` で minibuffer からなんか読み取るようなコマンドで minibuffer に突入する前に実行される hook。
引数として

  1. minibuffer 内で使う buffer
  2. ヒストリ変数のシンボル

が渡される。この hook が走る時点では current-buffer は元の buffer のままなので、(`save-excursion` なりしといて)受け取った buffer に `set-buffer` してしまえば、あとは `use-keymap` とか `(setq keyword-hash-table ...)` とか好きにすればいい。

*exit-minibuffer-hook*

minibuffer で入力終わった後に呼ばれる。引数は

  1. minibuffer で使った buffer
  2. minibuffer に入力された文字列

が与えられる。デフォルトで epilogue-minibuffer という関数が登録されてて、ヒストリを更新したりしてる。

使い方

特定の場合だけ minibuffer で変なことさせるには

A: *enter-minibuffer-hook* の中で判別

`completing-read` とかで :history に指定したシンボルをチェックするとか、あるいはなんかフラグ立てといてそのフラグをチェックするとか。

(add-hook '*enter-minibuffer-hook*
  (defun setup-magical-minibuffer (buff hist)
    (when (eq hist 'magical)
      ...)))
B: *enter-minibuffer-hook* をローカライズ

`let` で `*enter-minibeffer-hook*` をローカライズ*1して、ローカルな `*enter-minibuffer-hook*` に `add-hook` すると、ここで `add-hook` された関数は `let` から抜けると消えてくれる。

(defun do-magical-stuff-with-minibuffer ()
  (let ((*enter-minibuffer-hook* *enter-minibuffer-hook*))
    (add-hook '*enter-minibuffer-hook*
      (defun setup-magical-minibuffer ()
        ...))
    (completing-read "> " nil)))

*1:ローカライズ」て言い方が正しいのか知らないけど、スペシャル変数を `let` とかで局所化するのをこう呼んでる。そのスコープを抜けると元に戻ってくれるんで、後のことはあんまり気にせずに変更できる。