Detailed changes
@@ -0,0 +1,4 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M11.3335 13.3333L8.00017 10L4.66685 13.3333" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M11.3335 2.66669L8.00017 6.00002L4.66685 2.66669" stroke="black" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
@@ -407,6 +407,7 @@
"bindings": {
"escape": "project_search::ToggleFocus",
"shift-find": "search::FocusSearch",
+ "shift-enter": "project_search::ToggleAllSearchResults",
"ctrl-shift-f": "search::FocusSearch",
"ctrl-shift-h": "search::ToggleReplace",
"alt-ctrl-g": "search::ToggleRegex",
@@ -479,6 +480,7 @@
"alt-w": "search::ToggleWholeWord",
"alt-find": "project_search::ToggleFilters",
"alt-ctrl-f": "project_search::ToggleFilters",
+ "shift-enter": "project_search::ToggleAllSearchResults",
"ctrl-alt-shift-r": "search::ToggleRegex",
"ctrl-alt-shift-x": "search::ToggleRegex",
"alt-r": "search::ToggleRegex",
@@ -468,6 +468,7 @@
"bindings": {
"escape": "project_search::ToggleFocus",
"cmd-shift-j": "project_search::ToggleFilters",
+ "shift-enter": "project_search::ToggleAllSearchResults",
"cmd-shift-f": "search::FocusSearch",
"cmd-shift-h": "search::ToggleReplace",
"alt-cmd-g": "search::ToggleRegex",
@@ -496,6 +497,7 @@
"bindings": {
"escape": "project_search::ToggleFocus",
"cmd-shift-j": "project_search::ToggleFilters",
+ "shift-enter": "project_search::ToggleAllSearchResults",
"cmd-shift-h": "search::ToggleReplace",
"alt-cmd-g": "search::ToggleRegex",
"alt-cmd-x": "search::ToggleRegex"
@@ -488,6 +488,7 @@
"alt-c": "search::ToggleCaseSensitive",
"alt-w": "search::ToggleWholeWord",
"alt-f": "project_search::ToggleFilters",
+ "shift-enter": "project_search::ToggleAllSearchResults",
"alt-r": "search::ToggleRegex",
// "ctrl-shift-alt-x": "search::ToggleRegex",
"ctrl-k shift-enter": "pane::TogglePinTab"
@@ -100,13 +100,21 @@ impl Render for Breadcrumbs {
let breadcrumbs_stack = h_flex().gap_1().children(breadcrumbs);
+ let prefix_element = active_item.breadcrumb_prefix(window, cx);
+
+ let breadcrumbs = if let Some(prefix) = prefix_element {
+ h_flex().gap_1p5().child(prefix).child(breadcrumbs_stack)
+ } else {
+ breadcrumbs_stack
+ };
+
match active_item
.downcast::<Editor>()
.map(|editor| editor.downgrade())
{
Some(editor) => element.child(
ButtonLike::new("toggle outline view")
- .child(breadcrumbs_stack)
+ .child(breadcrumbs)
.style(ButtonStyle::Transparent)
.on_click({
let editor = editor.clone();
@@ -141,7 +149,7 @@ impl Render for Breadcrumbs {
// Match the height and padding of the `ButtonLike` in the other arm.
.h(rems_from_px(22.))
.pl_1()
- .child(breadcrumbs_stack),
+ .child(breadcrumbs),
}
}
}
@@ -53,6 +53,7 @@ pub enum IconName {
Check,
CheckDouble,
ChevronDown,
+ ChevronDownUp,
ChevronLeft,
ChevronRight,
ChevronUp,
@@ -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()
@@ -296,6 +296,15 @@ pub trait Item: Focusable + EventEmitter<Self::Event> + Render + Sized {
None
}
+ /// Returns optional elements to render to the left of the breadcrumb.
+ fn breadcrumb_prefix(
+ &self,
+ _window: &mut Window,
+ _cx: &mut Context<Self>,
+ ) -> Option<gpui::AnyElement> {
+ None
+ }
+
fn added_to_workspace(
&mut self,
_workspace: &mut Workspace,
@@ -479,6 +488,7 @@ pub trait ItemHandle: 'static + Send {
fn to_searchable_item_handle(&self, cx: &App) -> Option<Box<dyn SearchableItemHandle>>;
fn breadcrumb_location(&self, cx: &App) -> ToolbarItemLocation;
fn breadcrumbs(&self, theme: &Theme, cx: &App) -> Option<Vec<BreadcrumbText>>;
+ fn breadcrumb_prefix(&self, window: &mut Window, cx: &mut App) -> Option<gpui::AnyElement>;
fn show_toolbar(&self, cx: &App) -> bool;
fn pixel_position_of_cursor(&self, cx: &App) -> Option<Point<Pixels>>;
fn downgrade_item(&self) -> Box<dyn WeakItemHandle>;
@@ -979,6 +989,10 @@ impl<T: Item> ItemHandle for Entity<T> {
self.read(cx).breadcrumbs(theme, cx)
}
+ fn breadcrumb_prefix(&self, window: &mut Window, cx: &mut App) -> Option<gpui::AnyElement> {
+ self.update(cx, |item, cx| item.breadcrumb_prefix(window, cx))
+ }
+
fn show_toolbar(&self, cx: &App) -> bool {
self.read(cx).show_toolbar()
}