Add a basic visual test

Kirill Bulatov created

Change summary

crates/editor/src/bracket_colorization.rs | 154 ++++++++++++++++++++++++
crates/editor/src/display_map.rs          |   3 
2 files changed, 154 insertions(+), 3 deletions(-)

Detailed changes

crates/editor/src/bracket_colorization.rs 🔗

@@ -107,26 +107,174 @@ impl Editor {
 
 #[cfg(test)]
 mod tests {
-    use std::{collections::HashSet, sync::Arc, time::Duration};
+    use std::{cmp, sync::Arc, time::Duration};
 
     use super::*;
     use crate::{
         DisplayPoint,
         display_map::{DisplayRow, ToDisplayPoint},
         editor_tests::init_test,
-        test::editor_lsp_test_context::EditorLspTestContext,
+        test::{
+            editor_lsp_test_context::EditorLspTestContext, editor_test_context::EditorTestContext,
+        },
     };
+    use collections::HashSet;
     use indoc::indoc;
+    use itertools::Itertools;
     use languages::rust_lang;
+    use pretty_assertions::assert_eq;
     use rope::Point;
     use text::OffsetRangeExt;
+    use util::post_inc;
 
     #[gpui::test]
-    async fn test_rainbow_bracket_highlights(cx: &mut gpui::TestAppContext) {
+    async fn test_basic_bracket_colorization(cx: &mut gpui::TestAppContext) {
         init_test(cx, |language_settings| {
             language_settings.defaults.colorize_brackets = Some(true);
         });
+        let mut cx = EditorLspTestContext::new(
+            Arc::into_inner(rust_lang()).unwrap(),
+            lsp::ServerCapabilities::default(),
+            cx,
+        )
+        .await;
+
+        cx.set_state(indoc! {r#"ˇuse std::{collections::HashMap, future::Future};
+
+fn main() {
+    let a = one((), { () }, ());
+    println!("{a}");
+    println!("{a}");
+    for i in 0..a {
+        println!("{i}");
+    }
+
+    let b = {
+        {
+            {
+                [([([([([([([([([([((), ())])])])])])])])])])]
+            }
+        }
+    };
+}
+
+#[rustfmt::skip]
+fn one(a: (), (): (), c: ()) -> usize { 1 }
 
+fn two<T>(a: HashMap<String, Vec<Option<T>>>) -> usize
+where
+    T: Future<Output = HashMap<String, Vec<Option<Box<()>>>>>,
+{
+    2
+}
+"#});
+        cx.executor().advance_clock(Duration::from_millis(100));
+        cx.executor().run_until_parked();
+
+        assert_bracket_colors(
+            r#"use std::«1{collections::HashMap, future::Future}1»;
+
+fn main«1()1» «1{
+    let a = one«2(«3()3», «3{ «4()4» }3», «3()3»)2»;
+    println!«2("{a}")2»;
+    println!«2("{a}")2»;
+    for i in 0..a «2{
+        println!«3("{i}")3»;
+    }2»
+
+    let b = «2{
+        «3{
+            «4{
+                «5[«6(«7[«1(«2[«3(«4[«5(«6[«7(«1[«2(«3[«4(«5[«6(«7[«1(«2[«3(«4()4», «4()4»)3»]2»)1»]7»)6»]5»)4»]3»)2»]1»)7»]6»)5»]4»)3»]2»)1»]7»)6»]5»
+            }4»
+        }3»
+    }2»;
+}1»
+
+#«1[rustfmt::skip]1»
+fn one«1(a: «2()2», «2()2»: «2()2», c: «2()2»)1» -> usize «1{ 1 }1»
+
+fn two«1<T>1»«1(a: HashMap«2<String, Vec«3<Option«4<T>4»>3»>2»)1» -> usize
+where
+    T: Future«1<Output = HashMap«2<String, Vec«3<Option«4<Box«5<«6()6»>5»>4»>3»>2»>1»,
+«1{
+    2
+}1»
+
+1 hsla(207.80, 16.20%, 69.19%, 1.00)
+2 hsla(29.00, 54.00%, 65.88%, 1.00)
+3 hsla(286.00, 51.00%, 75.25%, 1.00)
+4 hsla(187.00, 47.00%, 59.22%, 1.00)
+5 hsla(355.00, 65.00%, 75.94%, 1.00)
+6 hsla(95.00, 38.00%, 62.00%, 1.00)
+7 hsla(39.00, 67.00%, 69.00%, 1.00)
+"#,
+            &mut cx,
+        );
+    }
+
+    #[track_caller]
+    fn assert_bracket_colors(expected_markup: &str, cx: &mut EditorTestContext) {
+        let result = cx.update_editor(|editor, window, cx| {
+            let snapshot = editor.snapshot(window, cx);
+            let actual_ranges = snapshot.all_text_highlight_ranges::<RainbowBracketHighlight>();
+            let editor_text = snapshot.text();
+
+            let mut next_index = 1;
+            let mut color_to_index = HashMap::default();
+            let mut annotations = Vec::new();
+            for (color, range) in &actual_ranges {
+                let color_index = *color_to_index
+                    .entry(*color)
+                    .or_insert_with(|| post_inc(&mut next_index));
+                let start_offset = snapshot.buffer_snapshot().point_to_offset(range.start);
+                let end_offset = snapshot.buffer_snapshot().point_to_offset(range.end);
+                let bracket_text = &editor_text[start_offset..end_offset];
+                let bracket_char = bracket_text.chars().next().unwrap();
+
+                if matches!(bracket_char, '{' | '[' | '(' | '<') {
+                    annotations.push((start_offset, format!("«{color_index}")));
+                } else {
+                    annotations.push((end_offset, format!("{color_index}»")));
+                }
+            }
+
+            annotations.sort_by(|(pos_a, text_a), (pos_b, text_b)| {
+                pos_a.cmp(pos_b).reverse().then_with(|| {
+                    let a_is_opening = text_a.starts_with('«');
+                    let b_is_opening = text_b.starts_with('«');
+                    match (a_is_opening, b_is_opening) {
+                        (true, false) => cmp::Ordering::Less,
+                        (false, true) => cmp::Ordering::Greater,
+                        _ => cmp::Ordering::Equal,
+                    }
+                })
+            });
+
+            let mut text_with_annotations = editor_text;
+            for (pos, text) in annotations {
+                text_with_annotations.insert_str(pos, &text);
+            }
+
+            text_with_annotations.push_str("\n");
+            for (index, color) in color_to_index
+                .iter()
+                .map(|(color, index)| (*index, *color))
+                .sorted_by_key(|(index, _)| *index)
+            {
+                text_with_annotations.push_str(&format!("{index} {color}\n"));
+            }
+
+            text_with_annotations
+        });
+        assert_eq!(expected_markup, result);
+    }
+
+    #[gpui::test]
+    async fn test_rainbow_bracket_highlights(cx: &mut gpui::TestAppContext) {
+        init_test(cx, |language_settings| {
+            language_settings.defaults.colorize_brackets = Some(true);
+        });
         let mut cx = EditorLspTestContext::new(
             Arc::into_inner(rust_lang()).unwrap(),
             lsp::ServerCapabilities::default(),

crates/editor/src/display_map.rs 🔗

@@ -1412,6 +1412,8 @@ impl DisplaySnapshot {
 
     #[cfg(any(test, feature = "test-support"))]
     pub fn all_text_highlight_ranges<Tag: ?Sized + 'static>(&self) -> Vec<(Hsla, Range<Point>)> {
+        use itertools::Itertools;
+
         let needed_type_id = TypeId::of::<Tag>();
         self.text_highlights
             .iter()
@@ -1429,6 +1431,7 @@ impl DisplaySnapshot {
                     })
                     .collect::<Vec<_>>()
             })
+            .sorted_by_key(|(_, range)| range.start)
             .collect()
     }