Chapter 03

autoload と with-eval-after-load

zk_phi
zk_phi
2020.09.24に更新

全人類がやるべき設定その2です。

たとえば Perl 言語の設定は Perl のファイルを開くまで必要ないですし、Web ブラウザ eww の設定は eww を起動するまで必要ありません。それらを一度も使うことなく Emacs を閉じることがあれば、設定にかかった時間はまるまる無駄になってしまいます。

これを防ぐのが autoloadwith-eval-after-load です。

正統派な使い方

autoload の基本的な使い方は、主に以下の二つです。

  • コマンドを提供するパッケージのロードを、そのコマンドが実行されるタイミングまで遅延する
  • 特定の言語モードを拡張するパッケージのロードを、そのモードに入るタイミングまで遅延する

設定例を挙げます:

;; hoge パッケージを読み込む
(require 'hoge)

;; hoge パッケージの初期設定をする
(hoge-initialize)

;; hoge パッケージのコマンドにキーを割り当てる
(global-set-key (kbd "C-x h") 'hoge-run)

このようなコード片は、たいていの場合、次のように書き換えることができます。

;; hoge-run コマンドが実行されそうになったら慌てて hoge パッケージを読み込む
(autoload 'hoge-run "hoge" nil t)

;; hoge パッケージが読み込まれたらすぐに初期設定をする
(with-eval-after-load 'hoge
  (hoge-initialize))

(global-set-key (kbd "C-x h") 'hoge-run)

書き換え後のコード片は、キーバインドの設定だけを起動時に行い、パッケージの読み込みは hoge-run コマンドが実行されるまで遅延します。パッケージが読み込まれると、 with-eval-after-loadhoge-run の処理に移る前に割り込んで初期設定を実行するので、ほとんどの場合これで問題ありません。

この書き換えによって、 hoge-run コマンドがそもそも使われなければ hoge パッケージの設定はそもそも行われないし、仮に使うとしても起動時にすべてのパッケージの設定を行うよりはずっと起動時間が短くなります。

特定の言語だけで使用するパッケージなども同様に、トリガーだけを設定しておいて読み込みを遅延することができます。

(autoload 'sugoi-python-minor-mode "sugoi-python")
(add-hook 'python-mode-hook 'sugoi-python-minor-mode)

「起動時に必要とは限らないな」というパッケージに片っ端からこれを適用しましょう。突き詰めると、フォントやカラースキームの設定、ごく基本的なパッケージの読み込み(「かっこを光らせる」など)くらいしか、起動時に必須な設定はないことに気づくと思います。となれば当然、起動はかなり速くなります。


ところで自分はずっと with-eval-after-load (eval-after-load) の第一引数をファイル名の文字列にして使っていたのですが、

(with-eval-after-load "hoge"
  '(hoge-initialize))

シンボル ('hoge) にしておいた方がわずかに速いことに最近気づきました。前者はパッケージが読み込まれたかのチェックに正規表現を使うのに対して、後者はたんに eq で比較されるためです。オーダーは変わらないですが定数倍が良いです。

自作関数も遅延する

ある自分で実装した関数があって、これがあるパッケージ foo を利用している場合、次のように書くことで軽率に遅延することができます。

(autoload 'my-special-foo-command "foo" nil t)

(with-eval-after-load 'foo
  (defun my-special-foo-command ()
    ...))

こう書いても微々たる差かもですが…。

(defun my-special-foo-command ()
  (require 'foo)
  ...)

まとまった数の自作コマンドがある場合は、それらをまとめて別ファイルに移動してしまい、そのファイル全体をまるっと autoload するのも手です。