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:
@@ -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(
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user