Monday, May 21, 2007

Emacs has several packages for dealing with parentheses. Emacs comes with ways to highlight the matching parenthesis when you're on one; try show-paren-mode . One of the newer add-on packages is Nikolaj Schumacher's highlight-parentheses mode, which shows the parentheses that enclose the current cursor position. I tried modifying it to highlight the containing expressions instead of only their parentheses:

(defun hl-paren-highlight () "Highlight the parentheses around point." (unless (= (point) hl-paren-last-point) (save-excursion (let ((pos (point)) (match-pos (point)) (level -1) (max (1- (length hl-paren-overlays)))) (while (and match-pos (< level max)) (setq match-pos (when (setq pos (cadr (syn t ax-ppss pos) ) ) (ignore-errors (scan-sexps pos 1))) ) (when match-pos (if (eq 'expression hl-paren-type) (hl-paren-put-overlay pos match-pos (incf level)) (hl-paren-put-overlay pos (1+ pos) (incf level)) (hl-paren-put-overlay (1- match-pos) match-pos (incf level))) )) (while (< level max) (hl-paren-put-overlay nil nil (incf level)))) ) (setq hl-paren-last-point (point))) )

Unfortunately, as you can see, it's a mess. I tried better colors (white, gray, etc.) but I just couldn't make it usable. So I gave up on highlighting the regions and went back to highlighting just the parentheses. It's a bit better:

( defun hl-paren-highlight () "Highlight the parentheses around point." ( unless (= (point) hl-paren-last-point) ( save-excursion ( let ((pos (point)) (match-pos (point)) (level -1) (max (1- (length hl-paren-overlays)))) ( while (and match-pos (< level max)) ( setq match-pos ( when ( setq pos ( cadr ( syn t ax-ppss pos ) ) ) ( ignore-errors (scan-sexps pos 1)) ) ) ( when match-pos ( if (eq ' expression hl-paren-type) (hl-paren-put-overlay pos match-pos (incf level)) (hl-paren-put-overlay pos (1+ pos) (incf level)) (hl-paren-put-overlay (1- match-pos) match-pos (incf level))) ) ) ( while (< level max) (hl-paren-put-overlay nil nil (incf level))) ) ) ( setq hl-paren-last-point (point)) ) )

However it's still a bit too … colorful. So I changed it to simply make the enclosing parentheses bold:

( defun hl-paren-highlight () "Highlight the parentheses around point." ( unless (= (point) hl-paren-last-point) ( save-excursion ( let ((pos (point)) (match-pos (point)) (level -1) (max (1- (length hl-paren-overlays)))) ( while (and match-pos (< level max)) ( setq match-pos ( when ( setq pos (cadr (syn t ax-ppss pos))) ( ignore-errors (scan-sexps pos 1)))) ( when match-pos ( if (eq ' expression hl-paren-type) (hl-paren-put-overlay pos match-pos (incf level)) (hl-paren-put-overlay pos (1+ pos) (incf level)) (hl-paren-put-overlay (1- match-pos) match-pos (incf level))) )) ( while (< level max) (hl-paren-put-overlay nil nil (incf level))))) ( setq hl-paren-last-point (point))))

That's nicer, although perhaps too subtle. I added more bolding:

( defun hl-paren-highlight () "Highlight the parentheses around point." (unless (= (point) hl-paren-last-point) (save-excursion (let ((pos (point)) (match-pos (point)) (level -1) (max (1- (length hl-paren-overlays)))) (while (and match-pos (< level max)) (setq match-pos (when (setq pos (cadr (syn t ax-ppss pos ))) ( ignore-errors (scan-sexps pos 1)) )) ( when match-pos ( if (eq ' expression hl-paren-type) (hl-paren-put-overlay pos match-pos (incf level)) (hl-paren-put-overlay pos (1+ pos) (incf level)) (hl-paren-put-overlay (1- match-pos) match-pos (incf level))) ) ) ( while (< level max) (hl-paren-put-overlay nil nil (incf level))) )) (setq hl-paren-last-point (point)) ) )

It bolds the parentheses and also the first s-expression inside the opening parenthesis. It doesn't understand when the parentheses begin a form (instead of all the other uses of parentheses), so it sometimes highlights the first s-expression even when it's not special in any way. Despite this wart, I like this form of highlighting so far.

Update: [2007-05-29] However, there is one more thing I wanted. I de-emphasize parentheses by using a lighter color for them; I want the enclosing parentheses to be bold and black. However I want the enclosing first s-expressions to be bold, but not necessarily black. Note in the above example the keywords are normally blue, but when enclosing the current point they are black. I fixed this by adding separate highlighting for the enclosing parentheses and the first s-expression:

( defun hl-paren-highlight ( ) "Highlight the parentheses around point." ( unless ( = ( point ) hl-paren-last-point ) ( save-excursion ( let ( ( pos ( point ) ) ( match-pos ( point ) ) ( level -1 ) ( max ( 1- ( length hl-paren-overlays ) ) ) ) ( while ( and match-pos ( < level max ) ) ( setq match-pos ( when ( setq pos ( cadr ( syn t ax-ppss pos ) ) ) ( ignore-errors ( scan-sexps pos 1 ) ) ) ) ( when match-pos ( if ( eq ' expression hl-paren-type ) ( hl-paren-put-overlay pos match-pos ( incf level ) ) ( hl-paren-put-overlay pos ( 1+ pos ) ( incf level ) ) ( hl-paren-put-overlay ( 1- match-pos ) match-pos ( incf level ) ) ) ) ) ( while ( < level max ) ( hl-paren-put-overlay nil nil ( incf level ) ) ) ) ) ( setq hl-paren-last-point ( point ) ) ) )

I'm pretty happy with this variant of highlight-parentheses.el .

Update: [2014-05-10] Also check out the rainbow-blocks and rainbow-delimiters modes. For Python or Ruby code, highlight-indentation-mode may be useful. Update: [2019-08-05] Also see prism.

Labels: emacs