Emacsの設定を書いていると、 setq や setopt や custom-set-variables のあたりで、だんだん話がややこしくなってくる。普通の変数は setq でよさそうに見えるし、 M-x customize は便利だし、 defcustom で定義された変数には専用のやり方がありそうにも見える。
実際、それぞれの役割は少しずつ違う。しかも Customize を使い始めると、保存先の custom-set-variables は Emacs によって自動更新されるので、その中に長いコメントやメモを書き込みにくい。設定値はここに保存したいが、設定理由は別に残したい、という気持ちになる。
普通の変数は setq 、現在のバッファだけに効かせたいものは setq-local 、バッファローカル変数のデフォルト値は setq-default 、 defcustom で定義されたユーザーオプションは主に setopt 、必要に応じて customize-set-variable を使う、という分け方でおおむね良いだろう。 custom-set-variables は自分で育てる設定というより、 Customize が保存する領域として custom-file に分けておく方が扱いやすい。
この記事では、そのあたりをどう整理して、コメントを書きたい設定と Customize に任せたい設定をどう分けるのが扱いやすいかを考えてみる。
それぞれの役割
Emacsの設定には、見た目は似ていても役割の異なる書き方がいくつかある。
defvar
defvar は、変数を定義するための仕組みだ。主に Lisp 側で「この名前の変数を使う」と宣言する時に使われる。
(defvar my-cache-directory "~/.cache/my-app")これは利用者に Customize で変更してもらうための宣言というより、プログラム側が使う変数を用意するためのものだ。
defcustom
defcustom も変数を定義するが、こちらは利用者が Customize 経由で設定する事を前提としたユーザーオプションを定義する時に使う。
(defcustom my-enable-feature t
"Whether to enable my feature.")
defcustom で定義された変数は、 M-x customize から見つけやすく、型や説明文、専用の setter などを持てる。 setopt や customize-set-variable が話題に出てくるのは、こういう変数を設定する時だ。
setq
setq は、現在見えている変数束縛に値を設定するための基本手段だ。
(setq indent-tabs-mode nil)普通の変数を init.el で設定する時は、まずこれを考えればよい。
setq-local
setq-local は、現在のバッファだけで有効なバッファローカル変数として値を設定する時に使う。
(setq-local fill-column 72)
setq と違って、他のバッファには影響しない。その場限りのバッファ単位の設定を書きたい時はこちらを使う。
setq-default
setq-default は、主にバッファローカル変数のデフォルト値を設定するために使う。
(setq-default tab-width 4)
setq と似ているが、意味は少し違う。 setq はその場の値を変えるのに対し、 setq-default は新しいバッファでも参照されるデフォルト値を変える。
setopt
defcustom で定義されたユーザーオプションを手で設定するなら、最近は setopt が分かりやすい。
(setopt ring-bell-function 'ignore)
setopt は Custom 用の setter を通せる事があるため、 setq より適切な場合がある。
customize-set-variable
customize-set-variable も defcustom な変数を設定する関数だ。
(customize-set-variable 'ring-bell-function 'ignore)
最近の init.el では setopt の方が自然に見える事が多いが、次のような時には customize-set-variable を使う理由がある。
- 古い Emacs との互換性を意識したい時
- Custom の流儀で設定している事を明示したい時
- 既存の設定ファイルがすでにこの書き方で統一されている時
custom-set-variables
custom-set-variables は、普段自分で書くための関数というより、 M-x customize が設定を保存する時に書き出すブロックだ。
(custom-set-variables
'(ring-bell-function 'ignore)
'(tab-width 4))これは人間が説明を書き足しながら育てる形式というより、Emacsが機械的に更新する保存形式として扱う方がしっくりくる。
Customizeを使いたいが、コメントも残したい
僕が困るのはここだ。基本的には Customize を使いたい。UIで値を試せるし、 defcustom な変数を探しやすい。しかし保存先の custom-set-variables は自動更新されるので、その中に長いコメントや補足を書きにくい。
もちろん短いコメントなら何とかなる場合もある。しかし、設定理由や比較した選択肢、試した結果、注意点のような長いメモを残したくなると、急に扱いづらくなる。自動生成される領域なので、人間向けの注釈を書く場所としては落ち着かない。
この問題は、 Customize が悪いというより、 custom-set-variables が「機械が更新する保存形式」だから起きる。
うまい運用
いちばん扱いやすいのは、値と理由を分ける事だ。
- 値そのものは
Customizeに保存させる - 保存先は
custom-fileに分離する - 設定理由や長いメモは
init.elや別の Org/Elisp ファイルに書く - コメント込みで管理したい設定だけは、
setoptやcustomize-set-variableで手書きする
たとえば、まず M-x customize で設定を試し、気に入ったものだけを後で手書き設定へ昇格させる、という流れがある。
(setq custom-file (expand-file-name "~/.emacs.d/custom.el"))
(load custom-file t)
このように custom-file を分けておけば、 init.el は人間が読む設定、 custom.el はEmacsが書く設定、という役割分担ができる。
手書き設定へ昇格させる
ある変数について、値だけではなく理由も残したくなったら、その変数は手書き設定へ移した方がよい。
;; テーマによってはベルが激しくて気になるため無効にする
(setopt ring-bell-function 'ignore)こうしておけば、なぜその値なのかを隣に書ける。あとから見返した時にも理解しやすい。
逆に、 custom-set-variables の中へ同じ事を書こうとすると、自動更新で壊れやすいし、読み物としてもつらい。
二重管理を避ける
ただし、手書き設定へ昇格させた時に気をつけたい事がある。 setopt や customize-set-variable で手書きした変数が、同時に custom-file の custom-set-variables にも残っていると、同じ変数を二重管理する事になる。
この状態だと、どちらが最後に読み込まれるかで値が決まる。いったんは動いても、後から読むと分かりにくいし、将来の自分も混乱する。
なので、コメント付きで手書きに昇格させた変数は、 custom-file 側から消した方がよい。
運用としては、次のように分けるのが楽だ。
- 雑多で機械的な設定は
Customizeに任せる - 理由を残したい設定は手書きする
- 同じ変数を両方で持たない
さらに、手書き設定と custom-file に同じ変数名が出ていないかを、定期的に確認できるようにしておくと安心だ。
結局どう扱えばいいのか
今のところ、僕には次の方針がいちばんしっくりくる。
- 普通の変数は
setq - 現在のバッファだけに効かせたい時は
setq-local - バッファローカル変数のデフォルト値は
setq-default defcustomな変数を手で設定する時はsetoptを第一候補にする- 古い Emacs 互換や既存流儀を優先する時は
customize-set-variableを使う custom-set-variablesは自分で育てる設定ではなく、Customizeが保存する領域として分離する- コメントを書きたい設定は手書きに移し、
custom-file側には残さない
Customize を使うのをやめる必要はない。ただし、 custom-set-variables に人間向けの説明責任まで持たせようとすると窮屈になる。値の保存はEmacsに任せ、理由の記述は手書きの設定に寄せる、という分担の方がずっと扱いやすい。