Add a simple unit test for find bar

Max Brunsfeld created

Change summary

Cargo.lock                   |   1 
crates/editor/src/editor.rs  |  28 +++++--
crates/editor/src/element.rs |   4 
crates/find/Cargo.toml       |   6 +
crates/find/src/find.rs      | 150 ++++++++++++++++++++++++++++++++++++++
5 files changed, 179 insertions(+), 10 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -1732,6 +1732,7 @@ dependencies = [
  "regex",
  "smol",
  "theme",
+ "unindent",
  "workspace",
 ]
 

crates/editor/src/editor.rs 🔗

@@ -3750,11 +3750,23 @@ impl Editor {
         cx.notify();
     }
 
+    #[cfg(feature = "test-support")]
+    pub fn highlighted_ranges(
+        &mut self,
+        cx: &mut ViewContext<Self>,
+    ) -> Vec<(Range<DisplayPoint>, Color)> {
+        let snapshot = self.snapshot(cx);
+        let buffer = &snapshot.buffer_snapshot;
+        let start = buffer.anchor_before(0);
+        let end = buffer.anchor_after(buffer.len());
+        self.highlighted_ranges_in_range(start..end, &snapshot)
+    }
+
     pub fn highlighted_ranges_in_range(
         &self,
         search_range: Range<Anchor>,
         display_snapshot: &DisplaySnapshot,
-    ) -> Vec<(Color, Range<DisplayPoint>)> {
+    ) -> Vec<(Range<DisplayPoint>, Color)> {
         let mut results = Vec::new();
         let buffer = &display_snapshot.buffer_snapshot;
         for (color, ranges) in self.highlighted_ranges.values() {
@@ -3780,7 +3792,7 @@ impl Editor {
                     .end
                     .to_point(buffer)
                     .to_display_point(display_snapshot);
-                results.push((*color, start..end))
+                results.push((start..end, *color))
             }
         }
         results
@@ -6679,20 +6691,20 @@ mod tests {
                 ),
                 &[
                     (
-                        Color::red(),
                         DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
+                        Color::red(),
                     ),
                     (
-                        Color::red(),
                         DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
+                        Color::red(),
                     ),
                     (
+                        DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
                         Color::green(),
-                        DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5)
                     ),
                     (
+                        DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
                         Color::green(),
-                        DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6)
                     ),
                 ]
             );
@@ -6702,9 +6714,9 @@ mod tests {
                     &snapshot,
                 ),
                 &[(
-                    Color::red(),
                     DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
-                ),]
+                    Color::red(),
+                )]
             );
         });
     }

crates/editor/src/element.rs 🔗

@@ -316,7 +316,7 @@ impl EditorElement {
 
         cx.scene.push_layer(Some(bounds));
 
-        for (color, range) in &layout.highlighted_ranges {
+        for (range, color) in &layout.highlighted_ranges {
             self.paint_highlighted_range(
                 range.clone(),
                 start_row,
@@ -997,7 +997,7 @@ pub struct LayoutState {
     line_height: f32,
     em_width: f32,
     em_advance: f32,
-    highlighted_ranges: Vec<(Color, Range<DisplayPoint>)>,
+    highlighted_ranges: Vec<(Range<DisplayPoint>, Color)>,
     selections: HashMap<ReplicaId, Vec<text::Selection<DisplayPoint>>>,
     text_offset: Vector2F,
 }

crates/find/Cargo.toml 🔗

@@ -17,3 +17,9 @@ anyhow = "1.0"
 postage = { version = "0.4.1", features = ["futures-traits"] }
 regex = "1.5"
 smol = { version = "1.2" }
+
+[dev-dependencies]
+editor = { path = "../editor", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
+workspace = { path = "../workspace", features = ["test-support"] }
+unindent = "0.1"

crates/find/src/find.rs 🔗

@@ -148,6 +148,16 @@ impl FindBar {
         }
     }
 
+    #[cfg(test)]
+    fn set_query(&mut self, query: &str, cx: &mut ViewContext<Self>) {
+        self.query_editor.update(cx, |query_editor, cx| {
+            query_editor.buffer().update(cx, |query_buffer, cx| {
+                let len = query_buffer.read(cx).len();
+                query_buffer.edit([0..len], query, cx);
+            });
+        });
+    }
+
     fn render_mode_button(
         &self,
         icon: &str,
@@ -397,3 +407,143 @@ async fn regex_search(
 
     Ok(ranges)
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use editor::{DisplayPoint, Editor, EditorSettings, MultiBuffer};
+    use gpui::{color::Color, TestAppContext};
+    use std::sync::Arc;
+    use unindent::Unindent as _;
+
+    #[gpui::test]
+    async fn test_find_simple(mut cx: TestAppContext) {
+        let fonts = cx.font_cache();
+        let mut theme = gpui::fonts::with_font_cache(fonts.clone(), || theme::Theme::default());
+        theme.find.match_background = Color::red();
+        let settings = Settings::new("Courier", &fonts, Arc::new(theme)).unwrap();
+
+        let buffer = cx.update(|cx| {
+            MultiBuffer::build_simple(
+                &r#"
+                A regular expression (shortened as regex or regexp;[1] also referred to as
+                rational expression[2][3]) is a sequence of characters that specifies a search
+                pattern in text. Usually such patterns are used by string-searching algorithms
+                for "find" or "find and replace" operations on strings, or for input validation.
+                "#
+                .unindent(),
+                cx,
+            )
+        });
+        let editor = cx.add_view(Default::default(), |cx| {
+            Editor::new(buffer.clone(), Arc::new(EditorSettings::test), cx)
+        });
+
+        let find_bar = cx.add_view(Default::default(), |cx| {
+            let mut find_bar = FindBar::new(watch::channel_with(settings).1, cx);
+            find_bar.active_item_changed(Some(Box::new(editor.clone())), cx);
+            find_bar
+        });
+
+        // default: case-insensitive substring search.
+        find_bar.update(&mut cx, |find_bar, cx| {
+            find_bar.set_query("us", cx);
+        });
+        editor.next_notification(&cx).await;
+        editor.update(&mut cx, |editor, cx| {
+            assert_eq!(
+                editor.highlighted_ranges(cx),
+                &[
+                    (
+                        DisplayPoint::new(2, 17)..DisplayPoint::new(2, 19),
+                        Color::red(),
+                    ),
+                    (
+                        DisplayPoint::new(2, 43)..DisplayPoint::new(2, 45),
+                        Color::red(),
+                    ),
+                ]
+            );
+        });
+
+        // switch to case sensitive search
+        find_bar.update(&mut cx, |find_bar, cx| {
+            find_bar.toggle_mode(&ToggleMode(SearchMode::CaseSensitive), cx);
+        });
+        editor.next_notification(&cx).await;
+        editor.update(&mut cx, |editor, cx| {
+            assert_eq!(
+                editor.highlighted_ranges(cx),
+                &[(
+                    DisplayPoint::new(2, 43)..DisplayPoint::new(2, 45),
+                    Color::red(),
+                ),]
+            );
+        });
+
+        find_bar.update(&mut cx, |find_bar, cx| {
+            find_bar.set_query("or", cx);
+        });
+        editor.next_notification(&cx).await;
+        editor.update(&mut cx, |editor, cx| {
+            assert_eq!(
+                editor.highlighted_ranges(cx),
+                &[
+                    (
+                        DisplayPoint::new(0, 24)..DisplayPoint::new(0, 26),
+                        Color::red(),
+                    ),
+                    (
+                        DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43),
+                        Color::red(),
+                    ),
+                    (
+                        DisplayPoint::new(2, 71)..DisplayPoint::new(2, 73),
+                        Color::red(),
+                    ),
+                    (
+                        DisplayPoint::new(3, 1)..DisplayPoint::new(3, 3),
+                        Color::red(),
+                    ),
+                    (
+                        DisplayPoint::new(3, 11)..DisplayPoint::new(3, 13),
+                        Color::red(),
+                    ),
+                    (
+                        DisplayPoint::new(3, 56)..DisplayPoint::new(3, 58),
+                        Color::red(),
+                    ),
+                    (
+                        DisplayPoint::new(3, 60)..DisplayPoint::new(3, 62),
+                        Color::red(),
+                    ),
+                ]
+            );
+        });
+
+        // switch to whole word search
+        find_bar.update(&mut cx, |find_bar, cx| {
+            find_bar.toggle_mode(&ToggleMode(SearchMode::WholeWord), cx);
+        });
+        editor.next_notification(&cx).await;
+        editor.update(&mut cx, |editor, cx| {
+            assert_eq!(
+                editor.highlighted_ranges(cx),
+                &[
+                    (
+                        DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43),
+                        Color::red(),
+                    ),
+                    (
+                        DisplayPoint::new(3, 11)..DisplayPoint::new(3, 13),
+                        Color::red(),
+                    ),
+                    (
+                        DisplayPoint::new(3, 56)..DisplayPoint::new(3, 58),
+                        Color::red(),
+                    ),
+                ]
+            );
+        });
+    }
+}