diff --git a/crates/outline_panel/src/outline_panel.rs b/crates/outline_panel/src/outline_panel.rs index 25dd5cba8d..72b97c8f69 100644 --- a/crates/outline_panel/src/outline_panel.rs +++ b/crates/outline_panel/src/outline_panel.rs @@ -2825,7 +2825,6 @@ impl OutlinePanel { cx.spawn(|outline_panel, mut cx| async move { let mut entries = Vec::new(); let mut match_candidates = Vec::new(); - let mut added_contexts = HashSet::default(); let Ok(()) = outline_panel.update(&mut cx, |outline_panel, cx| { let auto_fold_dirs = OutlinePanelSettings::get_global(cx).auto_fold_dirs; @@ -2947,7 +2946,6 @@ impl OutlinePanel { outline_panel.push_entry( &mut entries, &mut match_candidates, - &mut added_contexts, track_matches, new_folded_dirs, folded_depth, @@ -2986,7 +2984,6 @@ impl OutlinePanel { outline_panel.push_entry( &mut entries, &mut match_candidates, - &mut added_contexts, track_matches, PanelEntry::FoldedDirs(worktree_id, folded_dirs), folded_depth, @@ -3012,7 +3009,6 @@ impl OutlinePanel { outline_panel.push_entry( &mut entries, &mut match_candidates, - &mut added_contexts, track_matches, PanelEntry::FoldedDirs(worktree_id, folded_dirs), folded_depth, @@ -3049,7 +3045,6 @@ impl OutlinePanel { outline_panel.push_entry( &mut entries, &mut match_candidates, - &mut added_contexts, track_matches, PanelEntry::Fs(entry.clone()), depth, @@ -3063,7 +3058,6 @@ impl OutlinePanel { outline_panel.add_search_entries( &mut entries, &mut match_candidates, - &mut added_contexts, entry.clone(), depth, query.clone(), @@ -3097,7 +3091,6 @@ impl OutlinePanel { query.as_deref(), &mut entries, &mut match_candidates, - &mut added_contexts, cx, ); } @@ -3113,7 +3106,6 @@ impl OutlinePanel { outline_panel.push_entry( &mut entries, &mut match_candidates, - &mut added_contexts, track_matches, PanelEntry::Fs(entry.clone()), 0, @@ -3132,7 +3124,6 @@ impl OutlinePanel { outline_panel.push_entry( &mut entries, &mut match_candidates, - &mut added_contexts, track_matches, PanelEntry::FoldedDirs(worktree_id, folded_dirs), folded_depth, @@ -3144,22 +3135,10 @@ impl OutlinePanel { return Vec::new(); }; - outline_panel - .update(&mut cx, |outline_panel, _| { - if matches!(outline_panel.mode, ItemsDisplayMode::Search(_)) { - cleanup_fs_entries_without_search_children( - &outline_panel.collapsed_entries, - &mut entries, - &mut match_candidates, - &mut added_contexts, - ); - } - }) - .ok(); - let Some(query) = query else { return entries; }; + let mut matched_ids = match_strings( &match_candidates, &query, @@ -3195,7 +3174,6 @@ impl OutlinePanel { &self, entries: &mut Vec, match_candidates: &mut Vec, - added_contexts: &mut HashSet, track_matches: bool, entry: PanelEntry, depth: usize, @@ -3221,47 +3199,39 @@ impl OutlinePanel { if let Some(file_name) = self.relative_path(fs_entry, cx).as_deref().map(file_name) { - if added_contexts.insert(file_name.clone()) { - match_candidates.push(StringMatchCandidate { - id, - string: file_name.to_string(), - char_bag: file_name.chars().collect(), - }); - } + match_candidates.push(StringMatchCandidate { + id, + string: file_name.to_string(), + char_bag: file_name.chars().collect(), + }); } } PanelEntry::FoldedDirs(worktree_id, entries) => { let dir_names = self.dir_names_string(entries, *worktree_id, cx); { - if added_contexts.insert(dir_names.clone()) { - match_candidates.push(StringMatchCandidate { - id, - string: dir_names.clone(), - char_bag: dir_names.chars().collect(), - }); - } + match_candidates.push(StringMatchCandidate { + id, + string: dir_names.clone(), + char_bag: dir_names.chars().collect(), + }); } } PanelEntry::Outline(outline_entry) => match outline_entry { OutlineEntry::Outline(_, _, outline) => { - if added_contexts.insert(outline.text.clone()) { - match_candidates.push(StringMatchCandidate { - id, - string: outline.text.clone(), - char_bag: outline.text.chars().collect(), - }); - } + match_candidates.push(StringMatchCandidate { + id, + string: outline.text.clone(), + char_bag: outline.text.chars().collect(), + }); } OutlineEntry::Excerpt(..) => {} }, PanelEntry::Search(new_search_entry) => { - if added_contexts.insert(new_search_entry.render_data.context_text.clone()) { - match_candidates.push(StringMatchCandidate { - id, - char_bag: new_search_entry.render_data.context_text.chars().collect(), - string: new_search_entry.render_data.context_text.clone(), - }); - } + match_candidates.push(StringMatchCandidate { + id, + char_bag: new_search_entry.render_data.context_text.chars().collect(), + string: new_search_entry.render_data.context_text.clone(), + }); } } } @@ -3408,7 +3378,6 @@ impl OutlinePanel { query: Option<&str>, entries: &mut Vec, match_candidates: &mut Vec, - added_contexts: &mut HashSet, cx: &mut ViewContext, ) { if let Some(excerpts) = self.excerpts.get(&buffer_id) { @@ -3420,7 +3389,6 @@ impl OutlinePanel { self.push_entry( entries, match_candidates, - added_contexts, track_matches, PanelEntry::Outline(OutlineEntry::Excerpt( buffer_id, @@ -3448,7 +3416,6 @@ impl OutlinePanel { self.push_entry( entries, match_candidates, - added_contexts, track_matches, PanelEntry::Outline(OutlineEntry::Outline( buffer_id, @@ -3468,7 +3435,6 @@ impl OutlinePanel { &mut self, entries: &mut Vec, match_candidates: &mut Vec, - added_contexts: &mut HashSet, parent_entry: FsEntry, parent_depth: usize, filter_query: Option, @@ -3556,7 +3522,6 @@ impl OutlinePanel { self.push_entry( entries, match_candidates, - added_contexts, filter_query.is_some(), PanelEntry::Search(new_search_entry), depth, @@ -3618,131 +3583,6 @@ impl OutlinePanel { } } -fn cleanup_fs_entries_without_search_children( - collapsed_entries: &HashSet, - entries: &mut Vec, - string_match_candidates: &mut Vec, - added_contexts: &mut HashSet, -) { - let mut match_ids_to_remove = BTreeSet::new(); - let mut previous_entry = None::<&PanelEntry>; - for (id, entry) in entries.iter().enumerate().rev() { - let has_search_items = match (previous_entry, &entry.entry) { - (Some(PanelEntry::Outline(_)), _) => unreachable!(), - (_, PanelEntry::Outline(_)) => false, - (_, PanelEntry::Search(_)) => true, - (None, PanelEntry::FoldedDirs(_, _) | PanelEntry::Fs(_)) => false, - ( - Some(PanelEntry::Search(_)), - PanelEntry::FoldedDirs(_, _) | PanelEntry::Fs(FsEntry::Directory(..)), - ) => false, - (Some(PanelEntry::FoldedDirs(..)), PanelEntry::FoldedDirs(..)) => true, - ( - Some(PanelEntry::Search(_)), - PanelEntry::Fs(FsEntry::File(..) | FsEntry::ExternalFile(..)), - ) => true, - ( - Some(PanelEntry::Fs(previous_fs)), - PanelEntry::FoldedDirs(folded_worktree, folded_dirs), - ) => { - let expected_parent = folded_dirs.last().map(|dir_entry| dir_entry.path.as_ref()); - match previous_fs { - FsEntry::ExternalFile(..) => false, - FsEntry::File(file_worktree, file_entry, ..) => { - file_worktree == folded_worktree - && file_entry.path.parent() == expected_parent - } - FsEntry::Directory(directory_wortree, directory_entry) => { - directory_wortree == folded_worktree - && directory_entry.path.parent() == expected_parent - } - } - } - ( - Some(PanelEntry::FoldedDirs(folded_worktree, folded_dirs)), - PanelEntry::Fs(fs_entry), - ) => match fs_entry { - FsEntry::File(..) | FsEntry::ExternalFile(..) => false, - FsEntry::Directory(directory_wortree, maybe_parent_directory) => { - directory_wortree == folded_worktree - && Some(maybe_parent_directory.path.as_ref()) - == folded_dirs - .first() - .and_then(|dir_entry| dir_entry.path.parent()) - } - }, - (Some(PanelEntry::Fs(previous_entry)), PanelEntry::Fs(maybe_parent_entry)) => { - match (previous_entry, maybe_parent_entry) { - (FsEntry::ExternalFile(..), _) | (_, FsEntry::ExternalFile(..)) => false, - (FsEntry::Directory(..) | FsEntry::File(..), FsEntry::File(..)) => false, - ( - FsEntry::Directory(previous_worktree, previous_directory), - FsEntry::Directory(new_worktree, maybe_parent_directory), - ) => { - previous_worktree == new_worktree - && previous_directory.path.parent() - == Some(maybe_parent_directory.path.as_ref()) - } - ( - FsEntry::File(previous_worktree, previous_file, ..), - FsEntry::Directory(new_worktree, maybe_parent_directory), - ) => { - previous_worktree == new_worktree - && previous_file.path.parent() - == Some(maybe_parent_directory.path.as_ref()) - } - } - } - }; - - if has_search_items { - previous_entry = Some(&entry.entry); - } else { - let collapsed_entries_to_check = match &entry.entry { - PanelEntry::FoldedDirs(worktree_id, entries) => entries - .iter() - .map(|entry| CollapsedEntry::Dir(*worktree_id, entry.id)) - .collect(), - PanelEntry::Fs(FsEntry::Directory(worktree_id, entry)) => { - vec![CollapsedEntry::Dir(*worktree_id, entry.id)] - } - PanelEntry::Fs(FsEntry::ExternalFile(buffer_id, _)) => { - vec![CollapsedEntry::ExternalFile(*buffer_id)] - } - PanelEntry::Fs(FsEntry::File(worktree_id, _, buffer_id, _)) => { - vec![CollapsedEntry::File(*worktree_id, *buffer_id)] - } - PanelEntry::Search(_) | PanelEntry::Outline(_) => Vec::new(), - }; - if !collapsed_entries_to_check.is_empty() - && collapsed_entries_to_check - .iter() - .any(|collapsed_entry| collapsed_entries.contains(collapsed_entry)) - { - previous_entry = Some(&entry.entry); - continue; - } - match_ids_to_remove.insert(id); - previous_entry = None; - } - } - - if match_ids_to_remove.is_empty() { - return; - } - - string_match_candidates.retain(|candidate| { - let retain = !match_ids_to_remove.contains(&candidate.id); - if !retain { - added_contexts.remove(&candidate.string); - } - retain - }); - match_ids_to_remove.into_iter().rev().for_each(|id| { - entries.remove(id); - }); -} - fn workspace_active_editor( workspace: &Workspace, cx: &AppContext, @@ -4374,6 +4214,117 @@ mod tests { }); } + #[gpui::test] + async fn test_item_filtering(cx: &mut TestAppContext) { + init_test(cx); + + let fs = FakeFs::new(cx.background_executor.clone()); + populate_with_test_ra_project(&fs, "/rust-analyzer").await; + let project = Project::test(fs.clone(), ["/rust-analyzer".as_ref()], cx).await; + project.read_with(cx, |project, _| { + project.languages().add(Arc::new(rust_lang())) + }); + let workspace = add_outline_panel(&project, cx).await; + let cx = &mut VisualTestContext::from_window(*workspace, cx); + let outline_panel = outline_panel(&workspace, cx); + outline_panel.update(cx, |outline_panel, cx| outline_panel.set_active(true, cx)); + + workspace + .update(cx, |workspace, cx| { + ProjectSearchView::deploy_search(workspace, &workspace::DeploySearch::default(), cx) + }) + .unwrap(); + let search_view = workspace + .update(cx, |workspace, cx| { + workspace + .active_pane() + .read(cx) + .items() + .find_map(|item| item.downcast::()) + .expect("Project search view expected to appear after new search event trigger") + }) + .unwrap(); + + let query = "param_names_for_lifetime_elision_hints"; + perform_project_search(&search_view, query, cx); + search_view.update(cx, |search_view, cx| { + search_view + .results_editor() + .update(cx, |results_editor, cx| { + assert_eq!( + results_editor.display_text(cx).match_indices(query).count(), + 9 + ); + }); + }); + let all_matches = r#"/ + crates/ + ide/src/ + inlay_hints/ + fn_lifetime_fn.rs + search: match config.param_names_for_lifetime_elision_hints { + search: allocated_lifetimes.push(if config.param_names_for_lifetime_elision_hints { + search: Some(it) if config.param_names_for_lifetime_elision_hints => { + search: InlayHintsConfig { param_names_for_lifetime_elision_hints: true, ..TEST_CONFIG }, + inlay_hints.rs + search: pub param_names_for_lifetime_elision_hints: bool, + search: param_names_for_lifetime_elision_hints: self + static_index.rs + search: param_names_for_lifetime_elision_hints: false, + rust-analyzer/src/ + cli/ + analysis_stats.rs + search: param_names_for_lifetime_elision_hints: true, + config.rs + search: param_names_for_lifetime_elision_hints: self"#; + + cx.executor() + .advance_clock(UPDATE_DEBOUNCE + Duration::from_millis(100)); + cx.run_until_parked(); + outline_panel.update(cx, |outline_panel, _| { + assert_eq!( + display_entries(&outline_panel.cached_entries, None,), + all_matches, + ); + }); + + let filter_text = "a"; + outline_panel.update(cx, |outline_panel, cx| { + outline_panel.filter_editor.update(cx, |filter_editor, cx| { + filter_editor.set_text(filter_text, cx); + }); + }); + cx.executor() + .advance_clock(UPDATE_DEBOUNCE + Duration::from_millis(100)); + cx.run_until_parked(); + + outline_panel.update(cx, |outline_panel, _| { + assert_eq!( + display_entries(&outline_panel.cached_entries, None), + all_matches + .lines() + .filter(|item| item.contains(filter_text)) + .collect::>() + .join("\n"), + ); + }); + + outline_panel.update(cx, |outline_panel, cx| { + outline_panel.filter_editor.update(cx, |filter_editor, cx| { + filter_editor.set_text("", cx); + }); + }); + cx.executor() + .advance_clock(UPDATE_DEBOUNCE + Duration::from_millis(100)); + cx.run_until_parked(); + outline_panel.update(cx, |outline_panel, _| { + assert_eq!( + display_entries(&outline_panel.cached_entries, None,), + all_matches, + ); + }); + } + #[gpui::test] async fn test_frontend_repo_structure(cx: &mut TestAppContext) { init_test(cx);