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?

Drew
  • 75,699
  • 9
  • 109
  • 225
chen bin
  • 4,781
  • 18
  • 36
  • I wrote a plugin evil-matchit (https://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 Jun 19 '15 at 02:33

4 Answers4

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.

Jordon Biondo
  • 12,332
  • 2
  • 41
  • 62
  • 1
    thanks, I will try it asap. It's great if I can avoid writing the code. – chen bin Jun 17 '15 at 03: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. – Jordon Biondo Jun 17 '15 at 03: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 Jun 17 '15 at 09:58
  • @Tobias never knew about that function, you should write up an answer for it. – Jordon Biondo Jun 17 '15 at 11:51
  • thanks @Tobias, I think both of you answered my questions correctly because `forward-sexp` actually calls the `scan-sexps`. – chen bin Jun 19 '15 at 02: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...

Andreas Röhler
  • 1,894
  • 10
  • 10
  • Thanks @Andreas. Check https://github.com/redguardtoo/evil-matchit/commit/62cef4f91a813a03d6d2df35fe4727757e94e530 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 Jun 23 '15 at 13:26
  • @chenbin That's interesting, thanks! Wondering if we can use some from your library for nascending https://github.com/emacs-berlin/general-close – Andreas Röhler Jun 24 '15 at 06: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) :)

yPhil
  • 963
  • 5
  • 22
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.

Basil
  • 12,019
  • 43
  • 69
Ruy
  • 787
  • 4
  • 11
  • 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 Oct 26 '17 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 Oct 26 '17 at 20:00