Skip to content

Rainbow tree-sitter matches 🌈 #13530

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open

Rainbow tree-sitter matches 🌈 #13530

wants to merge 2 commits into from

Conversation

the-mikedavis
Copy link
Member

@the-mikedavis the-mikedavis commented May 14, 2025

This is #2857 based on the tree-house bindings. (Recreated after that PR closed unexpectedly)

Configurable, themable, nesting rainbow highlights:

demo

This approach uses the syntax tree to guide nesting levels and highlights so it ends up being much more flexible than a naive pair-matching implementation. For example, we can highlight </> when used in Rust type parameters like struct MyStruct<'a> { .. } and not mistakenly highlight </> comparison operators. We can also capture multiple nodes, for example the #, [ and ] characters in a Rust attribute like #[cfg(windows)] and none of these characters are required to be adjacent.

The cherry on top is that we can highlight according to the syntax tree's nesting information rather than whether characters surround each other. For example, in this TOML table, characters is a sub-table of editor.whitespace and the rainbow highlights reflect that fact.

theme = "default"

[editor.whitespace]
# <- red          ^ red
render = "all"
characters = { tab = "", newline = "" }
#            ^ yellow                   ^ yellow (comes after red in base16)

It also works across injected languages like javascript injected into HTML script tags.

(You can see each of these things in action in the gif above.)

How does it work?...

This implementation leverages tree-sitter queries. We use a QueryCaptures Iterator very similar to the existing one for syntax highlights to query the parsed syntax tree for new rainbows.scm patterns.

Two new captures - @rainbow.scope and @rainbow.bracket - track nesting level and capture syntax nodes for highlighting with rainbows. Specifically: @rainbow.scope pushes a RainbowScope on a stack. When @rainbow.bracket matches, we test that the captured node is a direct descendant of the current scope. If it is, we emit highlights for it according to the scope's level of nesting.


Configuration...

In the theme we add a rainbow key that takes a list of Styles. You can have however many you want and you can re-use colors from the palette or even throw modifiers in there:

# ~/.config/helix/themes/my_theme.toml

rainbow = ["#fc6b59", { fg = "lavender", bg = "comet" }, "lilac", { modifiers = ["reversed"] }]

[palette]
lavender = "#a4a0e8"
comet = "#5a5977"
lilac = "#dbbfef"

A default rainbow using the red, yellow, green, blue, cyan, and magenta terminal colors is used as a fallback.

Enable the rendering of rainbow brackets in your config.toml

# ~/.config/helix/config.toml
[editor]
rainbow-brackets = true

Or enable rendering per-language in languages.toml (this overrides the config.toml setting for any languages that set this value):

# ~/.config/helix/languages.toml
[[language]]
name = "scheme"
rainbow-brackets = true

[[language]]
name = "commonlisp"
rainbow-brackets = true

When the rainbow-brackets option is disabled, rainbow brackets are not rendered or computed.


Closes #695

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-tree-sitter Area: Tree-sitter
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Bracket pair colourization / Rainbow parenthesis
1 participant