Save Ukraine

Emacs: jump to matching paren/beginning of block

Christian Kruse,

One thing I always wanted for Emacs was a „jump to matching paren” button. And when I began to program Ruby, I also wanted a button to jump to the matching keyword when on end or to matching end when on a keyword. Well, lets first have a look at the „jump to matching paren” problem. This is relatively easy to solve since Emacs already has a function for that, so we only need a little bit of boilerplate:

(defun goto-match-paren (arg)
  "Go to the matching  if on (){}[], similar to vi style of % "
  (interactive "p")
  ;; first, check for "outside of bracket" positions expected by forward-sexp, etc
  (cond ((looking-at "[\[\(\{]") (forward-sexp))
        ((looking-back "[\]\)\}]" 1) (backward-sexp))
        ;; now, try to succeed from inside of a bracket
        ((looking-at "[\]\)\}]") (forward-char) (backward-sexp))
        ((looking-back "[\[\(\{]" 1) (backward-char) (forward-sexp))
        (t nil)
        )
  )
  

We basically check if we are on or behind a paren and jump to the matching one. Next: jump to matching keyword/end. This is also relatively easy to solve, since ruby-mode has the functions (ruby-beginning-of-block) and (ruby-end-of-block):

(defun goto-matching-ruby-block (arg)
  (cond
   ;; are we at an end keyword?
   ((equal (current-word) "end")
    (ruby-beginning-of-block))

;; or are we at a keyword itself? ((string-match (current-word) "\(for\|while\|until\|if\|class\|module\|case\|unless\|def\|begin\|do\)") (ruby-end-of-block) ) ) )

Now, since we want to use the same hotkey for both functions we need a dispatching function:

(defun dispatch-goto-matching (arg)
  (interactive "p")

(if (or (looking-at "[[({]") (looking-at "[])}]") (looking-back "[[({]" 1) (looking-back "[])}]" 1))

  (goto-match-paren arg)

(when (eq major-mode 'ruby-mode)
  (goto-matching-ruby-block arg)
  )

)

)

This function basically just looks if we are at a paren and calls (goto-matching-paren). If not and we're in ruby-mode, it calls (goto-matching-ruby-block). Now we bind this function to a key and tada! we can navigate very easily to the beginning or end of the block:

(global-set-key "\M--" 'dispatch-goto-matching)
  

I must say, the more LISP I write the more I like it.