Emacs: jump to matching paren/beginning of block
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.