Skip to content

Commit 55e7483

Browse files
committed
Add a hook so that we briefly highlight the matching < when > is typed.
Also add an interactive command for finding the matching `<`. I'd like to bind this to `C-c >` (reserved for major modes), but I can't figure out what the local keymap is for rust-mode (help!).
1 parent f0d4c25 commit 55e7483

File tree

1 file changed

+75
-1
lines changed

1 file changed

+75
-1
lines changed

rust-mode.el

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,11 +481,84 @@ This is written mainly to be used as `end-of-defun-function' for Rust."
481481
;; There is no opening brace, so consider the whole buffer to be one "defun"
482482
(goto-char (point-max))))
483483

484+
;; Angle-bracket matching. This is kind of a hack designed to deal
485+
;; with the fact that we can't add angle-brackets to the list of
486+
;; matching characters unconditionally. Basically we just have some
487+
;; special-case code such that whenever `>` is typed, we look
488+
;; backwards to find a matching `<` and highlight it, whether or not
489+
;; this is *actually* appropriate. This could be annoying so it is
490+
;; configurable (but on by default because it's awesome).
491+
492+
(defcustom rust-blink-matching-angle-brackets t
493+
"Blink matching `<` (if any) when `>` is typed"
494+
:type 'boolean
495+
:group 'rust-mode)
496+
497+
(defvar rust-point-before-matching-angle-bracket 0)
498+
499+
(defvar rust-matching-angle-bracker-timer nil)
500+
501+
(defun rust-find-matching-angle-bracket ()
502+
(save-excursion
503+
(let ((angle-brackets 1)
504+
(start-point (point))
505+
(invalid nil))
506+
(while (and
507+
;; didn't find a match
508+
(> angle-brackets 0)
509+
;; we have no guarantee of a match, so give up eventually
510+
(< (- start-point (point)) blink-matching-paren-distance)
511+
;; didn't hit the top of the buffer
512+
(> (point) (point-min))
513+
;; didn't hit something else weird like a `;`
514+
(not invalid))
515+
(backward-char 1)
516+
(cond
517+
((looking-at ">")
518+
(setq angle-brackets (+ angle-brackets 1)))
519+
((looking-at "<")
520+
(setq angle-brackets (- angle-brackets 1)))
521+
((looking-at "[;{]")
522+
(setq invalid t))))
523+
(cond
524+
((= angle-brackets 0) (point))
525+
(t nil)))))
526+
527+
(defun rust-restore-point-after-angle-bracket ()
528+
(goto-char rust-point-before-matching-angle-bracket)
529+
(when rust-matching-angle-bracker-timer
530+
(cancel-timer rust-matching-angle-bracker-timer))
531+
(setq rust-matching-angle-bracker-timer nil)
532+
(remove-hook 'pre-command-hook 'rust-restore-point-after-angle-bracket))
533+
534+
(defun rust-match-angle-bracket-hook ()
535+
"If the most recently inserted character is a `>`, briefly moves point to matching `<` (if any)."
536+
(interactive)
537+
(when (and rust-blink-matching-angle-brackets
538+
(looking-back ">"))
539+
(let ((matching-angle-bracket-point (save-excursion
540+
(backward-char 1)
541+
(rust-find-matching-angle-bracket))))
542+
(when matching-angle-bracket-point
543+
(progn
544+
(setq rust-point-before-matching-angle-bracket (point))
545+
(goto-char matching-angle-bracket-point)
546+
(add-hook 'pre-command-hook 'rust-restore-point-after-angle-bracket)
547+
(setq rust-matching-angle-bracker-timer
548+
(run-at-time blink-matching-delay nil 'rust-restore-point-after-angle-bracket)))))))
549+
550+
(defun rust-match-angle-bracket ()
551+
"The point should be placed on a `>`. Finds the matching `<` and moves point there."
552+
(interactive)
553+
(let ((matching-angle-bracket-point (rust-find-matching-angle-bracket)))
554+
(if matching-angle-bracket-point
555+
(goto-char matching-angle-bracket-point)
556+
(message "no matching angle bracket found"))))
557+
484558
;; For compatibility with Emacs < 24, derive conditionally
485559
(defalias 'rust-parent-mode
486560
(if (fboundp 'prog-mode) 'prog-mode 'fundamental-mode))
487561

488-
489562
;;;###autoload
490563
(define-derived-mode rust-mode rust-parent-mode "Rust"
491564
"Major mode for Rust code."
@@ -519,6 +592,7 @@ This is written mainly to be used as `end-of-defun-function' for Rust."
519592
(setq-local end-of-defun-function 'rust-end-of-defun)
520593
(setq-local parse-sexp-lookup-properties t)
521594
(add-hook 'syntax-propertize-extend-region-functions 'rust-syntax-propertize-extend-region)
595+
(add-hook 'post-self-insert-hook 'rust-match-angle-bracket-hook)
522596
(setq-local syntax-propertize-function 'rust-syntax-propertize))
523597

524598
(defun rust-syntax-propertize-extend-region (start end)

0 commit comments

Comments
 (0)