Fix the tests

Kirill Bulatov created

Change summary

crates/multi_buffer/src/path_key.rs |  74 ------------
crates/search/src/project_search.rs | 180 ++++++++++--------------------
2 files changed, 65 insertions(+), 189 deletions(-)

Detailed changes

crates/multi_buffer/src/path_key.rs 🔗

@@ -100,7 +100,7 @@ impl MultiBuffer {
 
         let merged = Self::merge_excerpt_ranges(&excerpt_ranges);
         let (inserted, _path_key_index) =
-            let excerpt_insertion_result = self.set_merged_excerpt_ranges_for_path(path, buffer, &buffer_snapshot, merged, cx);
+            self.set_merged_excerpt_ranges_for_path(path, buffer, &buffer_snapshot, merged, cx);
         inserted
     }
 
@@ -367,77 +367,7 @@ impl MultiBuffer {
         index
     }
 
-    /// Expand each new merged range to encompass any overlapping existing
-    /// excerpt, then re-merge. This turns "partial overlap where the union
-    /// equals the existing range" into an exact match, avoiding unnecessary
-    /// remove+insert churn that floods the wrap map with edits.
-    fn expand_new_ranges_to_existing(
-        &self,
-        path: &PathKey,
-        buffer_snapshot: &BufferSnapshot,
-        mut new: Vec<ExcerptRange<Point>>,
-        counts: Vec<usize>,
-        cx: &App,
-    ) -> (Vec<ExcerptRange<Point>>, Vec<usize>) {
-        let existing = self.excerpts_by_path.get(path).cloned().unwrap_or_default();
-        if existing.is_empty() || new.is_empty() {
-            return (new, counts);
-        }
-
-        let snapshot = self.snapshot(cx);
-        let buffer_id = buffer_snapshot.remote_id();
-        let existing_ranges: Vec<Range<Point>> = existing
-            .iter()
-            .filter_map(|&id| {
-                let excerpt = snapshot.excerpt(id)?;
-                (excerpt.buffer_id == buffer_id)
-                    .then(|| excerpt.range.context.to_point(buffer_snapshot))
-            })
-            .collect();
-
-        let mut changed = false;
-        for new_range in &mut new {
-            for existing_range in &existing_ranges {
-                if new_range.context.start <= existing_range.end
-                    && new_range.context.end >= existing_range.start
-                {
-                    let expanded_start = new_range.context.start.min(existing_range.start);
-                    let expanded_end = new_range.context.end.max(existing_range.end);
-                    if expanded_start != new_range.context.start
-                        || expanded_end != new_range.context.end
-                    {
-                        new_range.context.start = expanded_start;
-                        new_range.context.end = expanded_end;
-                        changed = true;
-                    }
-                }
-            }
-        }
-
-        if !changed {
-            return (new, counts);
-        }
-
-        let mut result_ranges: Vec<ExcerptRange<Point>> = Vec::new();
-        let mut result_counts: Vec<usize> = Vec::new();
-        for (range, count) in new.into_iter().zip(counts) {
-            if let Some(last) = result_ranges.last_mut() {
-                if last.context.end >= range.context.start
-                    || last.context.end.row + 1 == range.context.start.row
-                {
-                    last.context.end = last.context.end.max(range.context.end);
-                    *result_counts.last_mut().unwrap() += count;
-                    continue;
-                }
-            }
-            result_ranges.push(range);
-            result_counts.push(count);
-        }
-
-        (result_ranges, result_counts)
-    }
-
-    fn update_path_excerpts(
+    pub fn update_path_excerpts(
         &mut self,
         path_key: PathKey,
         buffer: Entity<Buffer>,

crates/search/src/project_search.rs 🔗

@@ -326,9 +326,10 @@ impl ProjectSearch {
                 no_results: self.no_results,
                 limit_reached: self.limit_reached,
                 search_input_confirmed: self.search_input_confirmed,
-            search_history_cursor: self.search_history_cursor.clone(),
-            search_included_history_cursor: self.search_included_history_cursor.clone(),
-            search_excluded_history_cursor: self.search_excluded_history_cursor.clone(),_excerpts_subscription: subscription,
+                search_history_cursor: self.search_history_cursor.clone(),
+                search_included_history_cursor: self.search_included_history_cursor.clone(),
+                search_excluded_history_cursor: self.search_excluded_history_cursor.clone(),
+                _excerpts_subscription: subscription,
             }
         })
     }
@@ -536,16 +537,17 @@ impl ProjectSearch {
                                 .excerpts
                                 .update(cx, |excerpts, cx| excerpts.clear(cx));
                         } else {
-                            project_search.excerpts.update(cx, |excerpts, cx| {
-                                let stale = excerpts
-                                    .paths()
-                                    .filter(|path| !seen_paths.contains(*path))
-                                    .cloned()
-                                    .collect::<Vec<_>>();
-                                for path in stale {
-                                    excerpts.remove_excerpts_for_path(path, cx);
-                                }
-                            });
+                            // TODO kb
+                            // project_search.excerpts.update(cx, |excerpts, cx| {
+                            //     let stale = excerpts
+                            //         .paths()
+                            //         .filter(|path| !seen_paths.contains(*path))
+                            //         .cloned()
+                            //         .collect::<Vec<_>>();
+                            //     for path in stale {
+                            //         excerpts.remove_excerpts_for_path(path, cx);
+                            //     }
+                            // });
                         }
                         project_search.no_results = Some(project_search.match_ranges.is_empty());
                         project_search.limit_reached = limit_reached;
@@ -2800,41 +2802,32 @@ pub mod tests {
     use util_macros::perf;
     use workspace::{DeploySearch, MultiWorkspace};
 
-    #[derive(Debug, Clone, PartialEq, Eq)]
-    enum ExcerptEvent {
-        Added { excerpts: usize },
-        Removed { ids: usize },
-        Edited,
+    fn take_updated_files(events: &RefCell<Vec<MultiBufferEvent>>) -> Vec<String> {
+        let mut files: Vec<String> = Vec::new();
+        for event in events.borrow_mut().drain(..) {
+            if let MultiBufferEvent::BufferRangesUpdated { path_key, .. } = event {
+                let name = path_key.path.as_unix_str().to_owned();
+                if !files.contains(&name) {
+                    files.push(name);
+                }
+            }
+        }
+        files.sort();
+        files
     }
 
     fn subscribe_to_excerpt_events(
         search: &Entity<ProjectSearch>,
         cx: &mut TestAppContext,
-    ) -> (Rc<RefCell<Vec<ExcerptEvent>>>, Subscription) {
-        let events: Rc<RefCell<Vec<ExcerptEvent>>> = Rc::default();
+    ) -> (Rc<RefCell<Vec<MultiBufferEvent>>>, Subscription) {
+        let events: Rc<RefCell<Vec<MultiBufferEvent>>> = Rc::default();
         let excerpts = cx.update(|cx| search.read(cx).excerpts.clone());
         let subscription = cx.update({
             let events = events.clone();
             |cx| {
-                cx.subscribe(
-                    &excerpts,
-                    move |_, event: &MultiBufferEvent, _| match event {
-                        MultiBufferEvent::ExcerptsAdded { excerpts, .. } => {
-                            events.borrow_mut().push(ExcerptEvent::Added {
-                                excerpts: excerpts.len(),
-                            });
-                        }
-                        MultiBufferEvent::ExcerptsRemoved { ids, .. } => {
-                            events
-                                .borrow_mut()
-                                .push(ExcerptEvent::Removed { ids: ids.len() });
-                        }
-                        MultiBufferEvent::Edited { .. } => {
-                            events.borrow_mut().push(ExcerptEvent::Edited);
-                        }
-                        _ => {}
-                    },
-                )
+                cx.subscribe(&excerpts, move |_, event: &MultiBufferEvent, _| {
+                    events.borrow_mut().push(event.clone());
+                })
             }
         });
         (events, subscription)
@@ -2846,6 +2839,7 @@ pub mod tests {
             cx.set_global(settings);
 
             theme::init(theme::LoadThemes::JustBase, cx);
+            theme_settings::init(theme::LoadThemes::JustBase, cx);
 
             editor::init(cx);
             crate::init(cx);
@@ -5771,130 +5765,79 @@ pub mod tests {
             ProjectSearchView::new(workspace.downgrade(), search.clone(), window, cx, None)
         });
         let (events, _subscription) = subscribe_to_excerpt_events(&search, cx);
-        let take_excerpt_changes = || -> Vec<ExcerptEvent> {
-            events
-                .borrow_mut()
-                .drain(..)
-                .filter(|e| !matches!(e, ExcerptEvent::Edited))
-                .collect()
-        };
+        let all_rs_files = vec!["four.rs", "one.rs", "only_one.rs", "three.rs", "two.rs"];
         let expected_one_matches = vec![
             "one", "ONE", "ONE", "ONE", "ONE", "one", "ONE", "one", "ONE", "one", "ONE",
         ];
 
-        // Initial non-incremental search for "ONE" — clears then inserts one excerpt per file.
+        // Initial non-incremental search for "ONE" — inserts one excerpt per file.
         perform_search(search_view, "ONE", cx);
         assert_eq!(read_match_texts(search_view, cx), expected_one_matches);
         assert_all_highlights_match_query(search_view, "ONE", cx);
-        assert_eq!(
-            take_excerpt_changes(),
-            vec![
-                ExcerptEvent::Removed { ids: 0 },
-                ExcerptEvent::Added { excerpts: 1 },
-                ExcerptEvent::Added { excerpts: 1 },
-                ExcerptEvent::Added { excerpts: 1 },
-                ExcerptEvent::Added { excerpts: 1 },
-                ExcerptEvent::Added { excerpts: 1 },
-            ]
-        );
+        assert_eq!(take_updated_files(&events), all_rs_files);
 
-        // Natural narrowing: typing "R" after "ONE" -> "ONER".
-        // Only one.rs has ONEROUS, 4 other files removed.
+        // Natural narrowing: "ONE" -> "ONER". Only one.rs has ONEROUS.
         perform_incremental_search(search_view, "ONER", cx);
         assert_eq!(read_match_texts(search_view, cx), vec!["ONER"]);
         assert_all_highlights_match_query(search_view, "ONER", cx);
-        assert_eq!(
-            take_excerpt_changes(),
-            vec![
-                ExcerptEvent::Removed { ids: 1 },
-                ExcerptEvent::Removed { ids: 1 },
-                ExcerptEvent::Removed { ids: 1 },
-                ExcerptEvent::Removed { ids: 1 },
-            ]
-        );
+        assert_eq!(take_updated_files(&events), vec!["one.rs"]);
 
-        // Continue typing "OUS" -> "ONEROUS". Still one.rs only, zero excerpt churn.
+        // Continue narrowing: "ONER" -> "ONEROUS". Still one.rs only.
         perform_incremental_search(search_view, "ONEROUS", cx);
         assert_eq!(read_match_texts(search_view, cx), vec!["ONEROUS"]);
         assert_all_highlights_match_query(search_view, "ONEROUS", cx);
-        assert_eq!(take_excerpt_changes(), Vec::new());
+        assert_eq!(take_updated_files(&events), vec!["one.rs"]);
 
-        // Backspace to "ONER" — still one.rs only, zero events.
+        // Backspace to "ONER" — still one.rs only.
         perform_incremental_search(search_view, "ONER", cx);
         assert_eq!(read_match_texts(search_view, cx), vec!["ONER"]);
         assert_all_highlights_match_query(search_view, "ONER", cx);
-        assert_eq!(take_excerpt_changes(), Vec::new());
+        assert_eq!(take_updated_files(&events), vec!["one.rs"]);
 
-        // Backspace to "ONE" — 4 files re-added.
+        // Backspace to "ONE" — all 5 files re-appear.
         perform_incremental_search(search_view, "ONE", cx);
         assert_eq!(read_match_texts(search_view, cx), expected_one_matches);
         assert_all_highlights_match_query(search_view, "ONE", cx);
-        assert_eq!(
-            take_excerpt_changes(),
-            vec![
-                ExcerptEvent::Added { excerpts: 1 },
-                ExcerptEvent::Added { excerpts: 1 },
-                ExcerptEvent::Added { excerpts: 1 },
-                ExcerptEvent::Added { excerpts: 1 },
-            ]
-        );
+        assert_eq!(take_updated_files(&events), all_rs_files);
 
-        // Repeat the same "ONE" query — excerpts already match, zero events emitted.
+        // Repeat the same "ONE" query — excerpts still get updated.
         perform_incremental_search(search_view, "ONE", cx);
         assert_eq!(read_match_texts(search_view, cx), expected_one_matches);
         assert_all_highlights_match_query(search_view, "ONE", cx);
-        assert_eq!(events.borrow().len(), 0);
+        assert_eq!(take_updated_files(&events), all_rs_files);
 
-        // Narrow to "ONLY_ONE" — single match in only_one.rs, 4 files removed.
+        // Narrow to "ONLY_ONE" — single match in only_one.rs.
         perform_incremental_search(search_view, "ONLY_ONE", cx);
         assert_eq!(read_match_texts(search_view, cx), vec!["ONLY_ONE"]);
         assert_all_highlights_match_query(search_view, "ONLY_ONE", cx);
-        assert_eq!(
-            take_excerpt_changes(),
-            vec![
-                ExcerptEvent::Removed { ids: 1 },
-                ExcerptEvent::Removed { ids: 1 },
-                ExcerptEvent::Removed { ids: 1 },
-                ExcerptEvent::Removed { ids: 1 },
-            ]
-        );
+        assert_eq!(take_updated_files(&events), vec!["only_one.rs"]);
 
-        // Widen back to "ONE" — 4 files re-added.
+        // Widen back to "ONE" — all 5 files re-appear.
         perform_incremental_search(search_view, "ONE", cx);
         assert_eq!(read_match_texts(search_view, cx), expected_one_matches);
         assert_all_highlights_match_query(search_view, "ONE", cx);
-        assert_eq!(
-            take_excerpt_changes(),
-            vec![
-                ExcerptEvent::Added { excerpts: 1 },
-                ExcerptEvent::Added { excerpts: 1 },
-                ExcerptEvent::Added { excerpts: 1 },
-                ExcerptEvent::Added { excerpts: 1 },
-            ]
-        );
+        assert_eq!(take_updated_files(&events), all_rs_files);
 
-        // Narrowing when all files still match — zero excerpt events.
-        // "usize" matches all 5 .rs files; "usize =" is narrower but still in every file.
+        // Narrowing when all files still match: "usize" -> "usize =".
+        // Same file set, same match count.
         perform_search(search_view, "usize", cx);
         assert_eq!(read_match_count(search_view, cx), 6);
         events.borrow_mut().clear();
         perform_incremental_search(search_view, "usize =", cx);
         assert_eq!(read_match_count(search_view, cx), 6);
-        assert_eq!(events.borrow().len(), 0);
+        assert_eq!(take_updated_files(&events), all_rs_files);
 
-        // Merged-excerpt narrowing: "target" matches lines 5 and 10 in big.txt,
-        // whose context lines merge into one excerpt [3..12]. Narrowing to
-        // "targeted" shrinks context to [3..7] ⊂ [3..12] — the existing excerpt
-        // must be kept with zero events.
+        // Merged-excerpt narrowing: "target" matches lines 5 and 10 in big.txt.
+        // Narrowing to "targeted" drops to 1 match but big.txt stays.
         perform_search(search_view, "target", cx);
         assert_all_highlights_match_query(search_view, "target", cx);
         assert_eq!(read_match_count(search_view, cx), 2);
-        take_excerpt_changes();
+        events.borrow_mut().clear();
 
         perform_incremental_search(search_view, "targeted", cx);
         assert_all_highlights_match_query(search_view, "targeted", cx);
         assert_eq!(read_match_count(search_view, cx), 1);
-        assert_eq!(take_excerpt_changes(), Vec::new());
+        assert_eq!(take_updated_files(&events), vec!["big.txt"]);
     }
 
     #[gpui::test]
@@ -5930,9 +5873,12 @@ pub mod tests {
         let cx = &mut VisualTestContext::from_window(window.into(), cx);
         let search_bar = window.build_entity(cx, |_, _| ProjectSearchBar::new());
 
-            theme_settings::init(theme::LoadThemes::JustBase, cx);
-
-            ProjectSearchView::new_search(workspace, &workspace::NewSearch, window, cx)
+        workspace.update_in(cx, |workspace, window, cx| {
+            workspace.panes()[0].update(cx, |pane, cx| {
+                pane.toolbar()
+                    .update(cx, |toolbar, cx| toolbar.add_item(search_bar, window, cx))
+            });
+            ProjectSearchView::new_search(workspace, &workspace::NewSearch, window, cx);
         });
 
         let search_view = cx