Eval and replace anywhere

Being a living Elisp virtual machine, Emacs naturally provides the ability to evaluate any Elisp expression anywhere. Just put the cursor right after the expression to be evaluated and press C-xC-e: the result appears in the mini-buffer. I use this continuously, for instance while reading about a variable to know its value. For instance, imagine i see this line in one of my files:

(setq planner-project "planner")

and want to know if planner-project has been modified. I just need to put my cursor right after the variable name and get its value with C-xC-e.

As i said, C-xC-e works in any (well, almost) buffer. Imagine you’re writing a note and need to perform an arithmetic operation: you could write something like

   Yesterday I spent (+ 2234.34 3423.4 (* 12.0 12.2) 10) dollars at ...

put the cursor just before ‘dollars’, press the eval shortcut, and see the result of the arithmetic operation in the mini-buffer. You memorize it, delete the Elisp expression… hmm, wait, this should be easier, shouldn’t it? What we want is Emacs to eval and replace the expression for us.

I’m sure that you already know what comes next :-) As it happens, this time i didn’t need to write the Elisp snippet myself: i stole it instead from Jorgen “forcer” Schäfer’s configuration file:

(defun fc-eval-and-replace ()
  "Replace the preceding sexp with its value."
  (interactive)
  (backward-kill-sexp)
  (prin1 (eval (read (current-kill 0)))
         (current-buffer)))

Complemented with the mandatory shortcut:

(global-set-key (kbd "C-c e") 'fc-eval-and-replace)

this little gem has probably the highest usefulness to lines of code ratio in my Elisp toolbox. Give it a try!

Update: pdq notes in a comment that, if the expression to be evaluated is malformed (like, say, (+1 1)) it gets deleted, and it would be nice if it wouldn’t. A quick way to get this functionality is to catch errors in eval and undo the killing in a generic error handler. To wit:

(defun fc-eval-and-replace ()
  "Replace the preceding sexp with its value."
  (interactive)
  (backward-kill-sexp)
  (condition-case nil
      (prin1 (eval (read (current-kill 0)))
             (current-buffer))
    (error (message "Invalid expression")
           (insert (current-kill 0)))))
About these ads

5 Responses to “Eval and replace anywhere”

  1. pdq Says:

    Sweet. How hard would it be to change so that an invalid expression (like “(+1 1)”) wouldn’t actually delete the sexp. “Undo” works, but isn’t quite as cool.

  2. jao Says:

    pdq, not that hard: see the updated post for a quick way to accomplish it

  3. mrd Says:

    You could, alternatively, just use C-u C-x C-e to insert the result at the point, and then delete the original form. Or even rewrite your function to do that.

  4. pdq Says:

    I think this is what mrd is getting at. This code looks hackerish with the backward and forward sexp’s, but seems to work, even giving you an interactive backtrace on error:

    (global-set-key (kbd “C-c e”) ‘(lambda () (interactive) (eval-last-sexp ‘universal-argument) (backward-sexp) (backward-kill-sexp) (forward-sexp)))

  5. dig Says:

    I find this function useful for similar purposes.

    ,—-[ C-h f eval-print-last-sexp RET ]
    | eval-print-last-sexp is an interactive compiled Lisp function in `c:/program files/Emacs/emacs/lisp/emacs-lisp/lisp-mode.elc’.
    | (eval-print-last-sexp)
    |
    | Evaluate sexp before point; print value into current buffer.
    |
    | If `eval-expression-debug-on-error’ is non-nil, which is the default,
    | this command arranges for all errors to enter the debugger.
    |
    | Note that printing the result is controlled by the variables
    | `eval-expression-print-length’ and `eval-expression-print-level’,
    | which see.
    |
    | [back]
    `—-


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: