-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Rainbow tree-sitter matches 🌈 #2857
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
Rainbow tree-sitter matches 🌈 #2857
Conversation
Surprisingly little code to get this working 👍🏻 |
Would it make sense to use the color palette colors as default values if the rainbow feature is enabled? |
That could work but usually the palette has a bunch of different ui colors too, so some deeply nested brackets could end up using backgrounds / grays which may look bad. I think a default rainbow palette not based on the current theme could work ok |
You could cheat and ignore any scopes that |
I suspect theme colors may not be good for a theme's rainbow because they might blend in and end up looking confusing. I'll give it a try though - that would take all the guesswork out of having to come up with a default that looks good on all themes 😄 |
You could get fancy and calculate the color contrast and only pick colors that are distant enough :D |
This allows one to match on bracket nodes with named nodes in queries. Doing so is useful for tree-sitter based rainbow brackets (helix-editor/helix#2857).
well, I have a snippet to calculate the L* luminance value (as in Lab/LCH "professional" colour systems) from an sRGB triple. I use it for exactly that, grading and excluding colours with weak contrast in a CLI app 👍 if interested Helix would be most welcome to it, I can PR it to @the-mikedavis rainbow branch? |
Let's keep this branched focused just on the happy path for now (rainbows enabled and themed). If anything, we might want to merge this once cleaned up without any default behavior - i.e. the theme needs to have a |
The reason I lean towards a hardcoded default that uses red/orange?/yellow/green/blue/purple/etc is that it's easy to get a feel for how things are nested when you can predict the ordering of the bracket colors. If I know blue comes after green, I know where a blue tuple stands inside a huge green map or list literal. I don't want to discourage discussion on automatically determining a good rainbow though, it sounds like there are some cool color math tricks we could use 🙂 |
fc9aad4
to
1e323cf
Compare
This comment was marked as resolved.
This comment was marked as resolved.
1e323cf
to
1445e23
Compare
a78735b
to
6886965
Compare
05bd03c
to
3396f9b
Compare
I found an alternate approach for this that ends up being less complicated. Now it works similarly to the locals tracking system in the syntax highlights iterator. The queries end up being more intuitive to write and more flexible (see the updated PR description). I think this approach is general-purpose enough: I've worked through 27 languages so far with the toughest being HTML. HTML works now so that nested XML elements behave like other nesting scopes rather than just highlighting the |
656e3fd
to
be2e9f4
Compare
I don’t understand why this PR has been pending since 2022, especially since it’s fully functional and adds valuable feature. Leaving this to plugins isn’t ideal, as it could hurt performance. VS Code faced a similar issue and ended up integrating support directly. |
For those asking "why not merge dis??", read #2857 (comment) and explore the different iaauea/PRs related to treesitter (that are all somewhat related to one another). |
For some more details on top of that older comment: @pascalkuthe and I are working on new tree-sitter bindings (#10286) and the new highlighter based on those is written a way that makes running a query over injection layers very easy. (This feature uses that so that rainbow brackets are highlighted even within code fences in markdown, for example, or within JS in As for the feature itself: I still use this in my driver branch and have been keeping it more-or-less up to date. I'll give this PR a proper rebase so it's easier for others to use too. I'm not totally sure where other maintainers stand on the feature but I remember @archseer considering it after working on a bunch of scheme code. Even if it's something we don't pull in as a core feature I'd be interested in maintaining a plugin for it officially or personally. |
2dc56f0
to
1a5eb55
Compare
It's not possible to rebase and build this branch any more :( |
I have this rebased locally on #12972 - I'll push that up when I get the chance. (Tree-house covers the main part of this that was hacky since it introduces a generic query iterator across injection layers) |
This type also exists on `Editor`. This change brings it to the `Document` as well because the replacement for `Syntax` in the child commits will eliminate `Syntax`'s copy of `syn_loader`. `Syntax` will also be responsible for returning the highlighter and query iterators (which will borrow the loader), so the loader must be separated from that type. In the long run, when we make a larger refactor to have `Document::apply` be a function of the `Editor` instead of the `Document`, we will be able to drop this field on `Document` - it is currently only necessary for `Document::apply`. Once we make that refactor, we will be able to eliminate the surrounding `Arc` in `Arc<ArcSwap<syntax::Loader>>` and use the `ArcSwap` directly instead.
Co-authored-by: Nik Revenco <[email protected]>
1a5eb55
to
ea7b6ce
Compare
813f771
to
974caf0
Compare
Interesting... I guess fast-forward merging closes PRs targetting the merged branch. I'll reopen this in a new PR Re-opened in #13530 |
Configurable, themable, nesting rainbow highlights:
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 likestruct 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 ofeditor.whitespace
and the rainbow highlights reflect that fact.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 newrainbows.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 aRainbowScope
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 ofStyle
s. You can have however many you want and you can re-use colors from the palette or even throw modifiers in there: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
Or enable rendering per-language in
languages.toml
(this overrides theconfig.toml
setting for any languages that set this value):When the
rainbow-brackets
option is disabled, rainbow brackets are not rendered or computed.Status...
The current implementation is working well. Take it for a spin and let me know if you find any bugs!