Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

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

三行で

  • 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を使ってな。

tadsan
僕に警備する自宅をください。Emacs初心者。Rubyist。 全ての投稿された記事は別段の表記がない限りはCC 3.0 BY-SA https://creativecommons.org/licenses/by-sa/3.0/deed.ja で二次利用できます。 記事中に含まれる全てのコードスニペットの著作権は抛棄するので、煮るなり焼くなりお好きにどうぞ。
https://tadsan.github.io/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした