EmacsがC-gで終了しなくなったときの対処方法
Emacs使いならば、Emacsが無限ループに陥ったときに「やめれ!」と叫ばんばかりにC-gを押すのが癖になっているだろう。しかし、それでも止まらないときがある。その原因は「inhibit-quit」変数がtになっているからだ。
-- Variable: quit-flag `inhibit-quit'が`nil'であれば, この変数が`nil'以外であるとEmacsは ただちに中断する. `C-g'は, `inhibit-quit'に関わらず, 通常, `quit-flag'に`nil'以外を設定する. -- Variable: inhibit-quit この変数は, `quit-flag'が`nil'以外の値に設定されたときにEmacsが中 断すべきかどうかを決定する. `inhibit-quit'が`nil'以外であると, `quit-flag'に特別な意味はない.
だから、たとえばこんなコマンドを実行したら、CPU喰らいつくししかも止められない。死ねます。良い子は真似しないように。
(defun die () (interactive) (let ((inhibit-quit t)) (while t)))
inhibit-quitがどんなに危険な変数であるかはわかっただろう。
Emacs Lispのソースをgrepしてみると、けっこう使われているんだわ。font-lockにも使われているから、めちゃ長い行の色付けが死ぬほど遅くて「やめれ」と叫んでも止まらない。待つしかない。なんてこったい。
inhibit-quit はrun-with-idle-timer等の遅延実行に使われる関数にも使われている。たとえば、このstart-loopコマンドを実行すると1秒後に暴走する。
(defun do-loop () (while t)) (defun start-loop () (interactive) (run-with-idle-timer 1 nil 'do-loop))
この対処法はrun-with-idle-timerが実行する関数内で inhibit-quit を nil にしておくことだ。start-loop2は止まる。
(defun do-loop2 () (let ((inhibit-quit nil)) (while t))) (defun start-loop2 () (interactive) (run-with-idle-timer 1 nil 'do-loop2))
run-with-idle-timer 等の遅延実行関数を使うときは、 inhibit-quit を nil にしておこう!!
anything.elでは anything-input-idle-delay を設定していたり、 delayed ソースを実行するときにこの関数が使われている。anything-c-moccurを使っていて無限ループになってC-gが効かなくて泣く泣くEmacs再起動した人もいるだろう。原因は検索結果の出力(anything.el側)で run-with-idle-timer が使われていて、その内部で inhibit-quit が t になっていることだ。anything.elを修正したのでよろしく。