I have a package with the variable my-package--my-var that needs to be buffer-local when my-package-buffer-only is t but global if my-package-buffer-only is nil. This is what I have so far:

(defcustom my-package-buffer-only nil
  :type 'boolean
  :set (lambda (sym val)
         (when val
           (make-variable-buffer-local 'my-package--my-var))
         (set-default sym val)))

(defvar my-package--my-var nil)

The problem is that I can't find a way to make the variable global again if my-package-buffer-only is set to nil.


Note, I have a strange feeling about your wish making a local variable var global:

  1. You can always access the global value of var with (default-value 'var).
  2. You can set the global value of a local variable with (setq-default var val).
  3. Maybe, you should have a global version var and a local version var-local of your variable. AFAIK that is how most modes deal with variables that can have a global (default) value and a local value.

Nevertheless, the following lisp snippet defines two functions related to your wish. The predicate lispTZA-variable-made-buffer-local-p tells you whether make-variable-buffer-local was applied to a variable and lispTZA-make-variable-global gives a symbol a fresh start as global variable.

(defun lispTZA-variable-made-buffer-local-p (var)
  "Return t if VAR is local_if_set.
This is the case if `make-variable-buffer-local' was applied to VAR."
        (local-variable-if-set-p var)))

(defun lispTZA-make-variable-global (var)
  "Give VAR a fresh start forgetting about localness."
  (let ((name (symbol-name var))
    (val (default-value var))
    (plist (symbol-plist var))
    (fun (symbol-function var))
    (special (special-variable-p var))) ;; Hm, should always be t. Maybe worth an assert?
    (unintern name obarray)
    (setq var (intern name)) ;; Here we have a fresh start.
    (when special
      (eval `(defvar ,var)))
    (set var val)
    (setf (symbol-plist var) plist)
    (fset var fun)))

Thanks to the answer from Tobias I realized that I can do what I want by using a function instead of a variable. my-package--my-var returns/sets the local value if my-package-buffer-only is set and the global value otherwise.

(defcustom my-package-buffer-only nil
  :type 'boolean)

(defvar-local my-package--my-var-v nil)

(defun my-package--my-var (&optional val)
  "Return `my-package--my-var-v' or set it if VAL is non-nil.
Use the local value if `my-package-buffer-only' is set and the global value
  (if val
      (if my-package-buffer-only
          (setq my-package--my-var-v val)
        (setq-default my-package--my-var-v val))
    (if my-package-buffer-only
      (default-value 'my-package--my-var-v))))
  • Please, do not forget to mark your answer as solution by clicking the check mark left-hand of your answer if it solves your question. That removes this question from the list of open questions. – Tobias Mar 8 '20 at 23:34
  • @Tobias I have to wait 48 hours to mark my own answer as solution. – tastytea Mar 9 '20 at 5:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.