8

Is there a way to find out the type of the surrounding parenthesis (i.e. '(', '[' or '{') around point? For example (using | to represent point)

{ abc, | df }

should return '{', and

{ abc[ | ], 123 }

should return '['. Ideally I would like it to handle quotation marks as well.


In case anyone is curious or needs more details: my aim is to set up smart automatic spacing around : in python using electric-spacing (also known as smart-operator). The problem is that normally (in python) : is either the slice operator or the start of a for/if/... statement, which shouldn't be surrounded by spaces. However, within a dictionary it is something like an assignment operator, and so it should be surrounded by spaces. So I a way need to check if point is inside a dict (i.e. inside {}), but not inside a slice operation or string within that dict (i.e. not inside [] or "").


Edit:

Here's the helper function I wrote, based on abo-abo's answer:

(defun enclosing-paren ()
  "Return the closing parenthesis of the enclosing parens, or nil if not inside any parens."
  (ignore-errors
    (save-excursion
      (up-list)
      (char-before))))

Then then the final predicate is:

(and (not (in-string-p))
     (eq (enclosing-paren) ?\}))

Edit 2:

The above function turned out to be too slow (it often caused a noticeable lag when a : was typed). I'm using Stefan's answer instead now, which seems to be much faster.

dshepherd
  • 1,281
  • 6
  • 18

4 Answers4

11

Rather than up-list I'd recommend you use (syntax-ppss) which will return to you some parsing state. This will include info about whether you're inside a string or a comment, the position of the last open "paren" etc...

E.g. you can find the kind of paren with

(let ((ppss (syntax-ppss)))
  (when (nth 1 ppss) (char-after (nth 1 ppss))))

and it should hopefully let you handle quotation marks as well (by checking (nth 3 ppss) and (char-after (nth 8 ppss))).

Stefan
  • 26,154
  • 3
  • 46
  • 84
5

Try this:

(save-excursion
  (up-list)
  (char-before))

Note that up-list can throw, so you need to handle errors as well.

abo-abo
  • 13,943
  • 1
  • 29
  • 43
  • 1
    I like the simplicity of this answer but it turned out to be too slow in python when point is not inside any parens (very common with python's whitespace syntax). Presumably it's because it ends up parsing the entire buffer in this case. – dshepherd Jun 01 '15 at 09:42
1

While the preferable answer IMO was given by Stefan, here an example which includes a solution not relying on syntax-table WRT delimiters: It uses something like

 (skip-chars-backward "^{\(\[\]\)}")

and a stack. See source here

https://github.com/emacs-berlin/general-close

Andreas Röhler
  • 1,894
  • 10
  • 10
0

Here is a function which returns surrounding parenthesis, including the case when the point is directly on the first or last parenthesis (which was important in my case).

This works with the language, so ({[]}) all work in C for example.

(defun find-surrounding-brackets (pos)
  "Return a pair of buffer positions for the opening & closing bracket positions.

Or nil when nothing is found."
  (save-excursion
    (goto-char pos)
    (when
      (or
        ;; Check if we're on the opening brace.
        (when
          ;; Note that the following check for opening brace
          ;; can be skipped, however it can cause the entire buffer
          ;; to be scanned for an opening brace causing noticeable lag.
          (and
            ;; Opening brace.
            (eq (syntax-class (syntax-after pos)) 4)
            ;; Not escaped.
            (= (logand (skip-syntax-backward "/\\") 1) 0))
          (forward-char 1)
          (if (and (ignore-errors (backward-up-list arg) t) (eq (point) pos))
            t
            ;; Restore location and fall through to the next check.
            (goto-char pos)
            nil))
        ;; Check if we're on the closing or final brace.
        (ignore-errors (backward-up-list arg) t))
      ;; Upon success, return the pair as a list.
      (list
        (point)
        (progn
          (forward-list)
          (1- (point)))))))
ideasman42
  • 8,375
  • 1
  • 28
  • 105