27

Suppose I have an Emacs lisp buffer that contains:

(defvar foo 1)

If I call eval-last-sexp or eval-buffer, foo is bound to 1. If I then edit this buffer to:

(defvar foo 2)

eval-last-sexp and eval-buffer do not re-execute this line, so foo is still 1.

This is particularly challenging when there are multiple such statements and I have to track down which lines are not being re-evaluated.

I looked at just restarting Emacs and then (require 'foo), but then I have to be careful to avoid loading any older .elc files.

How can I be absolutely, positively sure that the variables and functions defined in the current file are in the same state as loading code afresh in a new Emacs instance?

Wilfred Hughes
  • 6,890
  • 2
  • 29
  • 59
  • You cannot be "*absolutely, positively sure that Emacs is in a state that is the same as loading the code afresh in a new Emacs instance*" without doing just that. If you want to be sure *only* wrt this and other *global variables*, then you can remove their values using `makunbound` and then re-evaluate the code in the buffer. – Drew Oct 17 '14 at 21:30
  • Sure, side effects like (silly code) `(incf emacs-major-version)` I can live with happening repeatedly. I'm interested in hacking on code with lots of `defvar` forms. – Wilfred Hughes Oct 17 '14 at 21:36

5 Answers5

32

As explained in other answers, evaluating a defvar form using eval-last-sexp does not reset the default value.

Instead, you can use eval-defun (bound to C-M-x in emacs-lisp-mode by default), which implements the behaviour you want as a special exception:

If the current defun is actually a call to defvar or defcustom, evaluating it this way resets the variable using its initial value expression even if the variable already has some other value. (Normally defvar and defcustom do not alter the value if there already is one.)


If you need to evaluate the full contents of a buffer, you can write a function that walks the top-level forms in turn and calls eval-defun on each. Something like this should work:

(defun my/eval-buffer ()
  "Execute the current buffer as Lisp code.
Top-level forms are evaluated with `eval-defun' so that `defvar'
and `defcustom' forms reset their default values."
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (while (not (eobp))
      (forward-sexp)
      (eval-defun nil))))
François Févotte
  • 5,917
  • 1
  • 24
  • 37
  • 6
    _This_ is the answer. No need for fake defvars or extra setqs. Just use `eval-defun` instead of `eval-last-sexp `. You can even write a function that calls `eval-defun` on every form in the buffer, and use it instead of `eval-buffer`. – Malabarba Oct 17 '14 at 19:06
  • 1
    @Malabarba This post falls far short of answering the question. Use `eval-defun` instead of `eval-last-sexp`, sure, but the difficulty is for `eval-buffer`. – Gilles 'SO- stop being evil' Oct 17 '14 at 21:17
  • @Gilles yes, you're right. I added a tentative implementation of @Malabara's idea of calling `eval-defun` on every top-level form in the buffer. – François Févotte Oct 17 '14 at 21:41
  • 1
    This approach does not seem to work if the `defvar` is not inside a `defun`. Example: `(progn (defvar foo "bar"))`. – Kaushal Modi Mar 13 '15 at 15:01
  • @kaushalmodi indeed, it only works when the `defvar` is a top-level form. Do you have actual code in which a `defvar` is nested inside a `progn` form? I sometimes do things like this, but only for throw-away code, in which I can easily replace `defvar` by `setq` if needed... – François Févotte Mar 13 '15 at 20:36
  • I like `defvar` because I can see the documentation on doing `C-h v` but I also like how `setq` can be re-eval'd. So I ended up with a solution that has both, using a [modified version](https://github.com/kaushalmodi/.emacs.d/blob/340b7ce03dc8be2b9b1183cb72eca68d4d030bcb/elisp/defuns.el#L73-109) of [Jordon's solution](http://emacs.stackexchange.com/a/2301/115) (as the latter breaks loading of `projectile`). I use `defvar-re` for [vars that I'd need to tweak a lot](https://github.com/kaushalmodi/.emacs.d/blob/340b7ce03dc8be2b9b1183cb72eca68d4d030bcb/setup-files/setup-verilog.el#L33-92). – Kaushal Modi Mar 13 '15 at 20:51
  • 2
    @kaushalmodi The latter examples that you cite (variables storing regular expressions) look a lot like candidates for `defconst` (which is always re-evaluated). There's recently been a very enlightening post on this topic in [endless parentheses](http://endlessparentheses.com/what-s-a-defconst-and-why-you-should-use-it.html) – François Févotte Mar 13 '15 at 21:07
  • @Francesco That's a great point! I will do that and use `defconst` instead :) – Kaushal Modi Mar 13 '15 at 21:24
6

Like the other answers say, this is just the way defvar works, but you can get around it, this is elisp after all.

You can temporarily redefine how defvar works if you'd like and during that time, reload the packages you'd like to reset.

I wrote a macro where during the evaluation of the body, defvars values will always be reevaluated.

(defmacro my-fake-defvar (name value &rest _)
  "defvar impersonator that forces reeval."
  `(progn (setq ,name ,value)
          ',name))

(defmacro with-forced-defvar-eval (&rest body)
  "While evaluating, any defvars encountered are reevaluated"
  (declare (indent defun))
  (let ((dv-sym (make-symbol "old-defvar")))
    `(let ((,dv-sym (symbol-function 'defvar)))
       (unwind-protect
           (progn
             (fset 'defvar (symbol-function 'my-fake-defvar))
             ,@body)
         (fset 'defvar ,dv-sym)))))

Example Usage:

file_a.el

(defvar my-var 10)

file_b.el

(with-forced-defvar-eval
  (load-file "file_a.el")
  (assert (= my-var 10))
  (setq my-var 11)
  (assert (= my-var 11)
  (load-file "file_a.el")
  (assert (= my-var 10))

Note: This this should only be used for the purpose of reevaluating defvars, as it just ignores docstrings when reevaluating. You can modify the macro to support re-evaluating that applies docstrings as well, but I will leave that up to you.

In your case you could do

(with-forced-defvar-eval (require 'some-package))

But know what those who write elisp do so expecting defvar to work as specified, it could be they use defvar to define and setq in some init function to specify the value, so you may end up nil'ing variables you don't intend but this is probably rare.

Alternative Implementation

Using this you can just redefine defvar globally and control whether or not it will set the symbol's value to the INIT-VALUE arg even if the symbol is defined by changing the value of the new defvar-always-reeval-values symbol.

;; save the original defvar definition
(fset 'original-defvar (symbol-function 'defvar))

(defvar defvar-always-reeval-values nil
  "When non-nil, defvar will reevaluate the init-val arg even if the symbol is defined.")

(defmacro my-new-defvar (name &optional init-value docstring)
  "Like defvar, but when `defvar-always-reeval-values' is non-nil, it will set the symbol's value to INIT-VALUE even if the symbol is defined."
  `(progn
     (when defvar-always-reeval-values (condition-case nil
         (makunbound ',name)
       (error nil)))
     (original-defvar ,name ,init-value ,docstring)))

;; globally redefine defvar to the new form
(fset 'defvar (symbol-function 'my-new-defvar))
David
  • 127
  • 7
Jordon Biondo
  • 12,332
  • 2
  • 41
  • 62
  • 1
    I'm not sure redefining the behaviour of `defvar` is a good idea: there are several different possible uses of `defvar`, with slightly different semantics. For example, one use your macro doesn't account for is the `(defvar SYMBOL)` form, which is used to tell the byte-compiler about the existence of a variable without setting a value. – François Févotte Oct 17 '14 at 19:07
  • If you absolutely need to redefine `defvar` with a macro, you would probably be better off prefixing the original `defvar` form with a `makunbound`, rather than replacing it by `setq`. – François Févotte Oct 17 '14 at 19:11
  • Yes, it is a terrible idea, and should only be used for things like reevaluting the defvars of a loaded package in your scratch buffer, you should never ship something like this. – Jordon Biondo Oct 17 '14 at 19:12
  • @Francesco also you are right about the makunbound version, I had implemented that but strayed away from the idea, I have put that code on my answer as an alternative. – Jordon Biondo Oct 17 '14 at 19:15
  • In case anyone runs across this answer and wonders _why_ `my-new-defvar` might crash your Emacs, it's because if the symbol being passed is already void (like from a fresh Emacs restart) and `makunbound` gets involved, it throws an error (and coincidentally crashed Emacs for me). Wrap the `makunbound` call in a `condition-case` to catch that error. – David Nov 29 '21 at 01:29
3

The defvar is being evaluated and doing exactly what you've specified. However, defvar only sets an initial value:

The optional argument INITVALUE is evaluated, and used to set SYMBOL, only if SYMBOL's value is void.

So to achieve what you want you would either need to unbind the variable before re-evaluating, e.g.

(makunbound 'foo)

or use setq to set the value, e.g.

(defvar foo nil "My foo variable.")
(setq foo 1)

If you don't need to specify a docstring here you can skip the defvar altogether.

If you really want to use defvar and automatically unbind this, you will need to write a function to find defvar calls in the current buffer (or region, or last sexp, etc); call makunbound for each one; and then do the actual eval.

glucas
  • 20,175
  • 1
  • 51
  • 83
  • I was off playing with an `eval-buffer` wrapper that would unbind everything first, but @Francesco's answer about `eval-defun` is really what you want. – glucas Oct 17 '14 at 19:22
1

The following macro was created by tracing eval-defun to its supporting functions and modifying it so that it is no longer necessary to evaluate a region of a particular buffer. I needed help in the related thread Converting an expression to a string, and @Tobias came to the rescue -- teaching me how to convert the imperfect function into a macro. I don't think we need eval-sexp-add-defvars to precede elisp--eval-defun-1, but if someone thinks that is important, please let me know.

;;; EXAMPLE:
;;;   (defvar-reevaluate
;;;     (defvar undo-auto--this-command-amalgamating "hello-world"
;;;     "My new doc-string."))

(defmacro defvar-reevaluate (input)
"Force reevaluation of defvar."
  (let* ((string (prin1-to-string input))
        (form (read string))
        (form (elisp--eval-defun-1 (macroexpand form))))
    form))
lawlist
  • 18,826
  • 5
  • 37
  • 118
0

The problem is not that the line is not getting re-evaluated. The problem is that defvar defines a variable and its default value. If a variable already exists, then changing its default value does not modify the current value. Unfortunately, I think you will need to run a setq for every variable whose value you wish to update.

This may be overkill, but you could update your file like this if you want to be able to easily update foo to its new default value.

(defvar foo 2)
(setq foo 2)

but that requires that you maintain the default value in two places in your code. You could also do this:

(makunbound 'foo)
(defvar foo 2)

but if there is a chance that foo is declared elsewhere you may have some side effects to deal with.

nispio
  • 8,175
  • 2
  • 35
  • 73
  • That's difficult when trying to test changes to a complex mode. I'd rather not change the code. `eval-defun` treats `defvar` specially, so surely there's something similar for whole buffers? – Wilfred Hughes Oct 17 '14 at 18:49
  • @WilfredHughes I'm not sure what you mean by "something for whole buffers." You want a single function that will `makunbound` any variables declared in the current buffer, and then re-evaluate it? You could write your own, but I don't think that there is an out-of-the-box function for this. **EDIT:** Never mind, I get what you are saying. An `eval-defun` that works on the whole buffer. It looks like @JordonBiondo has your solution for that. – nispio Oct 17 '14 at 18:53
  • No. The problem *is* lack of re-evaluation: `defvar` does nothing if the variable already has a value (as its doc says: `The optional argument INITVALUE is evaluated, and used to set SYMBOL, only if SYMBOL's value is void.`). The problem is **not** that `defvar` changes the default value and not the current value. `(defvar a 4) (default-value 'a) (setq a 2) (default-value 'a)`; then `C-x C-e` after the `defvar` sexp; then `(default-value 'a)`. `C-x C-e`, `eval-region`, and the like on a `defvar` sexp do **not** change the default value. – Drew Oct 17 '14 at 20:40