project_search: Add button to collapse/expand all excerpts (#41654)

<img width="500" height="834" alt="Screenshot 2025-11-03 at 12  59@2x"
src="https://github.com/user-attachments/assets/15c5e1fc-2291-41b4-9eec-a8cfa5a446c7"
/>

Releases Note:

- Added a button that allows to expand/collapse all project search
excerpts at once.

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
This commit is contained in:
Dylan
2025-11-03 14:50:34 -03:00
committed by GitHub
co-authored by Danilo Leal
parent 45b78482f5
commit 7cfce60570
9 changed files with 125 additions and 8 deletions
+91 -5
View File
@@ -57,7 +57,9 @@ actions!(
/// Moves to the next input field.
NextField,
/// Toggles the search filters panel.
ToggleFilters
ToggleFilters,
/// Toggles collapse/expand state of all search result excerpts.
ToggleAllSearchResults
]
);
@@ -120,6 +122,20 @@ pub fn init(cx: &mut App) {
ProjectSearchView::search_in_new(workspace, action, window, cx)
});
register_workspace_action_for_present_search(
workspace,
|workspace, action: &ToggleAllSearchResults, window, cx| {
if let Some(search_view) = workspace
.active_item(cx)
.and_then(|item| item.downcast::<ProjectSearchView>())
{
search_view.update(cx, |search_view, cx| {
search_view.toggle_all_search_results(action, window, cx);
});
}
},
);
register_workspace_action_for_present_search(
workspace,
|workspace, _: &menu::Cancel, window, cx| {
@@ -219,6 +235,7 @@ pub struct ProjectSearchView {
replace_enabled: bool,
included_opened_only: bool,
regex_language: Option<Arc<Language>>,
results_collapsed: bool,
_subscriptions: Vec<Subscription>,
}
@@ -651,6 +668,44 @@ impl Item for ProjectSearchView {
fn breadcrumbs(&self, theme: &theme::Theme, cx: &App) -> Option<Vec<BreadcrumbText>> {
self.results_editor.breadcrumbs(theme, cx)
}
fn breadcrumb_prefix(
&self,
_window: &mut Window,
cx: &mut Context<Self>,
) -> Option<gpui::AnyElement> {
if !self.has_matches() {
return None;
}
let is_collapsed = self.results_collapsed;
let (icon, tooltip_label) = if is_collapsed {
(IconName::ChevronUpDown, "Expand All Search Results")
} else {
(IconName::ChevronDownUp, "Collapse All Search Results")
};
let focus_handle = self.query_editor.focus_handle(cx);
Some(
IconButton::new("project-search-collapse-expand", icon)
.shape(IconButtonShape::Square)
.icon_size(IconSize::Small)
.tooltip(move |_, cx| {
Tooltip::for_action_in(
tooltip_label,
&ToggleAllSearchResults,
&focus_handle,
cx,
)
})
.on_click(cx.listener(|this, _, window, cx| {
this.toggle_all_search_results(&ToggleAllSearchResults, window, cx);
}))
.into_any_element(),
)
}
}
impl ProjectSearchView {
@@ -753,6 +808,34 @@ impl ProjectSearchView {
});
}
fn toggle_all_search_results(
&mut self,
_: &ToggleAllSearchResults,
_window: &mut Window,
cx: &mut Context<Self>,
) {
self.results_collapsed = !self.results_collapsed;
self.update_results_visibility(cx);
}
fn update_results_visibility(&mut self, cx: &mut Context<Self>) {
self.results_editor.update(cx, |editor, cx| {
let multibuffer = editor.buffer().read(cx);
let buffer_ids = multibuffer.excerpt_buffer_ids();
if self.results_collapsed {
for buffer_id in buffer_ids {
editor.fold_buffer(buffer_id, cx);
}
} else {
for buffer_id in buffer_ids {
editor.unfold_buffer(buffer_id, cx);
}
}
});
cx.notify();
}
pub fn new(
workspace: WeakEntity<Workspace>,
entity: Entity<ProjectSearch>,
@@ -911,8 +994,10 @@ impl ProjectSearchView {
replace_enabled: false,
included_opened_only: false,
regex_language: None,
results_collapsed: false,
_subscriptions: subscriptions,
};
this.entity_changed(window, cx);
this
}
@@ -1411,6 +1496,7 @@ impl ProjectSearchView {
fn entity_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
let match_ranges = self.entity.read(cx).match_ranges.clone();
if match_ranges.is_empty() {
self.active_match_index = None;
self.results_editor.update(cx, |editor, cx| {
@@ -1968,6 +2054,8 @@ impl Render for ProjectSearchBar {
})
.unwrap_or_else(|| "0/0".to_string());
let query_focus = search.query_editor.focus_handle(cx);
let query_column = input_base_styles(InputPanel::Query)
.on_action(cx.listener(|this, action, window, cx| this.confirm(action, window, cx)))
.on_action(cx.listener(|this, action, window, cx| {
@@ -1997,11 +2085,9 @@ impl Render for ProjectSearchBar {
)),
);
let query_focus = search.query_editor.focus_handle(cx);
let matches_column = h_flex()
.pl_2()
.ml_2()
.ml_1()
.pl_1p5()
.border_l_1()
.border_color(theme_colors.border_variant)
.child(render_action_button(
-1
View File
@@ -46,7 +46,6 @@ pub(crate) fn input_base_styles(border_color: Hsla, map: impl FnOnce(Div) -> Div
.h_8()
.pl_2()
.pr_1()
.py_1()
.border_1()
.border_color(border_color)
.rounded_md()