モダン hook 入門。

Emacs の設定に慣れてくると、様々なモード別に設定をほどこしたくなってくるかと思います。
具体的には、プログラム言語によってスタイルを変えたいとか、特定のモードに限ってキーバインドを変えたいとか、モード別に起動するマイナーモードを設定したい等が思い浮びますが、そういった細かい設定に対応するために、Emacs には hook という仕組みがあります。

イベント発生時に設定を有効化できる、それが hook。

hook:フックとは、引っ掛けておくフックのことで、決められたイベント (例えば、ファイルを保存するときとか、モードを変更したときなど) に予め関数をセットしておくことで、イベント発生のタイミングで関数を実行させることができる仕組みです。
よくある例を挙げてみましょう。これはメジャーモード emacs-lisp-mode を呼び出したときに、実行される hook です。
ですが、この書き方には後述する問題があります

(add-hook 'emacs-lisp-mode-hook
	  '(lambda ()
	     (when (require 'eldoc nil t)
	       ;; (auto-install-from-emacswiki "eldoc-extension.el")
	       (require 'eldoc-extension nil t)
	       (setq eldoc-idle-delay 0.2)
	       (setq eldoc-echo-area-use-multiline-p t)
	       (turn-on-eldoc-mode))))

フックの書き方は help に

(add-hook HOOK FUNCTION &optional APPEND LOCAL)

とありますが、実際には、HOOK:フックのシンボル、FUNCTION: 関数のシンボルの二つを渡すだけになります。
巷に溢れている .emacs には一般的に FUNCTION の部分を lambda (無名関数) で表記しているケースが多いのですが、実は lambda はあまり望ましくありません。

hook に lambda を突っ込むと、内容が重複する。


最初に add-hook の式を評価評価して、その内容を表示しています。
次に eldoc-idle-delay の値を 0.2 から 0.4 へ変更して、再び評価してみました。

add-hook は内容を上書きするのではなく、単純に追加するため、最初に評価した時の lambda の式がそのまま残ってしまいました。
これでは、emacs-lisp-mode を起動する度に、2つの lambda 関数が実行されてしまう事になります。

関数を定義して hook に追加する。変更したい場合は、定義した関数を再評価する。

先ほどの hook は、この様に書く方が望ましいでしょう。

(defun emacs-lisp-mode-hooks ()
  (when (require 'eldoc nil t)
    ;; (auto-install-from-emacswiki "eldoc-extension.el")
    (require 'eldoc-extension nil t)
    (setq eldoc-idle-delay 0.2)
    (setq eldoc-echo-area-use-multiline-p t)
    (turn-on-eldoc-mode)))

(add-hook 'emacs-lisp-mode-hook 'emacs-lisp-mode-hooks)

新たに emacs-lisp-mode-hooks という関数を用意します。その中に、lambda 式の内容をそのまま記述します。
そして、add-hook 関数には、emacs-lisp-mode-hook のシンボル (関数名にクオートを付けたもの) を渡します。

emacs-lisp-mode-hook の内容が emacs-lisp-mode-hooks 関数だけになりました。非常にすっきりしています。
この場合、emacs-lisp-mode-hooks 関数の中身をいくら変更しても、emacs-lisp-mode-hook の中身は何も変更しません。安心して設定を書き換えることが可能になると言うわけです。

代表的な hook の紹介。

Emacs 本体の hook
after-save-hook
ファイル保存後に実行される
find-file-hook
find-file (C-x C-f) でファイルを開いたときに実行される
emacs-startup-hook
Emacs起動時に設定ファイルを読み込み終えてから一度だけ実行される
kill-emacs-hook
Emacs終了時に実行される

他にも沢山あるので、詳しくは info の Standard Hooks

メジャーモードの hook (メジャーモードを切り替えたときに実行される)
  • php-mode-hook
  • perl-mode-hook
  • cperl-mode-hook
  • emacs-lisp-mode-hook
  • lisp-interaction-mode-hook
  • shell-mode-hook
  • ansi-term-after-hook
  • js2-mode-hook
  • css-mode-hook
  • nxml-mode-hook
  • nxhtml-mode-hook
  • flymake-mode-hook
  • grep-setup-hook
  • skk-load-hook
  • isearch-mode-hook
  • isearch-mode-end-hook

まぁ、普通メジャーモードには xxx-mode-hook が用意されています。
Elisp で書かれたメーラーIRCなどのアプリケーションには豊富に hook が用意されているので、hook を極めることでかなり柔軟に自分好みの設定を施すことが可能となります。