Emacs: how to evaluate the smallest s-expression the cursor is in, or the following s-expression

StackOverflow https://stackoverflow.com/questions/2171890

  •  24-09-2019
  •  | 
  •  

Question

What is a good way to evaluate the (+ 100 (+ 100 100)) part in

(+ (+ 1 2) (+ 100 (+ 100 100)))

?

For now, I do it by C-x C-e, which means I need to locate the ending parenthesis, which is difficult in most cases. Options > Paren Matching Highlighting helps, but still I need to move the cursor toward the ending parenthesis until the highlighted match is the starting parenthesis.

One way would be to have the reverse version of C-x C-e, so that I can place the cursor at the starting parenthesis like this:

(+ (+ 1 2) |(+ 100 (+ 100 100)))

and then press the appropriate keybinding.

Or I could place the cursor inside the expression, but not inside smaller expressions,:

(+ (+ 1 2) (+ | 100 (+ 100 100)))

and press a keybinding. Because aiming at a target is easier if the target is big.

How can I make such a command? Or is there one already provided?

Sidenote: bar cursor and box cursor

Emacsers who use box cursor (default) might wonder where I'm putting the cursor with the bar notation above. In emacs, you can choose box cursor or bar cursor, (bar-cursor-mode t). When the bar cursor is between the letters A and B, the box cursor is on B. So the bar is the left wall of the box.

BTW, the concept of bar cursor is useful in some unusual way: The practice of iterating from index1 to index2-1 in programming surprises beginners. It helps to imagine index1 and index2 as indicating bars (left walls) rather than boxes.

Was it helpful?

Solution

Bind a key to one or both of these:

(defun eval-next-sexp ()
  (interactive)
  (save-excursion
    (forward-sexp)
    (eval-last-sexp nil)))

(defun eval-surrounding-sexp (levels)
  (interactive "p")
  (save-excursion
    (up-list (abs levels))
    (eval-last-sexp nil)))

Tangentially related, I highly recommend paredit for working with s-expressions. The structure editing commands and bindings make editing s-expressions a breeze. It binds C-down to up-list, so that eval-surrounding-sexp above is almost exactly the same as C-down C-x C-e (the only difference is that the function uses save-excursion to prevent movement).

OTHER TIPS

You could write such a command like so:

(require 'thingatpt)
(defun eval-sexp-at-or-surrounding-pt ()
  "evaluate the sexp following the point, or surrounding the point"
  (interactive)
  (save-excursion
    (forward-char 1)
    (if (search-backward "(" nil t)
        (message "%s" (eval (read-from-whole-string (thing-at-point 'sexp)))))))

In Icicles there is a general way to do what you want.

By default, in the minibuffer M-. is bound to a command that inserts text at (or near) point into the minibuffer (doesn't enter it or doing else with it; just inserts it).

You can, for example, use M-: to evaluate a Lisp sexp, and then you use M-. to grab a sexp at/near point.

If you repeat M-. then it drops what it just grabbed and grabs some other kind of THING (text) at/near point and inserts that. By default, it runs through these kinds of THING, in order:

a. A Lisp symbol or file name.

b. The active region (selected text) or a word.

c. The most immediate list.

d. The next largest list.

e. The next largest list.

f. Whichever file or URL the function ffap-guesser guesses.

g. Whatever URL the function thing-at-point-url-at-point guesses.

What does this mean for your example of (+ (+ 1 2) (+ 100 (+ 100 100)))?

If point is before the 1 of the second-to-last 100, for example, these are the sexps that are consecutively inserted in the minibuffer when you hit M-. repeatedly, in order:

a. +

b. 100

c. (+ 100 100)

d. (+ 100 (+ 100 100))

e. (+ (+ 1 2) (+ 100 (+ 100 100)))?

So to insert the largest of the enclosing lists you would do M-: M-. M-. M-. M-. M-., that is, hit M-. five times.

For this behavior, in particular for the accurate grabbing of lists, you also need library Thing At Point+.

There is an inbuilt eval-defun. It's bound by default to C-M-x. It's similar to what you want but evals the top-level defun. Perhaps you can adapt that.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top