* lisp/custom.el: Avoid adding vars to load-history multiple times

Avoid the abuse of (eval `(defvar ...)) which tends to end up
adding redundant entries in `load-history`, as discussed in
https://lists.gnu.org/r/help-gnu-emacs/2019-03/msg00237.html

(custom-initialize-default): Don't add to load-history.
(custom-declare-variable): Use internal--define-uninitialized-variable
and only add the var to load-history once.  Do it before calling
`initialize` so the special-variable-p flag is set.

* src/eval.c (Finternal__define_uninitialized_variable): New function.
(Fdefvar, Fdefconst): Use it.
(syms_of_eval): Defsubr' it.
This commit is contained in:
Stefan Monnier 2019-05-06 12:37:00 -04:00
parent f0c0b2cea3
commit 6309131349
2 changed files with 39 additions and 32 deletions

View file

@ -56,8 +56,14 @@ Otherwise, if symbol has a `saved-value' property, it will evaluate
the car of that and use it as the default binding for symbol.
Otherwise, EXP will be evaluated and used as the default binding for
symbol."
(eval `(defvar ,symbol ,(let ((sv (get symbol 'saved-value)))
(if sv (car sv) exp)))))
(condition-case nil
(default-toplevel-value symbol) ;Test presence of default value.
(void-variable
;; The var is not initialized yet.
(set-default-toplevel-value
symbol (eval (let ((sv (get symbol 'saved-value)))
(if sv (car sv) exp))
t)))))
(defun custom-initialize-set (symbol exp)
"Initialize SYMBOL based on EXP.
@ -188,18 +194,13 @@ set to nil, as the value is no longer rogue."
(t
(custom-handle-keyword symbol keyword value
'custom-variable))))))
;; Set the docstring, record the var on load-history, as well
;; as set the special-variable-p flag.
(internal--define-uninitialized-variable symbol doc)
(put symbol 'custom-requests requests)
;; Do the actual initialization.
(unless custom-dont-initialize
(funcall initialize symbol default)))
;; Use defvar to set the docstring as well as the special-variable-p flag.
;; FIXME: We should reproduce more of `defvar's behavior, such as the warning
;; when the var is currently let-bound.
(if (not (default-boundp symbol))
;; Don't use defvar to avoid setting a default-value when undesired.
(when doc (put symbol 'variable-documentation doc))
(eval `(defvar ,symbol nil ,@(when doc (list doc)))))
(push symbol current-load-list)
(run-hooks 'custom-define-hook)
symbol)

View file

@ -715,6 +715,25 @@ DEFUN ("set-default-toplevel-value", Fset_default_toplevel_value,
return Qnil;
}
DEFUN ("internal--define-uninitialized-variable",
Finternal__define_uninitialized_variable,
Sinternal__define_uninitialized_variable, 1, 2, 0,
doc: /* Define SYMBOL as a variable, with DOC as its docstring.
This is like `defvar' and `defconst' but without affecting the variable's
value. */)
(Lisp_Object symbol, Lisp_Object doc)
{
XSYMBOL (symbol)->u.s.declared_special = true;
if (!NILP (doc))
{
if (!NILP (Vpurify_flag))
doc = Fpurecopy (doc);
Fput (symbol, Qvariable_documentation, doc);
}
LOADHIST_ATTACH (symbol);
return Qnil;
}
DEFUN ("defvar", Fdefvar, Sdefvar, 1, UNEVALLED, 0,
doc: /* Define SYMBOL as a variable, and return SYMBOL.
You are not required to define a variable in order to use it, but
@ -754,32 +773,25 @@ usage: (defvar SYMBOL &optional INITVALUE DOCSTRING) */)
{
if (!NILP (XCDR (tail)) && !NILP (XCDR (XCDR (tail))))
error ("Too many arguments");
Lisp_Object exp = XCAR (tail);
tem = Fdefault_boundp (sym);
tail = XCDR (tail);
/* Do it before evaluating the initial value, for self-references. */
XSYMBOL (sym)->u.s.declared_special = true;
Finternal__define_uninitialized_variable (sym, CAR (tail));
if (NILP (tem))
Fset_default (sym, eval_sub (XCAR (tail)));
Fset_default (sym, eval_sub (exp));
else
{ /* Check if there is really a global binding rather than just a let
binding that shadows the global unboundness of the var. */
union specbinding *binding = default_toplevel_binding (sym);
if (binding && EQ (specpdl_old_value (binding), Qunbound))
{
set_specpdl_old_value (binding, eval_sub (XCAR (tail)));
set_specpdl_old_value (binding, eval_sub (exp));
}
}
tail = XCDR (tail);
tem = Fcar (tail);
if (!NILP (tem))
{
if (!NILP (Vpurify_flag))
tem = Fpurecopy (tem);
Fput (sym, Qvariable_documentation, tem);
}
LOADHIST_ATTACH (sym);
}
else if (!NILP (Vinternal_interpreter_environment)
&& (SYMBOLP (sym) && !XSYMBOL (sym)->u.s.declared_special))
@ -827,19 +839,12 @@ usage: (defconst SYMBOL INITVALUE [DOCSTRING]) */)
docstring = XCAR (XCDR (XCDR (args)));
}
Finternal__define_uninitialized_variable (sym, docstring);
tem = eval_sub (XCAR (XCDR (args)));
if (!NILP (Vpurify_flag))
tem = Fpurecopy (tem);
Fset_default (sym, tem);
XSYMBOL (sym)->u.s.declared_special = true;
if (!NILP (docstring))
{
if (!NILP (Vpurify_flag))
docstring = Fpurecopy (docstring);
Fput (sym, Qvariable_documentation, docstring);
}
Fput (sym, Qrisky_local_variable, Qt);
LOADHIST_ATTACH (sym);
Fset_default (sym, tem); /* FIXME: set-default-toplevel-value? */
Fput (sym, Qrisky_local_variable, Qt); /* FIXME: Why? */
return sym;
}
@ -4198,6 +4203,7 @@ alist of active lexical bindings. */);
defsubr (&Sdefvaralias);
DEFSYM (Qdefvaralias, "defvaralias");
defsubr (&Sdefconst);
defsubr (&Sinternal__define_uninitialized_variable);
defsubr (&Smake_var_non_special);
defsubr (&Slet);
defsubr (&SletX);