Start adding project search listeners to workspace

Kirill Bulatov created

co-authored-by: Piotr <piotr@zed.dev>

To be able to trigger them from search multibuffer excerpts.

Change summary

crates/assistant/src/assistant_panel.rs    |  2 
crates/search/src/buffer_search.rs         | 72 +++++++++++++++++++----
crates/search/src/project_search.rs        | 70 +++++++++++++++++++++++
crates/terminal_view/src/terminal_panel.rs |  2 
4 files changed, 129 insertions(+), 17 deletions(-)

Detailed changes

crates/assistant/src/assistant_panel.rs 🔗

@@ -1148,7 +1148,7 @@ impl Render for AssistantPanel {
                     |panel, cx| panel.toolbar.read(cx).item_of_type::<BufferSearchBar>(),
                     cx,
                 );
-                BufferSearchBar::register_inner(&mut registrar);
+                BufferSearchBar::register(&mut registrar);
                 registrar.into_div()
             } else {
                 div()

crates/search/src/buffer_search.rs 🔗

@@ -496,67 +496,111 @@ impl SearchActionsRegistrar for Workspace {
         });
     }
 }
+
 impl BufferSearchBar {
-    pub fn register_inner(registrar: &mut impl SearchActionsRegistrar) {
+    pub fn register(registrar: &mut impl SearchActionsRegistrar) {
         registrar.register_handler(|this, action: &ToggleCaseSensitive, cx| {
+            if this.is_dismissed() {
+                cx.propagate();
+                return;
+            }
+
             if this.supported_options().case {
                 this.toggle_case_sensitive(action, cx);
             }
         });
-
         registrar.register_handler(|this, action: &ToggleWholeWord, cx| {
+            if this.is_dismissed() {
+                cx.propagate();
+                return;
+            }
+
             if this.supported_options().word {
                 this.toggle_whole_word(action, cx);
             }
         });
-
         registrar.register_handler(|this, action: &ToggleReplace, cx| {
+            if this.is_dismissed() {
+                cx.propagate();
+                return;
+            }
+
             if this.supported_options().replacement {
                 this.toggle_replace(action, cx);
             }
         });
-
         registrar.register_handler(|this, _: &ActivateRegexMode, cx| {
+            if this.is_dismissed() {
+                cx.propagate();
+                return;
+            }
+
             if this.supported_options().regex {
                 this.activate_search_mode(SearchMode::Regex, cx);
             }
         });
-
         registrar.register_handler(|this, _: &ActivateTextMode, cx| {
+            if this.is_dismissed() {
+                cx.propagate();
+                return;
+            }
+
             this.activate_search_mode(SearchMode::Text, cx);
         });
-
         registrar.register_handler(|this, action: &CycleMode, cx| {
+            if this.is_dismissed() {
+                cx.propagate();
+                return;
+            }
+
             if this.supported_options().regex {
                 // If regex is not supported then search has just one mode (text) - in that case there's no point in supporting
                 // cycling.
                 this.cycle_mode(action, cx)
             }
         });
-
         registrar.register_handler(|this, action: &SelectNextMatch, cx| {
+            if this.is_dismissed() {
+                cx.propagate();
+                return;
+            }
+
             this.select_next_match(action, cx);
         });
         registrar.register_handler(|this, action: &SelectPrevMatch, cx| {
+            if this.is_dismissed() {
+                cx.propagate();
+                return;
+            }
+
             this.select_prev_match(action, cx);
         });
         registrar.register_handler(|this, action: &SelectAllMatches, cx| {
+            if this.is_dismissed() {
+                cx.propagate();
+                return;
+            }
+
             this.select_all_matches(action, cx);
         });
         registrar.register_handler(|this, _: &editor::Cancel, cx| {
-            if this.dismissed {
+            if this.is_dismissed() {
                 cx.propagate();
-            } else {
-                this.dismiss(&Dismiss, cx);
+                return;
             }
+
+            this.dismiss(&Dismiss, cx);
         });
         registrar.register_handler(|this, deploy, cx| {
-            this.deploy(deploy, cx);
+            if this.is_dismissed() {
+                this.deploy(deploy, cx);
+                return;
+            }
+
+            cx.propagate();
         })
     }
-    fn register(workspace: &mut Workspace) {
-        Self::register_inner(workspace);
-    }
+
     pub fn new(cx: &mut ViewContext<Self>) -> Self {
         let query_editor = cx.new_view(|cx| Editor::single_line(cx));
         cx.subscribe(&query_editor, Self::on_query_editor_event)

crates/search/src/project_search.rs 🔗

@@ -63,7 +63,57 @@ pub fn init(cx: &mut AppContext) {
         workspace
             .register_action(ProjectSearchView::new_search)
             .register_action(ProjectSearchView::deploy_search)
-            .register_action(ProjectSearchBar::search_in_new);
+            .register_action(ProjectSearchBar::search_in_new)
+            // TODO kb register these too, consider having the methods for &Workspace for that, as above
+            // ToggleCaseSensitive
+            // ToggleWholeWord
+            // ToggleReplace
+            // ActivateRegexMode
+            // SelectPrevMatch
+            // ActivateTextMode
+            // ActivateSemanticMode
+            // CycleMode
+            // SelectNextMatch (see a proto below)
+            /*
+            // Have a generic method similar to the registrar has:
+            fn register_workspace_action<A: Action>(
+                &mut workspace,
+                callback: fn(&mut ProjectSearchBar, &A, &mut ViewContext<ProjectSearchBar>),
+            ) {
+                workspace.register_action(move |workspace, action: &A, cx| {
+                    if workspace.has_active_modal(cx) {
+                        cx.propagate();
+                        return;
+                    }
+                    if let Some(search_bar) = workspace.active_item(cx).and_then(|item| item.downcast::<ProjectSearchView>()) {
+                        search_bar.update(cx, move |this, cx| callback(this, action, cx));
+                        cx.notify();
+                    }
+                });
+            }
+            */
+            .register_action(move |workspace, action: &SelectNextMatch, cx| {
+                dbg!("@@@@@@@@@1");
+                if workspace.has_active_modal(cx) {
+                    cx.propagate();
+                    return;
+                }
+
+                dbg!("????? 2");
+                let pane = workspace.active_pane();
+                pane.update(cx, move |this, cx| {
+                    this.toolbar().update(cx, move |this, cx| {
+                        dbg!("@@@@@@@@@ 3");
+                        if let Some(search_bar) = this.item_of_type::<ProjectSearchBar>() {
+                            dbg!("$$$$$$$$$ 4");
+                            search_bar.update(cx, move |search_bar, cx| {
+                                search_bar.select_next_match(action, cx)
+                            });
+                            cx.notify();
+                        }
+                    })
+                });
+            });
     })
     .detach();
 }
@@ -1502,6 +1552,22 @@ impl ProjectSearchBar {
         }
     }
 
+    pub fn select_next_match(&mut self, _: &SelectNextMatch, cx: &mut ViewContext<Self>) {
+        if let Some(search) = self.active_project_search.as_ref() {
+            search.update(cx, |this, cx| {
+                this.select_match(Direction::Next, cx);
+            })
+        }
+    }
+
+    fn select_prev_match(&mut self, _: &SelectPrevMatch, cx: &mut ViewContext<Self>) {
+        if let Some(search) = self.active_project_search.as_ref() {
+            search.update(cx, |this, cx| {
+                this.select_match(Direction::Prev, cx);
+            })
+        }
+    }
+
     fn new_placeholder_text(&self, cx: &mut ViewContext<Self>) -> Option<String> {
         let previous_query_keystrokes = cx
             .bindings_for_action(&PreviousHistoryQuery {})
@@ -1870,6 +1936,8 @@ impl Render for ProjectSearchBar {
                     }))
                 })
             })
+            .on_action(cx.listener(Self::select_next_match))
+            .on_action(cx.listener(Self::select_prev_match))
             .child(
                 h_flex()
                     .justify_between()

crates/terminal_view/src/terminal_panel.rs 🔗

@@ -387,7 +387,7 @@ impl Render for TerminalPanel {
             },
             cx,
         );
-        BufferSearchBar::register_inner(&mut registrar);
+        BufferSearchBar::register(&mut registrar);
         registrar.into_div().size_full().child(self.pane.clone())
     }
 }