1

I wanted to jump to the end of the current top-level S-expression as described in this question: How to move up to the ending parenthesis of the enclosing list (if it exists)?. The goal is to position the point in a location such that the current top-level S-expression can be evaluated with C-x C-e (and then evaluate it). Based on the answers linked above, I have the following shortcut key sequence:

(defalias 'eval-next-sexp
  ;; (kbd "M-m C-f C-u C-M-n C-x C-e")
  (kbd "M-m C-f C-u C-M-n")
  )
(global-set-key (kbd "C-x C-y") 'eval-next-sexp)

As I am using paredit mode, M-m moves the cursor to the first non-whitespace position, C-f moves one letter into the S-expression, C-u C-M-n repeatedly moves up the hierarchy of parentheses (paredit-forward-up) until it cannot move any further.

So far this has been (mostly) successful in moving the point to where it needs to be, and a subsequent C-x C-e evaluates the expression.

The problem is that when I tried to combine the move and the evaluation, as shown in the commented line: (kbd "M-m C-f C-u C-M-n C-x C-e"), the evaluation doesn't happen because the movement command generates an error when stopping:

After 0 kbd macro iterations: Scan error: "Unbalanced parentheses", 1647, 12558

My guess is that C-u C-M-n reached top and generates an error.

The question is:

How can errors from previous commands as above be ignored and the subsequent key strokes be executed?

Additionally, is it possible to repeat a key combination/command (C-M-n here) until an error happens? (If so, the going up can be implemented more reliably)

5
  • Given your last question, you probably realize that this is not sufficient if you start deeper than a nesting level of 4. I don't know how to repeat until you get the error, but using a C-u C-u prefix will suffice for 16 levels which is probably more than enough in practice - but you can use three of them for 64 levels if necessary - that should be enough for all practical purposes.
    – NickD
    Sep 14, 2022 at 8:23
  • Why is the initial M-m C-f necessary?
    – NickD
    Sep 14, 2022 at 8:25
  • @NickD Thanks. The M-m C-f is just a heuristic to get into the top-level sexp. e.g. in |(add1 1). C-M-n will do nothing. After the initial move, (|add1 1), C-M-n works. Maybe there are better ways..
    – tinlyx
    Sep 14, 2022 at 15:26
  • Also, I misunderstood the C-u and thought it's the same as a while loop.
    – tinlyx
    Sep 14, 2022 at 15:27
  • It's a prefix argument.
    – NickD
    Sep 14, 2022 at 15:44

1 Answer 1

2

I don't know how to ignore errors in a keyboard macro, but you can certainly write a function that ignores the error:

(defun ndk/paredit-move-to-end-of-top-level ()
  (interactive)
  (condition-case nil
      (paredit-forward-up 1000)
    (error nil)))

paredit-forward-up is the command that is bound to C-M-n. This tries to go up 1000 levels, catches the error and returns nil. If 1000 levels are not enough, you can wrap the call in a while like this: (while t (paredit-forward-up 1000) - that will repeat infinitely, until it gets the error of course.

You can bind the function to a key (that's why the (interactive) form is there):

(global-set-key (kbd "C-c z") #'ndk/paredit-move-to-end-of-top-level)

and define a macro that does (kbd "C-c z C-x C-e").

N.B. I've left out the M-m C-f heuristic, but you can put it back if you want.

EDIT: to answer your comment, you can call eval-last-sexp in the function but it takes an argument - the error message is telling you that you called it with 0 arguments but it requires 1 argument - read the doc string with C-h f eval-last-sexp to see that, but it is surprisingly unenlightening and a bit stingy in the descripiton of what values the argument can take: making the argument nil is probably the best in order to avoid output in the buffer itself (nil makes the output appear in the echo area). In any case, here's how to do it:

(defun ndk/paredit-move-to-end-of-top-level-eval ()
  (interactive)
  (condition-case nil
      (paredit-forward-up 1000)
    (error nil))
  (eval-last-sexp nil))
1
  • I also tried to add (eval-last-sexp) in the end of the function, but it gives an error paredit-move-to-end-of-top-level: Wrong number of arguments: (1 . 1), 0
    – tinlyx
    Sep 14, 2022 at 16:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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