特定の major-mode "以外" の場合に hook を設定にする emacs lisp

やり始めたら 30 分くらいハマってたのでメモ。

以下コードは、 rust-mode "以外" の場合に after-save-hookexecutable-make-buffer-file-executable-if-script-p を設定するものです。

(defun after-change-major-mode-hook-fn ()
  (unless (eq major-mode 'rust-mode)
    (add-hook 'after-save-hook 'executable-make-buffer-file-executable-if-script-p nil t)))
(add-hook 'after-change-major-mode-hook 'after-change-major-mode-hook-fn)

解説

  • add-hook は optional な第4引数に nil 以外を設定すると buffer-local な hook を設定可能
  • after-change-major-mode-hook は、バッファのメジャーモードが設定された後に呼び出される hook

上記の合わせ技で、特定メジャーモード以外の場合に hook を設定することを実現しています。

できなかったこと

上記にたどり着くまでに以下を試してみましたが、うまくいきませんでした。原因は分かっていません。

  • after-save-hook に global (非 buffer-local) な hook を追加する。rust-mode-hookremove-hook を buffer-local オプション付きで呼び出し、追加した hook を削除する。
  • change-major-mode-hook で buffer-local な hook を追加する。 rust-mode-hook の場合のみ remove-hook を呼び出し、追加した hook を削除する。

hook の動作原理を知らないと、このあたりはハマりどころとなって試行錯誤するハメになりそう。。。勉強せねば。。。

動機

Rust の attribute syntax が変更されたため、executable-make-buffer-file-executable-if-script-p が、以下のような 「crate_id が設定されているファイル」を 「shebang 付きの実行可能ファイル」と認識してしまうようになってしまいました。

#![crate_id = "foo"]
#![crate_type = "bin"]

fn main() {}

executable-make-buffer-file-executable-if-script-p は、スクリプトファイルを作成する際の chmod してパーミッション設定する手間を省いてくれる非常に便利な関数なのですが、rust のソースコードを保存する度に execute のパーミッションを設定されるのは非常に鬱陶しいかったため、rust-mode でだけ hook を削除したかった、というのが動機です。

最後に

Rust 0.10 が先日リリースされたので、みんなで使いましょう!!!

今回のリリースから、Mac/Linux 向けのバイナリや、公式の nightly-build が提供されるようになりました。もうLLVMコンパイルを長時間待つ必要はありません!!! あなたと rustlang, 今すぐダウンロード。