Fix enter, tab and shift-tab behavior in project search 2 (#3785)

Kirill Bulatov created

Change summary

crates/search2/src/project_search.rs | 315 -----------------------------
crates/workspace2/src/pane.rs        |  20 +
2 files changed, 18 insertions(+), 317 deletions(-)

Detailed changes

crates/search2/src/project_search.rs 🔗

@@ -1730,12 +1730,15 @@ impl Render for ProjectSearchBar {
             .on_action(cx.listener(|this, _: &ActivateSemanticMode, cx| {
                 this.activate_search_mode(SearchMode::Semantic, cx)
             }))
-            .on_action(cx.listener(|this, action, cx| {
+            .capture_action(cx.listener(|this, action, cx| {
                 this.tab(action, cx);
+                cx.stop_propagation();
             }))
-            .on_action(cx.listener(|this, action, cx| {
+            .capture_action(cx.listener(|this, action, cx| {
                 this.tab_previous(action, cx);
+                cx.stop_propagation();
             }))
+            .on_action(cx.listener(|this, action, cx| this.confirm(action, cx)))
             .on_action(cx.listener(|this, action, cx| {
                 this.cycle_mode(action, cx);
             }))
@@ -1824,314 +1827,6 @@ impl Render for ProjectSearchBar {
             })
     }
 }
-// impl Entity for ProjectSearchBar {
-//     type Event = ();
-// }
-
-// impl View for ProjectSearchBar {
-//     fn ui_name() -> &'static str {
-//         "ProjectSearchBar"
-//     }
-
-//     fn update_keymap_context(
-//         &self,
-//         keymap: &mut gpui::keymap_matcher::KeymapContext,
-//         cx: &AppContext,
-//     ) {
-//         Self::reset_to_default_keymap_context(keymap);
-//         let in_replace = self
-//             .active_project_search
-//             .as_ref()
-//             .map(|search| {
-//                 search
-//                     .read(cx)
-//                     .replacement_editor
-//                     .read_with(cx, |_, cx| cx.is_self_focused())
-//             })
-//             .flatten()
-//             .unwrap_or(false);
-//         if in_replace {
-//             keymap.add_identifier("in_replace");
-//         }
-//     }
-
-//     fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
-//         if let Some(_search) = self.active_project_search.as_ref() {
-//             let search = _search.read(cx);
-//             let theme = theme::current(cx).clone();
-//             let query_container_style = if search.panels_with_errors.contains(&InputPanel::Query) {
-//                 theme.search.invalid_editor
-//             } else {
-//                 theme.search.editor.input.container
-//             };
-
-//             let search = _search.read(cx);
-//             let filter_button = render_option_button_icon(
-//                 search.filters_enabled,
-//                 "icons/filter.svg",
-//                 0,
-//                 "Toggle filters",
-//                 Box::new(ToggleFilters),
-//                 move |_, this, cx| {
-//                     this.toggle_filters(cx);
-//                 },
-//                 cx,
-//             );
-
-//             let search = _search.read(cx);
-//             let is_semantic_available = SemanticIndex::enabled(cx);
-//             let is_semantic_disabled = search.semantic_state.is_none();
-//             let icon_style = theme.search.editor_icon.clone();
-//             let is_active = search.active_match_index.is_some();
-
-//             let render_option_button_icon = |path, option, cx: &mut ViewContext<Self>| {
-//                 crate::search_bar::render_option_button_icon(
-//                     self.is_option_enabled(option, cx),
-//                     path,
-//                     option.bits as usize,
-//                     format!("Toggle {}", option.label()),
-//                     option.to_toggle_action(),
-//                     move |_, this, cx| {
-//                         this.toggle_search_option(option, cx);
-//                     },
-//                     cx,
-//                 )
-//             };
-//             let case_sensitive = is_semantic_disabled.then(|| {
-//                 render_option_button_icon(
-//                     "icons/case_insensitive.svg",
-//                     SearchOptions::CASE_SENSITIVE,
-//                     cx,
-//                 )
-//             });
-
-//             let whole_word = is_semantic_disabled.then(|| {
-//                 render_option_button_icon("icons/word_search.svg", SearchOptions::WHOLE_WORD, cx)
-//             });
-
-//             let include_ignored = is_semantic_disabled.then(|| {
-//                 render_option_button_icon(
-//                     "icons/file_icons/git.svg",
-//                     SearchOptions::INCLUDE_IGNORED,
-//                     cx,
-//                 )
-//             });
-
-//             let search_button_for_mode = |mode, side, cx: &mut ViewContext<ProjectSearchBar>| {
-//                 let is_active = if let Some(search) = self.active_project_search.as_ref() {
-//                     let search = search.read(cx);
-//                     search.current_mode == mode
-//                 } else {
-//                     false
-//                 };
-//                 render_search_mode_button(
-//                     mode,
-//                     side,
-//                     is_active,
-//                     move |_, this, cx| {
-//                         this.activate_search_mode(mode, cx);
-//                     },
-//                     cx,
-//                 )
-//             };
-
-//             let search = _search.read(cx);
-
-//             let include_container_style =
-//                 if search.panels_with_errors.contains(&InputPanel::Include) {
-//                     theme.search.invalid_include_exclude_editor
-//                 } else {
-//                     theme.search.include_exclude_editor.input.container
-//                 };
-
-//             let exclude_container_style =
-//                 if search.panels_with_errors.contains(&InputPanel::Exclude) {
-//                     theme.search.invalid_include_exclude_editor
-//                 } else {
-//                     theme.search.include_exclude_editor.input.container
-//                 };
-
-//             let matches = search.active_match_index.map(|match_ix| {
-//                 Label::new(
-//                     format!(
-//                         "{}/{}",
-//                         match_ix + 1,
-//                         search.model.read(cx).match_ranges.len()
-//                     ),
-//                     theme.search.match_index.text.clone(),
-//                 )
-//                 .contained()
-//                 .with_style(theme.search.match_index.container)
-//                 .aligned()
-//             });
-//             let should_show_replace_input = search.replace_enabled;
-//             let replacement = should_show_replace_input.then(|| {
-//                 Flex::row()
-//                     .with_child(
-//                         Svg::for_style(theme.search.replace_icon.clone().icon)
-//                             .contained()
-//                             .with_style(theme.search.replace_icon.clone().container),
-//                     )
-//                     .with_child(ChildView::new(&search.replacement_editor, cx).flex(1., true))
-//                     .align_children_center()
-//                     .flex(1., true)
-//                     .contained()
-//                     .with_style(query_container_style)
-//                     .constrained()
-//                     .with_min_width(theme.search.editor.min_width)
-//                     .with_max_width(theme.search.editor.max_width)
-//                     .with_height(theme.search.search_bar_row_height)
-//                     .flex(1., false)
-//             });
-//             let replace_all = should_show_replace_input.then(|| {
-//                 super::replace_action(
-//                     ReplaceAll,
-//                     "Replace all",
-//                     "icons/replace_all.svg",
-//                     theme.tooltip.clone(),
-//                     theme.search.action_button.clone(),
-//                 )
-//             });
-//             let replace_next = should_show_replace_input.then(|| {
-//                 super::replace_action(
-//                     ReplaceNext,
-//                     "Replace next",
-//                     "icons/replace_next.svg",
-//                     theme.tooltip.clone(),
-//                     theme.search.action_button.clone(),
-//                 )
-//             });
-//             let query_column = Flex::column()
-//                 .with_spacing(theme.search.search_row_spacing)
-//                 .with_child(
-//                     Flex::row()
-//                         .with_child(
-//                             Svg::for_style(icon_style.icon)
-//                                 .contained()
-//                                 .with_style(icon_style.container),
-//                         )
-//                         .with_child(ChildView::new(&search.query_editor, cx).flex(1., true))
-//                         .with_child(
-//                             Flex::row()
-//                                 .with_child(filter_button)
-//                                 .with_children(case_sensitive)
-//                                 .with_children(whole_word)
-//                                 .flex(1., false)
-//                                 .constrained()
-//                                 .contained(),
-//                         )
-//                         .align_children_center()
-//                         .contained()
-//                         .with_style(query_container_style)
-//                         .constrained()
-//                         .with_min_width(theme.search.editor.min_width)
-//                         .with_max_width(theme.search.editor.max_width)
-//                         .with_height(theme.search.search_bar_row_height)
-//                         .flex(1., false),
-//                 )
-//                 .with_children(search.filters_enabled.then(|| {
-//                     Flex::row()
-//                         .with_child(
-//                             Flex::row()
-//                                 .with_child(
-//                                     ChildView::new(&search.included_files_editor, cx)
-//                                         .contained()
-//                                         .constrained()
-//                                         .with_height(theme.search.search_bar_row_height)
-//                                         .flex(1., true),
-//                                 )
-//                                 .with_children(include_ignored)
-//                                 .contained()
-//                                 .with_style(include_container_style)
-//                                 .constrained()
-//                                 .with_height(theme.search.search_bar_row_height)
-//                                 .flex(1., true),
-//                         )
-//                         .with_child(
-//                             ChildView::new(&search.excluded_files_editor, cx)
-//                                 .contained()
-//                                 .with_style(exclude_container_style)
-//                                 .constrained()
-//                                 .with_height(theme.search.search_bar_row_height)
-//                                 .flex(1., true),
-//                         )
-//                         .constrained()
-//                         .with_min_width(theme.search.editor.min_width)
-//                         .with_max_width(theme.search.editor.max_width)
-//                         .flex(1., false)
-//                 }))
-//                 .flex(1., false);
-//             let switches_column = Flex::row()
-//                 .align_children_center()
-//                 .with_child(super::toggle_replace_button(
-//                     search.replace_enabled,
-//                     theme.tooltip.clone(),
-//                     theme.search.option_button_component.clone(),
-//                 ))
-//                 .constrained()
-//                 .with_height(theme.search.search_bar_row_height)
-//                 .contained()
-//                 .with_style(theme.search.option_button_group);
-//             let mode_column =
-//                 Flex::row()
-//                     .with_child(search_button_for_mode(
-//                         SearchMode::Text,
-//                         Some(Side::Left),
-//                         cx,
-//                     ))
-//                     .with_child(search_button_for_mode(
-//                         SearchMode::Regex,
-//                         if is_semantic_available {
-//                             None
-//                         } else {
-//                             Some(Side::Right)
-//                         },
-//                         cx,
-//                     ))
-//                     .with_children(is_semantic_available.then(|| {
-//                         search_button_for_mode(SearchMode::Semantic, Some(Side::Right), cx)
-//                     }))
-//                     .contained()
-//                     .with_style(theme.search.modes_container);
-
-//             let nav_button_for_direction = |label, direction, cx: &mut ViewContext<Self>| {
-//                 render_nav_button(
-//                     label,
-//                     direction,
-//                     is_active,
-//                     move |_, this, cx| {
-//                         if let Some(search) = this.active_project_search.as_ref() {
-//                             search.update(cx, |search, cx| search.select_match(direction, cx));
-//                         }
-//                     },
-//                     cx,
-//                 )
-//             };
-
-//             let nav_column = Flex::row()
-//                 .with_children(replace_next)
-//                 .with_children(replace_all)
-//                 .with_child(Flex::row().with_children(matches))
-//                 .with_child(nav_button_for_direction("<", Direction::Prev, cx))
-//                 .with_child(nav_button_for_direction(">", Direction::Next, cx))
-//                 .constrained()
-//                 .with_height(theme.search.search_bar_row_height)
-//                 .flex_float();
-
-//             Flex::row()
-//                 .with_child(query_column)
-//                 .with_child(mode_column)
-//                 .with_child(switches_column)
-//                 .with_children(replacement)
-//                 .with_child(nav_column)
-//                 .contained()
-//                 .with_style(theme.search.container)
-//                 .into_any_named("project search")
-//         } else {
-//             Empty::new().into_any()
-//         }
-//     }
-// }
 
 impl EventEmitter<ToolbarItemEvent> for ProjectSearchBar {}
 

crates/workspace2/src/pane.rs 🔗

@@ -1930,7 +1930,8 @@ impl Render for Pane {
                 }),
             )
             .child(self.render_tab_bar(cx))
-            .child(
+            .child({
+                let has_worktrees = self.project.read(cx).worktrees().next().is_some();
                 // main content
                 div()
                     .flex_1()
@@ -1944,10 +1945,15 @@ impl Render for Pane {
                                 .child(self.toolbar.clone())
                                 .child(item.to_any())
                         } else {
-                            div.h_flex().size_full().justify_center().child(
-                                Label::new("Open a file or project to get started.")
-                                    .color(Color::Muted),
-                            )
+                            let placeholder = div.h_flex().size_full().justify_center();
+                            if has_worktrees {
+                                placeholder
+                            } else {
+                                placeholder.child(
+                                    Label::new("Open a file or project to get started.")
+                                        .color(Color::Muted),
+                                )
+                            }
                         }
                     })
                     .child(
@@ -1984,8 +1990,8 @@ impl Render for Pane {
                                     div.top_0().bottom_0().right_0().w_32()
                                 }
                             }),
-                    ),
-            )
+                    )
+            })
             .on_mouse_down(
                 MouseButton::Navigate(NavigationDirection::Back),
                 cx.listener(|pane, _, cx| {