Merge 99a44bac2b
into b1b60bb7fe
This commit is contained in:
commit
05e7b6e133
20 changed files with 1034 additions and 3 deletions
|
@ -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);
|
highlight_style.highlight(processed_highlight);
|
||||||
} else {
|
} else {
|
||||||
highlight_style = Some(processed_highlight);
|
highlight_style = Some(processed_highlight);
|
||||||
|
|
|
@ -35,6 +35,7 @@ mod mouse_context_menu;
|
||||||
pub mod movement;
|
pub mod movement;
|
||||||
mod persistence;
|
mod persistence;
|
||||||
mod proposed_changes_editor;
|
mod proposed_changes_editor;
|
||||||
|
mod rainbow_brackets;
|
||||||
mod rust_analyzer_ext;
|
mod rust_analyzer_ext;
|
||||||
pub mod scroll;
|
pub mod scroll;
|
||||||
mod selections_collection;
|
mod selections_collection;
|
||||||
|
|
|
@ -28,6 +28,7 @@ use crate::{
|
||||||
inlay_hint_settings,
|
inlay_hint_settings,
|
||||||
items::BufferSearchHighlights,
|
items::BufferSearchHighlights,
|
||||||
mouse_context_menu::{self, MenuPosition},
|
mouse_context_menu::{self, MenuPosition},
|
||||||
|
rainbow_brackets,
|
||||||
scroll::{ActiveScrollbarState, ScrollbarThumbState, scroll_amount::ScrollAmount},
|
scroll::{ActiveScrollbarState, ScrollbarThumbState, scroll_amount::ScrollAmount},
|
||||||
};
|
};
|
||||||
use buffer_diff::{DiffHunkStatus, DiffHunkStatusKind};
|
use buffer_diff::{DiffHunkStatus, DiffHunkStatusKind};
|
||||||
|
@ -9043,8 +9044,11 @@ impl Element for EditorElement {
|
||||||
diff_hunk_control_bounds,
|
diff_hunk_control_bounds,
|
||||||
});
|
});
|
||||||
|
|
||||||
self.editor.update(cx, |editor, _| {
|
self.editor.update(cx, |editor, cx| {
|
||||||
editor.last_position_map = Some(position_map.clone())
|
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 {
|
EditorLayout {
|
||||||
|
|
223
crates/editor/src/rainbow_brackets.rs
Normal file
223
crates/editor/src/rainbow_brackets.rs
Normal 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;
|
341
crates/editor/src/rainbow_brackets_tests.rs
Normal file
341
crates/editor/src/rainbow_brackets_tests.rs
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1104,6 +1104,7 @@ pub struct Grammar {
|
||||||
pub embedding_config: Option<EmbeddingConfig>,
|
pub embedding_config: Option<EmbeddingConfig>,
|
||||||
pub(crate) injection_config: Option<InjectionConfig>,
|
pub(crate) injection_config: Option<InjectionConfig>,
|
||||||
pub(crate) override_config: Option<OverrideConfig>,
|
pub(crate) override_config: Option<OverrideConfig>,
|
||||||
|
pub rainbow_config: Option<RainbowConfig>,
|
||||||
pub(crate) debug_variables_config: Option<DebugVariablesConfig>,
|
pub(crate) debug_variables_config: Option<DebugVariablesConfig>,
|
||||||
pub(crate) highlight_map: Mutex<HighlightMap>,
|
pub(crate) highlight_map: Mutex<HighlightMap>,
|
||||||
}
|
}
|
||||||
|
@ -1246,6 +1247,13 @@ struct BracketsPatternConfig {
|
||||||
newline_only: bool,
|
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 struct DebugVariablesConfig {
|
||||||
pub query: Query,
|
pub query: Query,
|
||||||
pub objects_by_capture_ix: Vec<(u32, DebuggerTextObject)>,
|
pub objects_by_capture_ix: Vec<(u32, DebuggerTextObject)>,
|
||||||
|
@ -1281,6 +1289,7 @@ impl Language {
|
||||||
override_config: None,
|
override_config: None,
|
||||||
redactions_config: None,
|
redactions_config: None,
|
||||||
runnable_config: None,
|
runnable_config: None,
|
||||||
|
rainbow_config: None,
|
||||||
error_query: Query::new(&ts_language, "(ERROR) @error").ok(),
|
error_query: Query::new(&ts_language, "(ERROR) @error").ok(),
|
||||||
debug_variables_config: None,
|
debug_variables_config: None,
|
||||||
ts_language,
|
ts_language,
|
||||||
|
@ -1358,6 +1367,7 @@ impl Language {
|
||||||
.with_text_object_query(query.as_ref())
|
.with_text_object_query(query.as_ref())
|
||||||
.context("Error loading textobject query")?;
|
.context("Error loading textobject query")?;
|
||||||
}
|
}
|
||||||
|
// Rainbow queries are now loaded as part of brackets.scm
|
||||||
if let Some(query) = queries.debugger {
|
if let Some(query) = queries.debugger {
|
||||||
self = self
|
self = self
|
||||||
.with_debug_variables_query(query.as_ref())
|
.with_debug_variables_query(query.as_ref())
|
||||||
|
@ -1499,16 +1509,24 @@ impl Language {
|
||||||
|
|
||||||
pub fn with_brackets_query(mut self, source: &str) -> Result<Self> {
|
pub fn with_brackets_query(mut self, source: &str) -> Result<Self> {
|
||||||
let grammar = self.grammar_mut().context("cannot mutate grammar")?;
|
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 query = Query::new(&grammar.ts_language, source)?;
|
||||||
let mut open_capture_ix = None;
|
let mut open_capture_ix = None;
|
||||||
let mut close_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(
|
get_capture_indices(
|
||||||
&query,
|
&query,
|
||||||
&mut [
|
&mut [
|
||||||
("open", &mut open_capture_ix),
|
("open", &mut open_capture_ix),
|
||||||
("close", &mut close_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())
|
let patterns = (0..query.pattern_count())
|
||||||
.map(|ix| {
|
.map(|ix| {
|
||||||
let mut config = BracketsPatternConfig::default();
|
let mut config = BracketsPatternConfig::default();
|
||||||
|
@ -1520,6 +1538,8 @@ impl Language {
|
||||||
config
|
config
|
||||||
})
|
})
|
||||||
.collect();
|
.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) {
|
if let Some((open_capture_ix, close_capture_ix)) = open_capture_ix.zip(close_capture_ix) {
|
||||||
grammar.brackets_config = Some(BracketsConfig {
|
grammar.brackets_config = Some(BracketsConfig {
|
||||||
query,
|
query,
|
||||||
|
@ -1528,6 +1548,28 @@ impl Language {
|
||||||
patterns,
|
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)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1730,6 +1772,38 @@ impl Language {
|
||||||
Ok(self)
|
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> {
|
fn grammar_mut(&mut self) -> Option<&mut Grammar> {
|
||||||
Arc::get_mut(self.grammar.as_mut()?)
|
Arc::get_mut(self.grammar.as_mut()?)
|
||||||
}
|
}
|
||||||
|
|
|
@ -232,6 +232,7 @@ pub const QUERY_FILENAME_PREFIXES: &[(
|
||||||
("runnables", |q| &mut q.runnables),
|
("runnables", |q| &mut q.runnables),
|
||||||
("debugger", |q| &mut q.debugger),
|
("debugger", |q| &mut q.debugger),
|
||||||
("textobjects", |q| &mut q.text_objects),
|
("textobjects", |q| &mut q.text_objects),
|
||||||
|
// Rainbow queries are now loaded as part of brackets.scm
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Tree-sitter language queries for a given language.
|
/// Tree-sitter language queries for a given language.
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
; Bracket matching pairs
|
||||||
("(" @open ")" @close)
|
("(" @open ")" @close)
|
||||||
("[" @open "]" @close)
|
("[" @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) (elif_clause ("elif" @close))) (#set! newline.only))
|
||||||
((if_statement ("then" @open) (else_clause ("else" @close))) (#set! newline.only))
|
((if_statement ("then" @open) (else_clause ("else" @close))) (#set! newline.only))
|
||||||
((if_statement ("then" @open "fi" @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
|
|
@ -1,5 +1,38 @@
|
||||||
|
; Bracket matching pairs
|
||||||
("(" @open ")" @close)
|
("(" @open ")" @close)
|
||||||
("[" @open "]" @close)
|
("[" @open "]" @close)
|
||||||
("{" @open "}" @close)
|
("{" @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
|
|
@ -1,5 +1,58 @@
|
||||||
|
; Bracket matching pairs
|
||||||
("(" @open ")" @close)
|
("(" @open ")" @close)
|
||||||
("[" @open "]" @close)
|
("[" @open "]" @close)
|
||||||
("{" @open "}" @close)
|
("{" @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
|
|
@ -1,5 +1,24 @@
|
||||||
|
; Bracket matching pairs
|
||||||
("(" @open ")" @close)
|
("(" @open ")" @close)
|
||||||
("[" @open "]" @close)
|
("[" @open "]" @close)
|
||||||
("{" @open "}" @close)
|
("{" @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
|
|
@ -1,6 +1,43 @@
|
||||||
|
; Bracket matching pairs
|
||||||
("(" @open ")" @close)
|
("(" @open ")" @close)
|
||||||
("[" @open "]" @close)
|
("[" @open "]" @close)
|
||||||
("{" @open "}" @close)
|
("{" @open "}" @close)
|
||||||
("\"" @open "\"" @close)
|
("\"" @open "\"" @close)
|
||||||
("`" @open "`" @close)
|
("`" @open "`" @close)
|
||||||
((rune_literal) @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
|
|
@ -1,3 +1,4 @@
|
||||||
|
; Bracket matching pairs
|
||||||
("(" @open ")" @close)
|
("(" @open ")" @close)
|
||||||
("[" @open "]" @close)
|
("[" @open "]" @close)
|
||||||
("{" @open "}" @close)
|
("{" @open "}" @close)
|
||||||
|
@ -7,3 +8,21 @@
|
||||||
("\"" @open "\"" @close)
|
("\"" @open "\"" @close)
|
||||||
("'" @open "'" @close)
|
("'" @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
|
|
@ -1,3 +1,16 @@
|
||||||
|
; Bracket matching pairs
|
||||||
("[" @open "]" @close)
|
("[" @open "]" @close)
|
||||||
("{" @open "}" @close)
|
("{" @open "}" @close)
|
||||||
("\"" @open "\"" @close)
|
("\"" @open "\"" @close)
|
||||||
|
|
||||||
|
; Rainbow bracket scopes
|
||||||
|
[
|
||||||
|
(object)
|
||||||
|
(array)
|
||||||
|
] @rainbow.scope
|
||||||
|
|
||||||
|
; Rainbow brackets
|
||||||
|
[
|
||||||
|
"[" "]"
|
||||||
|
"{" "}"
|
||||||
|
] @rainbow.bracket
|
|
@ -1,3 +1,16 @@
|
||||||
|
; Bracket matching pairs
|
||||||
("[" @open "]" @close)
|
("[" @open "]" @close)
|
||||||
("{" @open "}" @close)
|
("{" @open "}" @close)
|
||||||
("\"" @open "\"" @close)
|
("\"" @open "\"" @close)
|
||||||
|
|
||||||
|
; Rainbow bracket scopes
|
||||||
|
[
|
||||||
|
(object)
|
||||||
|
(array)
|
||||||
|
] @rainbow.scope
|
||||||
|
|
||||||
|
; Rainbow brackets
|
||||||
|
[
|
||||||
|
"[" "]"
|
||||||
|
"{" "}"
|
||||||
|
] @rainbow.bracket
|
|
@ -1,4 +1,36 @@
|
||||||
|
; Bracket matching pairs
|
||||||
("(" @open ")" @close)
|
("(" @open ")" @close)
|
||||||
("[" @open "]" @close)
|
("[" @open "]" @close)
|
||||||
("{" @open "}" @close)
|
("{" @open "}" @close)
|
||||||
((string_start) @open (string_end) @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
|
|
@ -1,3 +1,23 @@
|
||||||
|
; Bracket matching pairs
|
||||||
("(" @open ")" @close)
|
("(" @open ")" @close)
|
||||||
("[" @open "]" @close)
|
("[" @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
|
|
@ -1,3 +1,4 @@
|
||||||
|
; Bracket matching pairs
|
||||||
("(" @open ")" @close)
|
("(" @open ")" @close)
|
||||||
("[" @open "]" @close)
|
("[" @open "]" @close)
|
||||||
("{" @open "}" @close)
|
("{" @open "}" @close)
|
||||||
|
@ -5,3 +6,66 @@
|
||||||
("\"" @open "\"" @close)
|
("\"" @open "\"" @close)
|
||||||
(closure_parameters "|" @open "|" @close)
|
(closure_parameters "|" @open "|" @close)
|
||||||
("'" @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
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
; Bracket matching pairs
|
||||||
("(" @open ")" @close)
|
("(" @open ")" @close)
|
||||||
("[" @open "]" @close)
|
("[" @open "]" @close)
|
||||||
("{" @open "}" @close)
|
("{" @open "}" @close)
|
||||||
|
@ -9,3 +10,29 @@
|
||||||
("`" @open "`" @close)
|
("`" @open "`" @close)
|
||||||
|
|
||||||
((jsx_element (jsx_opening_element) @open (jsx_closing_element) @close) (#set! newline.only))
|
((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)
|
|
@ -1,3 +1,4 @@
|
||||||
|
; Bracket matching pairs
|
||||||
("(" @open ")" @close)
|
("(" @open ")" @close)
|
||||||
("[" @open "]" @close)
|
("[" @open "]" @close)
|
||||||
("{" @open "}" @close)
|
("{" @open "}" @close)
|
||||||
|
@ -5,3 +6,24 @@
|
||||||
("\"" @open "\"" @close)
|
("\"" @open "\"" @close)
|
||||||
("'" @open "'" @close)
|
("'" @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
|
Loading…
Add table
Add a link
Reference in a new issue