2

Here is the function spec: (defun jump-to-matched (open-char closed-char) )

say the content of current buffer is "a([b]c()d)".

If I move the cursor over the first "(" and run (jump-to-matched "(" ")"), I expect the function return 9 which is the position of ")" at the end of the buffer.

If I move the cursor over the "[" at the position 2 and run (jump-to-matched "[" "]"), I expect the function return 4 which is the position of "]".

My question is how to implement such function?

1
  • I wrote a plugin evil-matchit (github.com/redguardtoo/evil-matchit). It's a emulation of matchit in vim. I'm investigating some tech solution to replace the evil API evil-jump-item I'm using. So my "spec" exactly specify what I need, nothing more.
    – chen bin
    Commented Jun 19, 2015 at 2:33

4 Answers 4

4

If you want to write your own use the contents of (syntax-ppss (point)) which you give you the positions of matching pairs in the current context amongst other data. Read the docs for syntax-ppss for more info.

But you don't need to implement the function, you can use forward-sexp and backward-sexp to jump between matching pairs. bound by default to C-M-f and C-M-b. These commands are also extremely useful for intelligently moving about code.

5
  • 1
    thanks, I will try it asap. It's great if I can avoid writing the code.
    – chen bin
    Commented Jun 17, 2015 at 3:31
  • I'll have to edit the question, syntax-ppss will help you go backward to the left matching pair but on won't give you the position of the right matching pair. Commented Jun 17, 2015 at 3:34
  • 1
    You do not need to jump around just to get the position of the matching delimiter. You can also use scan-sexps.
    – Tobias
    Commented Jun 17, 2015 at 9:58
  • @Tobias never knew about that function, you should write up an answer for it. Commented Jun 17, 2015 at 11:51
  • thanks @Tobias, I think both of you answered my questions correctly because forward-sexp actually calls the scan-sexps.
    – chen bin
    Commented Jun 19, 2015 at 2:29
2

An implementation of this command was provided around 20 years ago, unfortunately forgot the source.

Here a basic approach:

(defun ar-simple-match-paren ()
  "Jump between matching paren. "
  (interactive)
  (cond ((eq 4 (car (syntax-after (point))))
     (forward-sexp)
     (forward-char -1))
    ((eq 5 (car (syntax-after (point))))
     (forward-char 1)
     (backward-sexp))
    (t (message "%s" "Don't see a matching paren"))))

Here with historical key "%":

(defun ar-match-paren (&optional arg)
  "Go to the matching brace, bracket or parenthesis if on its counterpart.

Otherwise insert the character, the key is assigned to, here `%'.
With \\[universal argument] insert a `%'. "
  (interactive "P")
  (if arg
      (self-insert-command (if (numberp arg) arg 1))
    (cond ((eq 4 (car (syntax-after (point))))
       (forward-sexp)
       (forward-char -1))
      ((eq 5 (car (syntax-after (point))))
       (forward-char 1)
       (backward-sexp))
      (t (self-insert-command 1)))))

(global-set-key [(%)] 'ar-match-paren)

Errors are mine...

2
  • Thanks @Andreas. Check github.com/redguardtoo/evil-matchit/commit/… for the solution. There are several issues in the code. (syantx-after) is not reliable enough, especially when used with evil-mode. (forward-char), (forward-sexp), (backward-char), and (backward-sexp) could be simplified by using (scan-sexps)
    – chen bin
    Commented Jun 23, 2015 at 13:26
  • @chenbin That's interesting, thanks! Wondering if we can use some from your library for nascending github.com/emacs-berlin/general-close Commented Jun 24, 2015 at 6:07
0

I used this :

(defun px-match-paren (arg)
  "Go to the matching paren if on a paren; otherwise insert <key>."
  (interactive "p")
  (cond
   ((char-equal 41 (char-before)) (backward-list 1))
   ((char-equal 125 (char-before)) (backward-list 1))
   ((and
     (char-equal 123 (char-before))
     (char-equal 10 (char-after)))
    (backward-char 1) (forward-list 1))
   ((looking-at "\\s\(") (forward-list 1))
   ((looking-at "\\s\)") (backward-list 1))
   (t (self-insert-command (or arg 1)))))

Until I learned about forward and backward-sexp (C-M-left and C-M-right) :)

0

How about the following?

(defun jump-to-matched ()
  (ignore-errors
    (save-excursion
      (forward-sexp)
      (1- (point)))))

You will get nil as an answer in case there is no match.

2
  • The above gives 10, as opposed to 9, in your first example, but I believe 10 is the correct answer since buffer positions start at 1.
    – Ruy
    Commented Oct 26, 2017 at 19:56
  • The use of "condition-case" is to avoid an error in case there is no matching bracket, which in turn will break @Andreas answer.
    – Ruy
    Commented Oct 26, 2017 at 20:00

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.