本記事では use-package の 2.4.4 を対象に、マクロ展開後のコードを例示します。
対象とする読者は他の人の設定を参考に use-package を使っているが、内部処理の詳細を理解できていない方です。
本記事と合わせて @conao3 さんの https://qiita.com/conao3/items/dc88bdadb0523ef95878 を読んでいただくとより理解が深まります。
上記の記事にはleaf.elだけでなくuse-packageの解説が含まれます。
本記事で行うようなマクロ展開の例も含まれており、実質的にこの記事の上位互換の記事です。
なお筆者はSpacemacsを使用しているため、環境依存による差異があるかもしれません。
use-package について
use-packageはパッケージの読み込みや設定を統一的なフォーマットで行えるようにするためのマクロです。
:ensure
キーワードを使ってパッケージの読み込みをする機能はあるものの、本質的な用途はインストール済みのパッケージの設定ということになります。
当初、筆者は「マクロである」と言われても、あまりピンときませんでしたが、実際に展開されたコードを見ると本質的な機能はシンプルであることが分かりました。
デフォルトの設定のままマクロ展開を行ったコードにはエラー処理が含まれるため、シンプルであることに気づきづらいことも事実です。
本記事では事前設定としてuse-packageの設定をいくつか紹介し、その後pp
, macroexpand
の関数を使ったマクロの展開の例を紹介します。
事前設定
マクロ展開後のコードを簡略化するため下記の設定を行います。
use-package-expand-minimally
(setq use-package-expand-minimally t)
エラー処理などを省いた、簡略化したコードで展開をするオプションです。
docsには
It can also help with reading macro-expanded
definitions, to understand the main intent of what's happening.
(翻訳) マクロ展開された定義を読むことで、主な処理の概要を理解することに役立ちます
とある通り、全体像の理解に役立つためこのオプションを有効にします。
use-package-inject-hooks
(setq use-package-inject-hooks nil)
default値はnilです。
spacemacs ではこのオプションが有効化されているため、nilに戻します
https://github.com/syl20bnr/spacemacs/blob/b28d65b7aa7a936cdab7fc0453df86b21ac1496f/core/core-use-package-ext.el#L75
use-package-use-theme
(setq use-package-use-theme nil)
:custom
キーワードの展開後のコードを簡略化します。
このオプションに nil 以外の値を設定すると :custom
で設定した値が custom file に保存されることを防ぐことができるオプションのようです。
マクロ展開
README.md の「Getting started」セクションの設定例をマクロ展開してみます。
最もシンプルな例
キーワードを何も与えなければ、展開されるコードは require 関数1行のみです。
(use-package foo)
(setq use-package-expand-minimally t)
(setq use-package-inject-hooks nil)
(pp
(macroexpand
'(use-package foo)))
;; (require 'foo nil nil)
:init
:init
キーワードに記載した処理は require 関数の前に実行されます。
(use-package foo
:init
(setq foo-variable t))
(pp
(macroexpand
'(use-package foo
:init
(setq foo-variable t))))
;; (progn
;; (setq foo-variable t)
;; (require 'foo nil nil))
:init
と :config
の併用
:init
の処理は require の前に :config
の処理はrequire関数の後に実行されます。
(use-package foo
:init
(setq foo-variable t)
:config
(foo-mode 1))
(pp
(macroexpand
'(use-package foo
:init
(setq foo-variable t)
:config
(foo-mode 1))))
;; (progn
;; (setq foo-variable t)
;; (require 'foo nil nil)
;; (foo-mode 1)
;; t)
use-packageのREADME.mdではより複雑な例が記載されていますが、同様の方法でマクロ展開後のコードを調べることができます。
:init
, :config
, :custom
の併用
最後に :init,
:config
, :custom
の3つのキーワードのマクロ展開例を記載します。
展開後のコードを見ると :custom
, :init
はrequire前に、:config
はrequire後に出力されることが分かります。
(use-package foo
:init
(setq foo-variable t)
:config
(foo-mode 1)
:custom
(bar-variables t)
)
(setq use-package-use-theme nil)
(pp
(macroexpand
'(use-package foo
:init
(setq foo-variable t)
:config
(foo-mode 1)
:custom
(bar-variables t)
)))
;; (progn
;; (customize-set-variable 'bar-variables t "Customized with use-package foo")
;; (setq foo-variable t)
;; (require 'foo nil nil)
;; (foo-mode 1)
;; t)
おわりに
筆者がuse-packageの設定中に陥った「:custom
キーワードの処理はrequire後に呼び出される」という誤解が、記事執筆のきっかけとなりました。
実際には:custom
キーワードの処理はrequire前に呼び出されます
(より正確には use-package-keywords
の設定に準ずるが正しいはずです)
pp
や macroexpand
を使いながら展開後のコードを読み解く方法は、他のライブラリでも参考にできるため、自分向けの備忘録として公開しておこうと思います。
参考文献
- [正式リリース]leaf.elで雑然としたEmacs設定ファイル「init.el」をクリーンにする
- https://qiita.com/conao3/items/dc88bdadb0523ef95878
- 「use-packageのキーワード解釈の非統一性」のセクションでは本記事と同じ内容の実行例を記述している