DEV Community

USAMI Kenta
USAMI Kenta

Posted on

make-local-variable vs make-variable-buffer-local

These two function names are confusing, but they are different functions.

The content of this article is explained in Creating Buffer-Local in GNU Emacs Lisp Reference Manual.

TL;DR

  • Local” in Emacs means that variables can hold independent values for each buffer.
  • make-local-variable *temporarily* makes the variable local in the current buffer
  • make-variable-buffer-local ensures that the variable is *always* set as local.

Emacs Lispers targeting Emacs 24.3 and later should only use defvar-local andsetq-local. Looking at its implementation (subr.el added in Emacs 24.3), you will find a difference between the two functions.

(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))))

make-local-variable

In the past, I wrote as follows in my init.el:

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

setq-local makes that code simple.

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

If you are the author of Lisp packages, please carefully consider using this function. Do you want to set it temporarily locally?

make-variable-buffer-local

This is exclusively a function for Lisp package authors.

;;;###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))

This function reduces repetition of function names.

;;;###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))

It is not necessary to use setq-local for variables defined in this way. Please use setq with confidence.

Do not forget the definition of safe-local-variable when you provide variables that you can customize for per directory/project. Directory Variables a better idea than defcustom + setq-local.

...It is the worst that the notation safe-local-variable and defcustom :type is different.

Afterword

Replacing the strange twins with defvar-local andsetq-local means dropping support for versions below Emacs 24.2.

Emacs 24.3 was released on March 10, 2013, but I ceased to pay for the rotten milks.

Top comments (0)