This commit is contained in:
Mikhail Pertsev 2025-08-26 15:11:09 -03:00 committed by GitHub
commit 05e7b6e133
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 1034 additions and 3 deletions

View file

@ -979,7 +979,18 @@ impl DisplaySnapshot {
}
}
if let Some(highlight_style) = highlight_style.as_mut() {
// If the highlight has a color with full opacity (alpha = 1.0),
// it should completely replace the syntax highlight color
if let Some(color) = processed_highlight.color {
if color.a >= 1.0 {
// Replace syntax highlighting entirely with our highlight
highlight_style = Some(processed_highlight);
} else if let Some(highlight_style) = highlight_style.as_mut() {
highlight_style.highlight(processed_highlight);
} else {
highlight_style = Some(processed_highlight);
}
} else if let Some(highlight_style) = highlight_style.as_mut() {
highlight_style.highlight(processed_highlight);
} else {
highlight_style = Some(processed_highlight);

View file

@ -35,6 +35,7 @@ mod mouse_context_menu;
pub mod movement;
mod persistence;
mod proposed_changes_editor;
mod rainbow_brackets;
mod rust_analyzer_ext;
pub mod scroll;
mod selections_collection;

View file

@ -28,6 +28,7 @@ use crate::{
inlay_hint_settings,
items::BufferSearchHighlights,
mouse_context_menu::{self, MenuPosition},
rainbow_brackets,
scroll::{ActiveScrollbarState, ScrollbarThumbState, scroll_amount::ScrollAmount},
};
use buffer_diff::{DiffHunkStatus, DiffHunkStatusKind};
@ -9043,8 +9044,11 @@ impl Element for EditorElement {
diff_hunk_control_bounds,
});
self.editor.update(cx, |editor, _| {
editor.last_position_map = Some(position_map.clone())
self.editor.update(cx, |editor, cx| {
editor.last_position_map = Some(position_map.clone());
// Refresh rainbow brackets for the visible range
rainbow_brackets::refresh_rainbow_bracket_highlights(editor, window, cx);
});
EditorLayout {

View file

@ -0,0 +1,223 @@
use crate::Editor;
use gpui::{Context, HighlightStyle, Window};
use language::{Bias, BufferSnapshot};
use std::collections::HashMap;
use std::ops::Range;
use text::ToOffset;
use theme::ActiveTheme;
/// Compute rainbow bracket highlights for the visible range
pub fn compute_rainbow_brackets_for_range(
buffer_snapshot: &BufferSnapshot,
range: Range<usize>,
) -> Option<HashMap<usize, Vec<Range<usize>>>> {
let language = buffer_snapshot.language()?;
let rainbow_config = language.grammar()?.rainbow_config.as_ref()?;
let mut highlights_by_level: HashMap<usize, Vec<Range<usize>>> = HashMap::new();
// Similar to Helix's RainbowScope structure
#[derive(Debug)]
struct RainbowScope {
end_byte: usize,
node: Option<usize>, // node ID
level: usize,
}
let mut scope_stack = Vec::<RainbowScope>::new();
// Use the proper tree-sitter query matching API
let mut matches = buffer_snapshot.matches(range, |grammar| {
grammar.rainbow_config.as_ref().map(|c| &c.query)
});
// Process all matches in order
while let Some(mat) = matches.peek() {
let byte_range = mat.captures[0].node.byte_range();
// Pop any scopes that end before this capture begins
while scope_stack
.last()
.is_some_and(|scope| byte_range.start >= scope.end_byte)
{
scope_stack.pop();
}
// Check which capture this is
let is_scope_capture = rainbow_config
.scope_capture_ix
.map_or(false, |ix| mat.captures.iter().any(|c| c.index == ix));
let is_bracket_capture = rainbow_config
.bracket_capture_ix
.map_or(false, |ix| mat.captures.iter().any(|c| c.index == ix));
if is_scope_capture {
// Process scope capture
if let Some(scope_capture) = rainbow_config
.scope_capture_ix
.and_then(|ix| mat.captures.iter().find(|c| c.index == ix))
{
let node = scope_capture.node;
let byte_range = node.byte_range();
scope_stack.push(RainbowScope {
end_byte: byte_range.end,
node: if rainbow_config
.include_children_patterns
.contains(&mat.pattern_index)
{
None
} else {
Some(node.id())
},
level: scope_stack.len(),
});
}
}
if is_bracket_capture {
// Process bracket capture
if let Some(bracket_capture) = rainbow_config
.bracket_capture_ix
.and_then(|ix| mat.captures.iter().find(|c| c.index == ix))
{
let node = bracket_capture.node;
let byte_range = node.byte_range();
if let Some(scope) = scope_stack.last() {
// Check if this bracket should be highlighted
let should_highlight = if let Some(scope_node_id) = scope.node {
// Only highlight if bracket is a direct child of the scope node
node.parent()
.map_or(false, |parent| parent.id() == scope_node_id)
} else {
// include-children mode: highlight all brackets in this scope
true
};
if should_highlight {
let level = scope.level % 10;
highlights_by_level
.entry(level)
.or_default()
.push(byte_range);
}
}
}
}
// IMPORTANT: Always advance to prevent infinite loop
matches.advance();
}
Some(highlights_by_level)
}
/// Rainbow bracket highlighting uses multiple colors to distinguish bracket nesting levels
pub fn refresh_rainbow_bracket_highlights(
editor: &mut Editor,
_window: &mut Window,
cx: &mut Context<Editor>,
) {
// Clear existing rainbow highlights for all levels
clear_current_rainbow_highlights(editor, cx);
let multi_buffer = editor.buffer().read(cx);
let multi_buffer_snapshot = multi_buffer.snapshot(cx);
// For now, handle only singleton buffers
if let Some((_, _, buffer_snapshot)) = multi_buffer_snapshot.as_singleton() {
// Compute only for the visible range
// Get the display map to find visible rows
let display_map = editor.display_map.update(cx, |map, cx| map.snapshot(cx));
let scroll_position = editor.scroll_position(cx);
let height = editor.visible_line_count().unwrap_or(50.0);
// Calculate visible display rows
let start_row = scroll_position.y.floor() as u32;
let end_row =
((scroll_position.y + height).ceil() as u32).min(display_map.max_point().row().0);
// Convert display rows to buffer offsets
let start_point = display_map.display_point_to_point(
crate::DisplayPoint::new(crate::DisplayRow(start_row), 0),
crate::Bias::Left,
);
let end_point = display_map.display_point_to_point(
crate::DisplayPoint::new(crate::DisplayRow(end_row), 0),
crate::Bias::Right,
);
let start_offset = start_point.to_offset(buffer_snapshot);
let end_offset = end_point.to_offset(buffer_snapshot);
if let Some(highlights_by_level) =
compute_rainbow_brackets_for_range(buffer_snapshot, start_offset..end_offset)
{
// Use Theme's accent colors for rainbow brackets
let accent_colors = cx.theme().accents().clone();
// Apply highlights by level
for (level, ranges) in highlights_by_level {
// Convert text ranges to multi-buffer anchors
let multi_buffer_ranges: Vec<_> = ranges
.into_iter()
.map(|range| {
let start = multi_buffer_snapshot.anchor_at(range.start, Bias::Left);
let end = multi_buffer_snapshot.anchor_at(range.end, Bias::Right);
start..end
})
.collect();
// Get color from theme accents based on level
let color = accent_colors.color_for_index(level as u32);
let style = HighlightStyle {
color: Some(color),
..Default::default()
};
match level {
0 => editor.highlight_text::<RainbowLevel0>(multi_buffer_ranges, style, cx),
1 => editor.highlight_text::<RainbowLevel1>(multi_buffer_ranges, style, cx),
2 => editor.highlight_text::<RainbowLevel2>(multi_buffer_ranges, style, cx),
3 => editor.highlight_text::<RainbowLevel3>(multi_buffer_ranges, style, cx),
4 => editor.highlight_text::<RainbowLevel4>(multi_buffer_ranges, style, cx),
5 => editor.highlight_text::<RainbowLevel5>(multi_buffer_ranges, style, cx),
6 => editor.highlight_text::<RainbowLevel6>(multi_buffer_ranges, style, cx),
7 => editor.highlight_text::<RainbowLevel7>(multi_buffer_ranges, style, cx),
8 => editor.highlight_text::<RainbowLevel8>(multi_buffer_ranges, style, cx),
_ => editor.highlight_text::<RainbowLevel9>(multi_buffer_ranges, style, cx),
}
}
}
}
}
fn clear_current_rainbow_highlights(editor: &mut Editor, cx: &mut Context<Editor>) {
editor.clear_highlights::<RainbowLevel0>(cx);
editor.clear_highlights::<RainbowLevel1>(cx);
editor.clear_highlights::<RainbowLevel2>(cx);
editor.clear_highlights::<RainbowLevel3>(cx);
editor.clear_highlights::<RainbowLevel4>(cx);
editor.clear_highlights::<RainbowLevel5>(cx);
editor.clear_highlights::<RainbowLevel6>(cx);
editor.clear_highlights::<RainbowLevel7>(cx);
editor.clear_highlights::<RainbowLevel8>(cx);
editor.clear_highlights::<RainbowLevel9>(cx);
}
// Marker types for different rainbow levels
enum RainbowLevel0 {}
enum RainbowLevel1 {}
enum RainbowLevel2 {}
enum RainbowLevel3 {}
enum RainbowLevel4 {}
enum RainbowLevel5 {}
enum RainbowLevel6 {}
enum RainbowLevel7 {}
enum RainbowLevel8 {}
enum RainbowLevel9 {}
#[cfg(test)]
#[path = "rainbow_brackets_tests.rs"]
mod tests;

View file

@ -0,0 +1,341 @@
use crate::editor_tests::init_test;
use crate::rainbow_brackets::compute_rainbow_brackets_for_range;
use crate::test::editor_lsp_test_context::EditorLspTestContext;
use gpui::TestAppContext;
use indoc::indoc;
use language::{BracketPair, BracketPairConfig, Language, LanguageConfig, LanguageMatcher};
/// Helper function to create a test language with bracket configuration
fn create_test_language() -> Language {
// Use a simpler language without tree-sitter queries for basic tests
Language::new(
LanguageConfig {
name: "TestLang".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["test".to_string()],
..Default::default()
},
brackets: BracketPairConfig {
pairs: vec![
BracketPair {
start: "{".to_string(),
end: "}".to_string(),
close: false,
surround: false,
newline: true,
},
BracketPair {
start: "(".to_string(),
end: ")".to_string(),
close: false,
surround: false,
newline: true,
},
BracketPair {
start: "[".to_string(),
end: "]".to_string(),
close: false,
surround: false,
newline: true,
},
],
..Default::default()
},
..Default::default()
},
None, // No tree-sitter grammar for simple tests
)
}
// Test that the empty buffer test works correctly
#[gpui::test]
async fn test_empty_buffer(cx: &mut TestAppContext) {
init_test(cx, |_| {});
let language = create_test_language();
let mut cx = EditorLspTestContext::new(language, Default::default(), cx).await;
cx.set_state("ˇ");
let buffer = cx.editor(|editor, _, cx| {
let multi_buffer = editor.buffer().read(cx).snapshot(cx);
multi_buffer.as_singleton().unwrap().2.clone()
});
let highlights = compute_rainbow_brackets_for_range(&buffer, 0..0);
// Without tree-sitter, should return None
assert!(highlights.is_none());
}
// Test simple single bracket pair detection
#[gpui::test]
async fn test_simple_bracket_detection(cx: &mut TestAppContext) {
init_test(cx, |_| {});
let language = create_test_language();
let mut cx = EditorLspTestContext::new(language, Default::default(), cx).await;
// Test single bracket pair
cx.set_state(indoc! {r#"
fn test() {
printlnˇ!("Hello");
}
"#});
let buffer = cx.editor(|editor, _, cx| {
let multi_buffer = editor.buffer().read(cx).snapshot(cx);
multi_buffer.as_singleton().unwrap().2.clone()
});
let highlights = compute_rainbow_brackets_for_range(&buffer, 0..buffer.len());
// Without tree-sitter, should return None
assert!(highlights.is_none());
}
// Test nested bracket levels
#[gpui::test]
async fn test_nested_bracket_levels(cx: &mut TestAppContext) {
init_test(cx, |_| {});
let language = create_test_language();
let mut cx = EditorLspTestContext::new(language, Default::default(), cx).await;
// Test nested brackets with different types
cx.set_state(indoc! {r#"
fn test() {
let array = [1, 2, 3];
if (condition) {
processˇ(array[0]);
}
}
"#});
let buffer = cx.editor(|editor, _, cx| {
let multi_buffer = editor.buffer().read(cx).snapshot(cx);
multi_buffer.as_singleton().unwrap().2.clone()
});
let highlights = compute_rainbow_brackets_for_range(&buffer, 0..buffer.len());
// Without tree-sitter, should return None
assert!(highlights.is_none());
}
// Test level wrapping
#[gpui::test]
async fn test_level_wrapping(cx: &mut TestAppContext) {
init_test(cx, |_| {});
let language = create_test_language();
let mut cx = EditorLspTestContext::new(language, Default::default(), cx).await;
// Test that levels wrap around after 10 (0-9)
let deeply_nested = r#"
fn testˇ() { // Level 0
{ // Level 1
{ // Level 2
{ // Level 3
{ // Level 4
{ // Level 5
{ // Level 6
{ // Level 7
{ // Level 8
{ // Level 9
{ // Level 0 (wrapped)
println!("Deep!");
}
}
}
}
}
}
}
}
}
}
}
"#;
cx.set_state(deeply_nested);
let buffer = cx.editor(|editor, _, cx| {
let multi_buffer = editor.buffer().read(cx).snapshot(cx);
multi_buffer.as_singleton().unwrap().2.clone()
});
let highlights = compute_rainbow_brackets_for_range(&buffer, 0..buffer.len());
// Without tree-sitter, should return None
assert!(highlights.is_none());
}
// Test mixed bracket types
#[gpui::test]
async fn test_mixed_bracket_types(cx: &mut TestAppContext) {
init_test(cx, |_| {});
let language = create_test_language();
let mut cx = EditorLspTestContext::new(language, Default::default(), cx).await;
cx.set_state(indoc! {r#"
fn test() {
let tuple = (1, 2, 3);
let array = [4, 5, 6];
let result = computeˇ({
value: tuple.0 + array[0]
});
}
"#});
let buffer = cx.editor(|editor, _, cx| {
let multi_buffer = editor.buffer().read(cx).snapshot(cx);
multi_buffer.as_singleton().unwrap().2.clone()
});
let highlights = compute_rainbow_brackets_for_range(&buffer, 0..buffer.len());
// Without tree-sitter, should return None
assert!(highlights.is_none());
}
// Test that unclosed brackets are handled gracefully
#[gpui::test]
async fn test_unclosed_brackets(cx: &mut TestAppContext) {
init_test(cx, |_| {});
let language = create_test_language();
let mut cx = EditorLspTestContext::new(language, Default::default(), cx).await;
// Test with unclosed brackets
cx.set_state(indoc! {r#"
fn test() {
let array = [1, 2, 3; // Missing closing bracket
if (condition { // Mixed up brackets
printlnˇ!("Test");
}
// Missing closing brace for function
"#});
let buffer = cx.editor(|editor, _, cx| {
let multi_buffer = editor.buffer().read(cx).snapshot(cx);
multi_buffer.as_singleton().unwrap().2.clone()
});
let highlights = compute_rainbow_brackets_for_range(&buffer, 0..buffer.len());
// Without tree-sitter, should return None
assert!(highlights.is_none());
}
// Test brackets in strings and comments
#[gpui::test]
async fn test_brackets_in_strings_and_comments(cx: &mut TestAppContext) {
init_test(cx, |_| {});
let language = create_test_language();
let mut cx = EditorLspTestContext::new(language, Default::default(), cx).await;
cx.set_state(indoc! {r#"
fn test() {
// This { bracket } should not be highlighted
let string = "Another { bracket } in string";
let actual = { valueˇ: 42 };
}
"#});
let buffer = cx.editor(|editor, _, cx| {
let multi_buffer = editor.buffer().read(cx).snapshot(cx);
multi_buffer.as_singleton().unwrap().2.clone()
});
let highlights = compute_rainbow_brackets_for_range(&buffer, 0..buffer.len());
// Without tree-sitter, should return None
assert!(highlights.is_none());
}
// Test with real Rust language and brackets.scm query
#[gpui::test]
async fn test_rust_rainbow_brackets(cx: &mut TestAppContext) {
init_test(cx, |_| {});
// Create a Rust-like language with proper tree-sitter and bracket query
let rust_lang = Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
brackets: BracketPairConfig {
pairs: vec![
BracketPair {
start: "{".to_string(),
end: "}".to_string(),
close: false,
surround: false,
newline: true,
},
BracketPair {
start: "(".to_string(),
end: ")".to_string(),
close: false,
surround: false,
newline: true,
},
BracketPair {
start: "[".to_string(),
end: "]".to_string(),
close: false,
surround: false,
newline: false,
},
],
..Default::default()
},
..Default::default()
},
Some(tree_sitter_rust::LANGUAGE.into()),
)
.with_brackets_query(indoc! {r#"
; Rainbow bracket scopes for common Rust constructs
[(block) (match_block) (use_list) (field_initializer_list)] @rainbow.scope
; Rainbow brackets - actual bracket characters
["{" "}" "(" ")" "[" "]"] @rainbow.bracket
"#})
.unwrap();
let mut cx = EditorLspTestContext::new(rust_lang, Default::default(), cx).await;
// Test real Rust code
cx.set_state(indoc! {r#"
fn process_data() {
let array = [1, 2, 3];
if true {
println!ˇ("Hello");
}
}
"#});
let buffer = cx.editor(|editor, _, cx| {
let multi_buffer = editor.buffer().read(cx).snapshot(cx);
multi_buffer.as_singleton().unwrap().2.clone()
});
let highlights = compute_rainbow_brackets_for_range(&buffer, 0..buffer.len());
// With proper tree-sitter setup, we should get highlights
assert!(highlights.is_some());
let highlights = highlights.unwrap();
// Verify we have some levels
assert!(
!highlights.is_empty(),
"Should have at least one level of brackets"
);
// Verify structure is correct
for (level, ranges) in highlights {
assert!(level < 10, "Level {} should be < 10", level);
for range in ranges {
assert!(range.start < range.end);
assert!(range.end <= buffer.len());
}
}
}

View file

@ -1104,6 +1104,7 @@ pub struct Grammar {
pub embedding_config: Option<EmbeddingConfig>,
pub(crate) injection_config: Option<InjectionConfig>,
pub(crate) override_config: Option<OverrideConfig>,
pub rainbow_config: Option<RainbowConfig>,
pub(crate) debug_variables_config: Option<DebugVariablesConfig>,
pub(crate) highlight_map: Mutex<HighlightMap>,
}
@ -1246,6 +1247,13 @@ struct BracketsPatternConfig {
newline_only: bool,
}
pub struct RainbowConfig {
pub query: Query,
pub scope_capture_ix: Option<u32>,
pub bracket_capture_ix: Option<u32>,
pub include_children_patterns: HashSet<usize>,
}
pub struct DebugVariablesConfig {
pub query: Query,
pub objects_by_capture_ix: Vec<(u32, DebuggerTextObject)>,
@ -1281,6 +1289,7 @@ impl Language {
override_config: None,
redactions_config: None,
runnable_config: None,
rainbow_config: None,
error_query: Query::new(&ts_language, "(ERROR) @error").ok(),
debug_variables_config: None,
ts_language,
@ -1358,6 +1367,7 @@ impl Language {
.with_text_object_query(query.as_ref())
.context("Error loading textobject query")?;
}
// Rainbow queries are now loaded as part of brackets.scm
if let Some(query) = queries.debugger {
self = self
.with_debug_variables_query(query.as_ref())
@ -1499,16 +1509,24 @@ impl Language {
pub fn with_brackets_query(mut self, source: &str) -> Result<Self> {
let grammar = self.grammar_mut().context("cannot mutate grammar")?;
// Check if we have rainbow captures in the query
let query = Query::new(&grammar.ts_language, source)?;
let mut open_capture_ix = None;
let mut close_capture_ix = None;
let mut rainbow_scope_capture_ix = None;
let mut rainbow_bracket_capture_ix = None;
get_capture_indices(
&query,
&mut [
("open", &mut open_capture_ix),
("close", &mut close_capture_ix),
("rainbow.scope", &mut rainbow_scope_capture_ix),
("rainbow.bracket", &mut rainbow_bracket_capture_ix),
],
);
// Process bracket matching patterns
let patterns = (0..query.pattern_count())
.map(|ix| {
let mut config = BracketsPatternConfig::default();
@ -1520,6 +1538,8 @@ impl Language {
config
})
.collect();
// Set brackets config if we have bracket matching captures
if let Some((open_capture_ix, close_capture_ix)) = open_capture_ix.zip(close_capture_ix) {
grammar.brackets_config = Some(BracketsConfig {
query,
@ -1528,6 +1548,28 @@ impl Language {
patterns,
});
}
// Set rainbow config if we have rainbow captures
// We need to create a new query for rainbow config since we can't share the query for bracket highlights
if rainbow_scope_capture_ix.is_some() || rainbow_bracket_capture_ix.is_some() {
let rainbow_query = Query::new(&grammar.ts_language, source)?;
let mut include_children_patterns = HashSet::default();
for ix in 0..rainbow_query.pattern_count() {
for setting in rainbow_query.property_settings(ix) {
if setting.key.as_ref() == "rainbow.include-children" {
include_children_patterns.insert(ix);
}
}
}
grammar.rainbow_config = Some(RainbowConfig {
query: rainbow_query,
scope_capture_ix: rainbow_scope_capture_ix,
bracket_capture_ix: rainbow_bracket_capture_ix,
include_children_patterns,
});
}
Ok(self)
}
@ -1730,6 +1772,38 @@ impl Language {
Ok(self)
}
pub fn with_rainbow_query(mut self, source: &str) -> Result<Self> {
let grammar = self.grammar_mut().context("cannot mutate grammar")?;
let query = Query::new(&grammar.ts_language, source)?;
let mut scope_capture_ix = None;
let mut bracket_capture_ix = None;
get_capture_indices(
&query,
&mut [
("rainbow.scope", &mut scope_capture_ix),
("rainbow.bracket", &mut bracket_capture_ix),
],
);
let mut include_children_patterns = HashSet::default();
for ix in 0..query.pattern_count() {
for setting in query.property_settings(ix) {
if setting.key.as_ref() == "rainbow.include-children" {
include_children_patterns.insert(ix);
}
}
}
grammar.rainbow_config = Some(RainbowConfig {
query,
scope_capture_ix,
bracket_capture_ix,
include_children_patterns,
});
Ok(self)
}
fn grammar_mut(&mut self) -> Option<&mut Grammar> {
Arc::get_mut(self.grammar.as_mut()?)
}

View file

@ -232,6 +232,7 @@ pub const QUERY_FILENAME_PREFIXES: &[(
("runnables", |q| &mut q.runnables),
("debugger", |q| &mut q.debugger),
("textobjects", |q| &mut q.text_objects),
// Rainbow queries are now loaded as part of brackets.scm
];
/// Tree-sitter language queries for a given language.

View file

@ -1,3 +1,4 @@
; Bracket matching pairs
("(" @open ")" @close)
("[" @open "]" @close)
("{" @open "}" @close)
@ -10,3 +11,26 @@
((if_statement ("then" @open) (elif_clause ("elif" @close))) (#set! newline.only))
((if_statement ("then" @open) (else_clause ("else" @close))) (#set! newline.only))
((if_statement ("then" @open "fi" @close)) (#set! newline.only))
; Rainbow bracket scopes
[
(function_definition)
(compound_statement)
(subshell)
(test_command)
(subscript)
(parenthesized_expression)
(array)
(expansion)
(command_substitution)
] @rainbow.scope
; Rainbow brackets
[
"(" ")"
"((" "))"
"${" "$("
"{" "}"
"[" "]"
"[[" "]]"
] @rainbow.bracket

View file

@ -1,5 +1,38 @@
; Bracket matching pairs
("(" @open ")" @close)
("[" @open "]" @close)
("{" @open "}" @close)
("\"" @open "\"" @close)
("'" @open "'" @close)
; Rainbow bracket scopes
[
(preproc_params)
(preproc_defined)
(argument_list)
(attribute_specifier)
(ms_declspec_modifier)
(declaration_list)
(parenthesized_declarator)
(parenthesized_expression)
(abstract_parenthesized_declarator)
(array_declarator)
(compound_statement)
(initializer_list)
(compound_literal_expression)
(enumerator_list)
(field_declaration_list)
(parameter_list)
(for_statement)
(macro_type_specifier)
(subscript_expression)
(subscript_designator)
(cast_expression)
] @rainbow.scope
; Rainbow brackets
[
"(" ")"
"{" "}"
"[" "]"
] @rainbow.bracket

View file

@ -1,5 +1,58 @@
; Bracket matching pairs
("(" @open ")" @close)
("[" @open "]" @close)
("{" @open "}" @close)
("\"" @open "\"" @close)
("'" @open "'" @close)
; Rainbow bracket scopes
[
; c
(preproc_params)
(preproc_defined)
(argument_list)
(attribute_specifier)
(ms_declspec_modifier)
(declaration_list)
(parenthesized_declarator)
(parenthesized_expression)
(abstract_parenthesized_declarator)
(array_declarator)
(compound_statement)
(initializer_list)
(compound_literal_expression)
(enumerator_list)
(field_declaration_list)
(parameter_list)
(for_statement)
; (macro_type_specifier) - not part of cpp
(subscript_expression)
(subscript_designator)
(cast_expression)
; cpp
(decltype)
(explicit_function_specifier)
(template_parameter_list)
(template_argument_list)
(parameter_list)
(argument_list)
(structured_binding_declarator)
(noexcept)
(throw_specifier)
(static_assert_declaration)
(condition_clause)
(for_range_loop)
(new_declarator)
(delete_expression "[" "]")
(lambda_capture_specifier)
(sizeof_expression)
] @rainbow.scope
; Rainbow brackets
[
"(" ")"
"{" "}"
"[" "]"
"<" ">"
] @rainbow.bracket

View file

@ -1,5 +1,24 @@
; Bracket matching pairs
("(" @open ")" @close)
("[" @open "]" @close)
("{" @open "}" @close)
("\"" @open "\"" @close)
("'" @open "'" @close)
; Rainbow bracket scopes
[
(keyframe_block_list)
(block)
(attribute_selector)
(feature_query)
(parenthesized_query)
(selector_query)
(parenthesized_value)
(arguments)
] @rainbow.scope
; Rainbow brackets
[
"{" "}"
"(" ")"
] @rainbow.bracket

View file

@ -1,6 +1,43 @@
; Bracket matching pairs
("(" @open ")" @close)
("[" @open "]" @close)
("{" @open "}" @close)
("\"" @open "\"" @close)
("`" @open "`" @close)
((rune_literal) @open @close)
; Rainbow bracket scopes
[
(import_spec_list)
(const_declaration)
(var_declaration)
(type_parameter_list)
(parameter_list)
(type_declaration)
(parenthesized_type)
(type_arguments)
(array_type)
(implicit_length_array_type)
(slice_type)
(field_declaration_list)
(interface_type)
(map_type)
(block)
(expression_switch_statement)
(type_switch_statement)
(select_statement)
(parenthesized_expression)
(argument_list)
(index_expression)
(slice_expression)
(type_assertion_expression)
(type_conversion_expression)
(literal_value)
] @rainbow.scope
; Rainbow brackets
[
"(" ")"
"[" "]"
"{" "}"
] @rainbow.bracket

View file

@ -1,3 +1,4 @@
; Bracket matching pairs
("(" @open ")" @close)
("[" @open "]" @close)
("{" @open "}" @close)
@ -7,3 +8,21 @@
("\"" @open "\"" @close)
("'" @open "'" @close)
("`" @open "`" @close)
; Rainbow bracket scopes
[
(object)
(array)
(arguments)
(formal_parameters)
(statement_block)
(parenthesized_expression)
(call_expression)
] @rainbow.scope
; Rainbow brackets
[
"[" "]"
"{" "}"
"(" ")"
] @rainbow.bracket

View file

@ -1,3 +1,16 @@
; Bracket matching pairs
("[" @open "]" @close)
("{" @open "}" @close)
("\"" @open "\"" @close)
; Rainbow bracket scopes
[
(object)
(array)
] @rainbow.scope
; Rainbow brackets
[
"[" "]"
"{" "}"
] @rainbow.bracket

View file

@ -1,3 +1,16 @@
; Bracket matching pairs
("[" @open "]" @close)
("{" @open "}" @close)
("\"" @open "\"" @close)
; Rainbow bracket scopes
[
(object)
(array)
] @rainbow.scope
; Rainbow brackets
[
"[" "]"
"{" "}"
] @rainbow.bracket

View file

@ -1,4 +1,36 @@
; Bracket matching pairs
("(" @open ")" @close)
("[" @open "]" @close)
("{" @open "}" @close)
((string_start) @open (string_end) @close)
; Rainbow bracket scopes
[
(future_import_statement)
(import_from_statement)
(with_clause)
(parameters)
(parenthesized_list_splat)
(argument_list)
(tuple_pattern)
(list_pattern)
(subscript)
(list)
(set)
(tuple)
(dictionary)
(dictionary_comprehension)
(set_comprehension)
(list_comprehension)
(generator_expression)
(parenthesized_expression)
(interpolation)
(format_expression)
] @rainbow.scope
; Rainbow brackets
[
"(" ")"
"{" "}"
"[" "]"
] @rainbow.bracket

View file

@ -1,3 +1,23 @@
; Bracket matching pairs
("(" @open ")" @close)
("[" @open "]" @close)
("{" @open "}" @close)
; Rainbow bracket scopes
[
(character_class)
(anonymous_capturing_group)
(named_capturing_group)
(non_capturing_group)
(count_quantifier)
(character_class_escape)
] @rainbow.scope
; Rainbow brackets
[
"(?" "(?:"
"(?<" ">"
"(" ")"
"[" "]"
"{" "}"
] @rainbow.bracket

View file

@ -1,3 +1,4 @@
; Bracket matching pairs
("(" @open ")" @close)
("[" @open "]" @close)
("{" @open "}" @close)
@ -5,3 +6,66 @@
("\"" @open "\"" @close)
(closure_parameters "|" @open "|" @close)
("'" @open "'" @close)
; Rainbow bracket scopes
[
; {/}
(declaration_list)
(field_declaration_list)
(field_initializer_list)
(enum_variant_list)
(block)
(match_block)
(use_list)
(struct_pattern)
; (/)
(ordered_field_declaration_list)
(arguments)
(parameters)
(tuple_type)
(tuple_expression)
(tuple_pattern)
(tuple_struct_pattern)
(unit_type)
(unit_expression)
(visibility_modifier)
(parenthesized_expression)
(token_repetition_pattern)
; </>
(type_parameters)
(type_arguments)
(bracketed_type)
(for_lifetimes)
; [/]
(array_type)
(array_expression)
(index_expression)
(slice_pattern)
; attributes #[]
(attribute_item)
(inner_attribute_item)
; macros
(token_tree_pattern)
(macro_definition)
; closures
(closure_parameters)
] @rainbow.scope
; attributes like `#[serde(rename_all = "kebab-case")]`
(attribute arguments: (token_tree) @rainbow.scope)
; Rainbow brackets
[
"#"
"[" "]"
"(" ")"
"{" "}"
"<" ">"
"|"
] @rainbow.bracket

View file

@ -1,3 +1,4 @@
; Bracket matching pairs
("(" @open ")" @close)
("[" @open "]" @close)
("{" @open "}" @close)
@ -9,3 +10,29 @@
("`" @open "`" @close)
((jsx_element (jsx_opening_element) @open (jsx_closing_element) @close) (#set! newline.only))
; Rainbow bracket scopes
[
(object)
(array)
(arguments)
(formal_parameters)
(statement_block)
(parenthesized_expression)
(call_expression)
(type_parameters)
(type_arguments)
(jsx_element)
(jsx_self_closing_element)
] @rainbow.scope
; Rainbow brackets
[
"[" "]"
"{" "}"
"(" ")"
] @rainbow.bracket
; TypeScript generics (but not JSX tags)
(type_parameters ["<" ">"] @rainbow.bracket)
(type_arguments ["<" ">"] @rainbow.bracket)

View file

@ -1,3 +1,4 @@
; Bracket matching pairs
("(" @open ")" @close)
("[" @open "]" @close)
("{" @open "}" @close)
@ -5,3 +6,24 @@
("\"" @open "\"" @close)
("'" @open "'" @close)
("`" @open "`" @close)
; Rainbow bracket scopes
[
(object)
(array)
(arguments)
(formal_parameters)
(statement_block)
(parenthesized_expression)
(call_expression)
(type_parameters)
(type_arguments)
] @rainbow.scope
; Rainbow brackets
[
"[" "]"
"{" "}"
"(" ")"
"<" ">"
] @rainbow.bracket