Fix previous bracket coloring overwriting

Kirill Bulatov created

Change summary

crates/editor/src/bracket_colorization.rs | 23 ++++++++++++--------
crates/language/src/buffer.rs             | 28 +++++++++++++++++-------
crates/language/src/buffer/row_chunk.rs   |  8 +++++++
3 files changed, 42 insertions(+), 17 deletions(-)

Detailed changes

crates/editor/src/bracket_colorization.rs 🔗

@@ -16,8 +16,9 @@ impl Editor {
             self.fetched_tree_sitter_chunks.clear();
         }
 
+        let accents_count = cx.theme().accents().0.len();
         let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
-        let bracket_matches = self.visible_excerpts(cx).into_iter().fold(
+        let bracket_matches_by_accent = self.visible_excerpts(cx).into_iter().fold(
             HashMap::default(),
             |mut acc, (excerpt_id, (buffer, buffer_version, buffer_range))| {
                 let buffer_snapshot = buffer.read(cx).snapshot();
@@ -33,7 +34,7 @@ impl Editor {
                         .entry(excerpt_id)
                         .or_default();
 
-                    for (depth, open_range, close_range) in buffer_snapshot
+                    let brackets_by_accent = buffer_snapshot
                         .fetch_bracket_ranges(
                             buffer_range.start..buffer_range.end,
                             Some((&buffer_version, fetched_chunks)),
@@ -61,14 +62,18 @@ impl Editor {
                                 .anchor_in_excerpt(excerpt_id, buffer_close_range.start)?
                                 ..multi_buffer_snapshot
                                     .anchor_in_excerpt(excerpt_id, buffer_close_range.end)?;
+
+                            let accent_number = pair.id % accents_count;
+
                             Some((
-                                pair.depth,
+                                accent_number,
                                 multi_buffer_open_range,
                                 multi_buffer_close_range,
                             ))
-                        })
-                    {
-                        let ranges = acc.entry(depth).or_insert_with(Vec::new);
+                        });
+
+                    for (accent_number, open_range, close_range) in brackets_by_accent {
+                        let ranges = acc.entry(accent_number).or_insert_with(Vec::new);
                         ranges.push(open_range);
                         ranges.push(close_range);
                     }
@@ -83,8 +88,8 @@ impl Editor {
         }
 
         let editor_background = cx.theme().colors().editor_background;
-        for (depth, bracket_highlights) in bracket_matches {
-            let bracket_color = cx.theme().accents().color_for_index(depth as u32);
+        for (accent_number, bracket_highlights) in bracket_matches_by_accent {
+            let bracket_color = cx.theme().accents().color_for_index(accent_number as u32);
             let adjusted_color = ensure_minimum_contrast(bracket_color, editor_background, 55.0);
             let style = HighlightStyle {
                 color: Some(adjusted_color),
@@ -92,7 +97,7 @@ impl Editor {
             };
 
             self.highlight_text_key::<RainbowBracketHighlight>(
-                depth,
+                accent_number,
                 bracket_highlights,
                 style,
                 true,

crates/language/src/buffer.rs 🔗

@@ -67,7 +67,7 @@ pub use text::{
 use theme::{ActiveTheme as _, SyntaxTheme};
 #[cfg(any(test, feature = "test-support"))]
 use util::RandomCharIter;
-use util::{RangeExt, debug_panic, maybe, paths::PathStyle, rel_path::RelPath};
+use util::{RangeExt, debug_panic, maybe, paths::PathStyle, post_inc, rel_path::RelPath};
 
 #[cfg(any(test, feature = "test-support"))]
 pub use {tree_sitter_python, tree_sitter_rust, tree_sitter_typescript};
@@ -850,7 +850,7 @@ pub struct BracketMatch {
     pub open_range: Range<usize>,
     pub close_range: Range<usize>,
     pub newline_only: bool,
-    pub depth: usize,
+    pub id: usize,
 }
 
 impl BracketMatch {
@@ -4191,6 +4191,23 @@ impl BufferSnapshot {
             let bracket_matches = match tree_sitter_data.brackets_by_chunks[chunk.id].take() {
                 Some(cached_brackets) => cached_brackets,
                 None => {
+                    // Sequential IDs are needed to determine the color of the bracket pair.
+                    let mut next_id = match tree_sitter_data.chunks.previous_chunk(chunk) {
+                        Some(previous_chunk) => tree_sitter_data.brackets_by_chunks
+                            [previous_chunk.id]
+                            .as_ref()
+                            .and_then(|previous_brackets| previous_brackets.last())
+                            // Try to continue previous sequence of IDs.
+                            .map(|bracket| bracket.id + 1)
+                            // If not possible, start another sequence: pick it far enough to avoid overlaps.
+                            //
+                            // This for sure will introduce the gaps between chunks' bracket IDs,
+                            // but this will only potentially skip `mod(accents_number)` colors between chunks.
+                            .unwrap_or_else(|| {
+                                (usize::MAX / tree_sitter_data.chunks.len()) * chunk.id + 1
+                            }),
+                        None => 0,
+                    };
                     let mut matches =
                         self.syntax
                             .matches(chunk_range.clone(), &self.text, |grammar| {
@@ -4202,9 +4219,6 @@ impl BufferSnapshot {
                         .map(|grammar| grammar.brackets_config.as_ref().unwrap())
                         .collect::<Vec<_>>();
 
-                    // todo! this seems like a wrong parameter: add bracket_id that will be used for each bracket
-                    // this will require changing `depth` treatment during style application, we'll need to group brackets by their hsla
-                    let mut depth = 0;
                     let chunk_range = chunk_range.clone();
                     let new_matches = iter::from_fn(move || {
                         while let Some(mat) = matches.peek() {
@@ -4231,13 +4245,11 @@ impl BufferSnapshot {
                                 continue;
                             }
 
-                            depth += 1;
-
                             return Some(BracketMatch {
                                 open_range,
                                 close_range,
                                 newline_only: pattern.newline_only,
-                                depth,
+                                id: post_inc(&mut next_id),
                             });
                         }
                         None

crates/language/src/buffer/row_chunk.rs 🔗

@@ -97,6 +97,14 @@ impl RowChunks {
         };
         Some(self.snapshot.anchor_before(start)..self.snapshot.anchor_after(end))
     }
+
+    pub fn previous_chunk(&self, chunk: RowChunk) -> Option<RowChunk> {
+        if chunk.id == 0 {
+            None
+        } else {
+            self.chunks.get(chunk.id - 1).copied()
+        }
+    }
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]