Subject: bug#74963: Ambiguous treesit named and anonymous nodes in ruby-ts-mode

From: Juri Linkov <[email-masked]>
Date: Mon, 13 Jan 2025 09:31:01 +0200

> I see that all ts-modes solve this common problem each in its own way
> (here 'list' indicates a list of strings that should match node names):
>
>   c-ts-mode:    (regexp-opt list 'symbols)
>   js-ts-mode:   (concat "\\_<" (regexp-opt list t) "\\_>")
>   java-ts-mode: (rx (or list))
>   ruby-ts-mode: (rx bol (or list) eol)
>
> Currently there is no uniform way to handle this frequent need.
> 'concat' like above looks too ugly, but 'regexp-opt' with the
> 'symbols' arg produces a strange regexp for matching symbols.

I was thinking about adding two functions treesit-regexp-strict
and treesit-regexp-lax.  But then discovered that some things
require specifying both strict and lax matches for the same thing.
For example, take treesit-thing-settings from c-ts-mode:

    (sentence
     ,(regexp-opt '("preproc"
                    "declaration"
                    "specifier"
                    "attributed_statement"
                    "labeled_statement"
                    "expression_statement"
                    "if_statement"
                    "switch_statement"
                    "do_statement"
                    "while_statement"
                    "for_statement"
                    "return_statement"
                    "break_statement"
                    "continue_statement"
                    "goto_statement"
                    "case_statement")))

"preproc" can be lax, this is fine to match all preprocessor directives.
But "declaration" should be strict and should not match "parameter_declaration".
Also "specifier" should not match "attribute_specifier" and "storage_class_specifier",
but only "enum_specifier" and "union_specifier" that end with the semicolon.
Also no need to specify all statements separately, it should be sufficient
to use lax match with "statement".

The most expressive language to specify all these requirements is the rx macro,
so let's use it in ts-modes.  Here is how the 'sentence' thing will look like:

    (sentence
     ,(rx (or (and bos (or "declaration"
                           "enum_specifier"
                           "union_specifier")
                   eos)
              (or "preproc"
                  "statement"))))