emacs-lisp

make-variable-buffer-localとmake-local-variableの関係

なんか同じような名前の機能だけど別の機能。

三行で

  • Emacsでの「ローカル」とは、変数にバッファごとに独立した値を保持できることです。
  • make-local-variableは、本来ローカルでない変数を現在のバッファで一時的にローカルにします。
  • make-variable-buffer-local は、その変数が常にローカルとしてセットされるようにします。

この両者の関係は、Emacs 24.3で追加された subr.el を見ればわかりやすいです。
emacs-26.0.91より抜萃。

subr.el
(defmacro setq-local (var val)
  "Set variable VAR to value VAL in current buffer."
  ;; Can't use backquote here, it's too early in the bootstrap.
  (declare (debug (symbolp form)))
  (list 'set (list 'make-local-variable (list 'quote var)) val))

(defmacro defvar-local (var val &optional docstring)
  "Define VAR as a buffer-local variable with default value VAL.
Like `defvar' but additionally marks the variable as being automatically
buffer-local wherever it is set."
  (declare (debug defvar) (doc-string 3))
  ;; Can't use backquote here, it's too early in the bootstrap.
  (list 'progn (list 'defvar var val docstring)
        (list 'make-variable-buffer-local (list 'quote var))))

Emacs Lispでのニュアンスとしては、defvar-は変数定義、setq-は変数のセット(代入・束縛)ですね。

そもそもEmacs 24.3以降では覚えやすいdefvar-localsetq-localを使っておけばいいので、わざわざ紛しい方を覚えておく必要は特にありません。

つまり、以下の記述はEmacs 24.3未満との互換性を考慮する必要がある場合の専用だと思ってください。

解説

make-local-variable

本来は全体で共有される変数だが、特別にバッファローカルにしたい場合に利用します。

init.el
(defun my/enh-ruby-mode-hook ()
  "Enhanced Ruby Modeでだけ ac-ignore-case を t にしたいお"
  (set (make-local-variable 'ac-ignore-case) t))

ac-ignore-caseは本来カスタマイズ変数なので、その影響は全体に影響します。そのため、特定のモードでは特別にローカルとして設定する必要があるのです。

しかし、 (set (make-local-variable 'ac-ignore-case) t) ってビジュアルがもう野暮ったい。

init.el
(defun my/enh-ruby-mode-hook ()
  "Enhanced Ruby Modeでだけ ac-ignore-case を t にしたいお"
  (setq-local ac-ignore-case t))

setq-localで超すっきりしましたね。

make-variable-buffer-local

最初からバッファローカルとして設定させたい変数に利用します。

copy-file-on-save-dest-dir.dir-locals.el で設定させたいのです。

copy-file-on-save.el
;;;###autoload
(progn
  (defvar copy-file-on-save-dest-dir nil
    "Path to deployment directory or convert (mapping) function.")
  (make-variable-buffer-local 'copy-file-on-save-dest-dir)
  (put 'copy-file-on-save-dest-dir 'safe-local-variable #'stringp))

copy-file-on-save-dest-dirって何回も書かなきゃいけないのはだるいですね。

copy-file-on-save.el
;;;###autoload
(progn
  (defvar-local copy-file-on-save-dest-dir nil
    "Path to deployment directory or convert (mapping) function.")
  (put 'copy-file-on-save-dest-dir 'safe-local-variable #'stringp))

一行減りました! うれしい! そんなことより、僕みたいな記憶力の悪い人間ミスをしにくいコードになったのは嬉しいことです。

まとめ

みんなは早うEmacs 24.3以下を切ってdefvar-localsetq-localを使ってな。