16

When writing lisp codes, sometimes I'm deep in a nested expression and all I want is to insert all the missing closing parenthesis. Right now I'm just inserting them until I get a mismatched paren, but it's not very efficient.
Is there any command to insert all the missing parentheses?

FYI, I'm using smartparens to automatically insert matching parens. Still, sometimes I just need to do this.

Malabarba
  • 22,878
  • 6
  • 78
  • 163
rlazo
  • 983
  • 8
  • 13
  • 3
    FWIW, Franz Lisp (before CL) had a feature where a `]` acted as a super right paren, closing all open parens, as you request. – Drew Oct 07 '14 at 19:37
  • 2
    I've used the same methodology in the past. Since then I've started using [paredit](http://www.emacswiki.org/emacs/ParEdit), which stops the problem before it starts. The only caveat is that pasting in code doesn't get the same balancing treatment. – elarson Oct 07 '14 at 22:46

3 Answers3

9

Here's a function that closes all unclosed parentheses and other matched pairs, such as square brackets. It relies on Emacs's sexp parsing. It only supports single-character matched pairs, so something like {- will be closed with }, not -}. For Lisp, that doesn't matter.

(defun close-all-parentheses ()
  (interactive "*")
  (let ((closing nil))
    (save-excursion
      (while (condition-case nil
         (progn
           (backward-up-list)
           (let ((syntax (syntax-after (point))))
             (cl-case (car syntax)
               ((4) (setq closing (cons (cdr syntax) closing)))
               ((7 8) (setq closing (cons (char-after (point)) closing)))))
           t)
           ((scan-error) nil))))
    (apply #'insert (nreverse closing))))
  • IIUC, This requires that point not be inside any set of matched parenthesis. I was under the impression the OQ needed to work from some inside a lisp expression, where done parentheses will be mismatched but others won't. – Malabarba Oct 09 '14 at 07:34
  • @Malabarba This closes all previously-opened parentheses, whether they already had matching closing parentheses after point or not. That's how I understand the question, but it is admittedly not clear on this point. Under your interpretation, where would the closing delimiters be inserted? E.g. with `([-!-foo]`, do you insert `])` at point, or `)` after `foo]`? – Gilles 'SO- stop being evil' Oct 09 '14 at 07:59
  • from my understanding, if you have `([-!-foo]`, I would insert `)` after `foo]`. But I could be wrong of course. Maybe @rlazo can elaborate. – Malabarba Oct 09 '14 at 08:22
  • for my use case, @Gilles is right, I don't care if the delimiters are closed after the point, I want to close everything before point. – rlazo Oct 09 '14 at 17:22
  • I had to replace `case` with `cl-case` to get this function to work. It works great! – Heitor Chang Aug 29 '21 at 18:46
3

I've found out that if you have slime installed, there is a command to do this, called slime-close-all-parens-in-sexp

rlazo
  • 983
  • 8
  • 13
  • Hmm... so this appears to close on the current line. It might be nice if there was an approach that closed "the current block". This could be achieved by going to the end of the file and then moving backward sexp until something no closed in found. – Att Righ Dec 30 '17 at 22:24
1

A very primitive (and almost certainly wrong) way to do that would be

(defun buffer-needs-parens-fixing ()
  (save-excursion
    (condition-case nil
        (check-parens)
      (error (point)))))

(defun buffer-fix-parens ()
  (interactive)
  (while (buffer-needs-parens-fixing)
    (insert ")")))

Amongst other limitations, it assumes that all the parentheses that need inserting are:

  • closing ones
  • needed at the current location

I guess it might be just enough to be useful for your specific use-case

Sigma
  • 4,510
  • 21
  • 27