diff --git a/Cargo.lock b/Cargo.lock index e00df6c0d0675eb55543abd409d844ec3474a60d..a8e555b42362bfa89a8cc308363fcd4df5d448bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1732,6 +1732,7 @@ dependencies = [ "regex", "smol", "theme", + "unindent", "workspace", ] diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 146985bcc23ee6b48cda44096c4025d591fdea4e..5b04e129c4c59844a0127c10daab0dae42be55bc 100644 --- a/crates/editor/src/editor.rs +++ b/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, + ) -> Vec<(Range, 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, display_snapshot: &DisplaySnapshot, - ) -> Vec<(Color, Range)> { + ) -> Vec<(Range, 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(), + )] ); }); } diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index ecff2b93d1c23729db867ba57e4ff3ebec0a9f47..8ef66568970ac5037711d258a8b502352054f62c 100644 --- a/crates/editor/src/element.rs +++ b/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)>, + highlighted_ranges: Vec<(Range, Color)>, selections: HashMap>>, text_offset: Vector2F, } diff --git a/crates/find/Cargo.toml b/crates/find/Cargo.toml index eb3d99ce346a2553d6f15f0b7eeadd02353676ca..acab695d12c17c3bd6501ac64adf9a4f2f718daf 100644 --- a/crates/find/Cargo.toml +++ b/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" diff --git a/crates/find/src/find.rs b/crates/find/src/find.rs index f57c531987fa8742ae95ffb7b41cd86960863807..db2d74a0a9b64cdbcbfbac135ad03ac137e4e2c4 100644 --- a/crates/find/src/find.rs +++ b/crates/find/src/find.rs @@ -148,6 +148,16 @@ impl FindBar { } } + #[cfg(test)] + fn set_query(&mut self, query: &str, cx: &mut ViewContext) { + 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(), + ), + ] + ); + }); + } +}