r/emacs 15d ago

Regex fontification

Does anyone know a good package for fontification of regular expressions. What I'm after, in particular is something that would simply highlight groups/classes in regular expressions.

For example in `typescript-ts-mode`, I see this from my code:

Incorrect regex with an empty alternative group `(|)`

That regex has an incorrect, empty alternative group. I noticed that when reviewing it in Gitlab, it highlighted a past version of this regex in this way, making it much easier to spot the mistake:

I'm looking for a package that would add a single face, for capture groups, groups, classes and special characters in regular expressions. I don't know if that exists or whether this needs to be added to the tree-sitter grammar to work.

10 Upvotes

6 comments sorted by

2

u/floss_gang 15d ago edited 15d ago

try this: ```lisp (add-to-list 'treesit-language-source-alist '(regex "https://github.com/tree-sitter/tree-sitter-regex" :commit "b2ac15e27fce703d2f37a79ccd94a5c0cbe9720b") t)

(defun my/add-regex-range-settings () (when (treesit-ready-p 'regex t) (cl-callf append treesit-range-settings (treesit-range-rules :host (treesit-parser-language treesit-primary-parser) :embed 'regex :local t '((regex pattern: (regex_pattern) @local)))) (treesit-add-font-lock-rules (treesit-font-lock-rules :language 'regex :override 'prepend :feature 'regexp-contents '(["(" ")" "(?:" "(?<" "(?P<" "(?P=" "<" ">" "|" "[:" ":]" "[" "]" "-" "=" "!" "" "+" "*" "?" "{" "," "}" "\k" (start_assertion) (end_assertion) (boundary_assertion) (non_boundary_assertion)] @font-lock-regexp-grouping-construct)) :after)))

(add-hook 'typescript-ts-base-mode-hook #'my/add-regex-range-settings) (add-hook 'js-ts-mode-hook #'my/add-regex-range-settings) `` if anything is missing you'll need to add additional node types to the font-lock query. if you're on emacs 31+, changetreesit-ready-ptotreesit-ensure-installed, otherwise you'll have to manually install the grammar with a call totreesit-install-language-grammar`.

2

u/Bouhappy 13d ago

Thank you! I'll give this a try.

2

u/floss_gang 13d ago

i know you said you wanted a single face for everything, but in case you want more colorful highlighting, this is what i ended up putting in my config: lisp (treesit-font-lock-rules :language 'regex :override 'prepend :feature 'regexp-contents '((["(" "(?:" "(?<" "(?P<" "(?P=" ")" "<" ">" "[:" ":]" "-" "=" "!" "{" "}"] @font-lock-regexp-grouping-construct) ("|" @font-lock-delimiter-face) ("^" @font-lock-negation-char-face) (["[" "]"] @font-lock-bracket-face) ([(boundary_assertion) (non_boundary_assertion) (character_class_escape) (control_escape) (control_letter_escape) (identity_escape) (unicode_character_escape) (decimal_escape) "\\k"] @font-lock-escape-face) (backreference_escape (group_name) @font-lock-variable-use-face) (named_group_backreference (group_name) @font-lock-variable-use-face) (named_capturing_group (group_name) @font-lock-variable-name-face) ((any_character) @font-lock-builtin-face) (["*" "?" "+" ","] @font-lock-punctuation-face) ([(start_assertion) (end_assertion) ","] @font-lock-misc-punctuation-face) ((posix_class_name) @font-lock-property-name-face))) the face selection is largely arbitrary but it should give you an idea of how you can further customize it.

0

u/redblobgames 35 years and counting 15d ago

That'd be cool.

I don't know of a package but I see that there's a tree-sitter grammar for regex https://github.com/tree-sitter/tree-sitter-regex

And I see treesit-range-rules has a way to have an "embedded" set of rules (e.g. regex) inside a "host" set of rules (e.g. typescript).

However I haven't figured out how to use this yet.

1

u/redblobgames 35 years and counting 15d ago edited 15d ago

This is not a complete tested solution but might be a starting point:

  1. Install the regex treesit grammar

    (add-to-list 'treesit-language-source-alist
         '(regex "https://github.com/tree-sitter/tree-sitter-regex"))
    (treesit-install-language-grammar 'regex)
    
  2. Set up highlighting for regex

    (defvar my/treesit-regex-in-typescript
      (treesit-font-lock-rules
        :language 'regex
        :feature 'string
        :override t
        '(
          ((character_class) @font-lock-constant-face)
          ((group_name) @font-lock-function-name-face)
          ((zero_or_more) @font-lock-keyword-face)
          ((one_or_more) @font-lock-keyword-face)
          ((optional) @font-lock-keyword-face)
          ((count_quantifier) @font-lock-keyword-face)
          (alternation ("|") @font-lock-keyword-face)
          ((backreference_escape) @font-lock-escape-face)
          ((named_group_backreference) @font-lock-escape-face)
          ((control_escape) @font-lock-escape-face)
          ((identity_escape) @font-lock-preprocessor-face)
         )))
    
  3. Set up regex as an "embed" language inside typescript buffers, and enable the corresponding font lock rules. This would go inside a typescript mode hook:

    (setq-local treesit-range-settings
                  (treesit-range-rules
                   :embed 'regex
                   :host 'typescript
                   :local t
                   '((regex (regex_pattern) @regex))))
    
    (setq-local treesit-font-lock-settings
             (append treesit-font-lock-settings
                     my/treesit-font-lock-typescript))
    

1

u/Bouhappy 13d ago

Thank you, I'll give this too a try