First pass at solving search positioning. Good except for buffer search in project search.

KyleBarton and Danilo Leal created

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>

Change summary

crates/breadcrumbs/src/breadcrumbs.rs |  1 
crates/editor/src/items.rs            |  4 +
crates/search/src/buffer_search.rs    | 83 ++++++++++++++++++++++++++--
crates/workspace/src/toolbar.rs       |  5 +
4 files changed, 82 insertions(+), 11 deletions(-)

Detailed changes

crates/breadcrumbs/src/breadcrumbs.rs 🔗

@@ -42,6 +42,7 @@ impl EventEmitter<ToolbarItemEvent> for Breadcrumbs {}
 // - Create a wrapping "Breadcrumb" struct for Vec<BreadcrumbText>
 // - Implement render for _that_ breadcrumb struct.
 // - Call that from here to eliminate much of the logic.
+// - This will change the Item interface, so do it only after you're happy with the features thus far
 impl Render for Breadcrumbs {
     fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         const MAX_SEGMENTS: usize = 12;

crates/editor/src/items.rs 🔗

@@ -997,7 +997,9 @@ impl Item for Editor {
         if self.buffer.read(cx).is_singleton() {
             self.breadcrumbs_inner(variant, cx)
         } else {
-            Some(vec![])
+            // Should say: If you're searching, None, else, vec![] -> What that really means is if you're searching, don't render the chevron (search bar will do it for you), otherwise _do_ render the chevron (which is defined as a breadcrumb prefix)
+            // Some(vec![])
+            None
         }
     }
 

crates/search/src/buffer_search.rs 🔗

@@ -1,9 +1,10 @@
 mod registrar;
 
 use crate::{
-    FocusSearch, NextHistoryQuery, PreviousHistoryQuery, ReplaceAll, ReplaceNext, SearchOption,
-    SearchOptions, SearchSource, SelectAllMatches, SelectNextMatch, SelectPreviousMatch,
-    ToggleCaseSensitive, ToggleRegex, ToggleReplace, ToggleSelection, ToggleWholeWord,
+    FocusSearch, NextHistoryQuery, PreviousHistoryQuery, ProjectSearchView, ReplaceAll,
+    ReplaceNext, SearchOption, SearchOptions, SearchSource, SelectAllMatches, SelectNextMatch,
+    SelectPreviousMatch, ToggleCaseSensitive, ToggleRegex, ToggleReplace, ToggleSelection,
+    ToggleWholeWord,
     search_bar::{ActionButtonState, input_base_styles, render_action_button, render_text_input},
 };
 use any_vec::AnyVec;
@@ -27,7 +28,7 @@ use project::{
 use schemars::JsonSchema;
 use serde::Deserialize;
 use settings::Settings;
-use std::sync::Arc;
+use std::{any::TypeId, sync::Arc};
 use zed_actions::{outline::ToggleOutline, workspace::CopyPath, workspace::CopyRelativePath};
 
 use ui::{
@@ -37,7 +38,7 @@ use ui::{
 use util::{ResultExt, paths::PathMatcher};
 use workspace::{
     ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace,
-    item::ItemHandle,
+    item::{ItemBufferKind, ItemHandle},
     searchable::{
         Direction, FilteredSearchRange, SearchEvent, SearchableItemHandle, WeakSearchableItemHandle,
     },
@@ -401,6 +402,38 @@ impl Render for BufferSearchBar {
                     )))
                     .w_full()
                 });
+
+        // let is_collapsed = self.is_multibuffer_collapsed;
+        // This shouldn't show on a singleton or on a project search
+        let button = if self.active_item_is_multibuffer(cx) {
+            let is_collapsed = false;
+
+            let (icon, tooltip_label) = if is_collapsed {
+                (IconName::ChevronUpDown, "Expand All Search Results")
+            } else {
+                (IconName::ChevronDownUp, "Collapse All Search Results")
+            };
+
+            let tooltip_focus_handle = focus_handle.clone();
+
+            // emit_action!(ToggleFoldAll, focus_handle.clone());
+
+            Some(
+                IconButton::new("multibuffer-collapse-expand", icon)
+                    .icon_size(IconSize::Small)
+                    // .tooltip(move |_, cx| {
+                    //     Tooltip::for_action_in(tooltip_label, &ToggleFoldAll, &tooltip_focus_handle, cx)
+                    // })
+                    // .on_click(cx.listener(|this, _, window, cx| {
+                    //     this.is_multibuffer_collapsed = !this.is_multibuffer_collapsed;
+                    //     this.toggle_fold_all(&ToggleFoldAll, window, cx);
+                    // }))
+                    .into_any_element(),
+            )
+        } else {
+            None
+        };
+
         v_flex()
             .id("buffer_search")
             .gap_2()
@@ -447,9 +480,15 @@ impl Render for BufferSearchBar {
             .when(selection, |this| {
                 this.on_action(cx.listener(Self::toggle_selection))
             })
-            .child(search_line)
+            .child(
+                h_flex()
+                    .gap_1()
+                    .when_some(button, |then, button| then.child(button))
+                    .child(search_line),
+            )
             .children(query_error_line)
             .children(replace_line)
+            .into_any_element()
     }
 }
 
@@ -503,7 +542,11 @@ impl ToolbarItemView for BufferSearchBar {
                 if is_project_search {
                     self.dismiss(&Default::default(), window, cx);
                 } else {
-                    return ToolbarItemLocation::Secondary;
+                    if self.active_item_is_multibuffer(cx) {
+                        return ToolbarItemLocation::PrimaryLeft;
+                    } else {
+                        return ToolbarItemLocation::Secondary;
+                    }
                 }
             }
         }
@@ -789,7 +832,11 @@ impl BufferSearchBar {
         cx.notify();
         cx.emit(Event::UpdateLocation);
         cx.emit(ToolbarItemEvent::ChangeLocation(
-            ToolbarItemLocation::Secondary,
+            if self.active_item_is_multibuffer(cx) {
+                ToolbarItemLocation::PrimaryLeft
+            } else {
+                ToolbarItemLocation::Secondary
+            },
         ));
         true
     }
@@ -801,6 +848,26 @@ impl BufferSearchBar {
             .unwrap_or_default()
     }
 
+    fn active_item_is_multibuffer(&self, cx: &App) -> bool {
+        if let Some(item) = &self.active_searchable_item {
+            dbg!("Calling");
+            let buffer_kind = item.buffer_kind(cx);
+
+            if item
+                .act_as_type(TypeId::of::<ProjectSearchView>(), cx)
+                .is_some()
+            {
+                dbg!("Called success");
+                return false;
+            }
+            dbg!("Called fail");
+
+            buffer_kind == ItemBufferKind::Multibuffer
+        } else {
+            false
+        }
+    }
+
     pub fn search_suggested(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         let search = self.query_suggestion(window, cx).map(|suggestion| {
             self.search(&suggestion, Some(self.default_options), true, window, cx)

crates/workspace/src/toolbar.rs 🔗

@@ -119,12 +119,13 @@ impl Render for Toolbar {
             .when(has_left_items || has_right_items, |this| {
                 this.child(
                     h_flex()
-                        .min_h_6()
+                        .items_start()
                         .justify_between()
                         .gap(DynamicSpacing::Base08.rems(cx))
                         .when(has_left_items, |this| {
                             this.child(
                                 h_flex()
+                                    .min_h_6()
                                     .flex_auto()
                                     .justify_start()
                                     .overflow_x_hidden()
@@ -134,7 +135,7 @@ impl Render for Toolbar {
                         .when(has_right_items, |this| {
                             this.child(
                                 h_flex()
-                                    .h_full()
+                                    .h_6()
                                     .flex_row_reverse()
                                     .map(|el| {
                                         if has_left_items {