Add DivRegistrar to reduce code duplication

Piotr Osiewicz created

Change summary

crates/assistant/src/assistant_panel.rs    | 30 ++-------------
crates/search/src/buffer_search.rs         | 40 +++++++++++++++++++++
crates/terminal_view/src/terminal_panel.rs | 44 +++++++----------------
3 files changed, 59 insertions(+), 55 deletions(-)

Detailed changes

crates/assistant/src/assistant_panel.rs 🔗

@@ -38,7 +38,7 @@ use gpui::{
 };
 use language::{language_settings::SoftWrap, Buffer, LanguageRegistry, ToOffset as _};
 use project::Project;
-use search::{buffer_search::SearchActionsRegistrar, BufferSearchBar};
+use search::{buffer_search::DivRegistrar, BufferSearchBar};
 use semantic_index::{SemanticIndex, SemanticIndexStatus};
 use settings::{Settings, SettingsStore};
 use std::{
@@ -1100,26 +1100,6 @@ fn build_api_key_editor(cx: &mut ViewContext<AssistantPanel>) -> View<Editor> {
     })
 }
 
-struct SearchRegistrar<'a, 'b> {
-    div: Option<Div>,
-    cx: &'a mut ViewContext<'b, AssistantPanel>,
-}
-
-impl SearchActionsRegistrar for SearchRegistrar<'_, '_> {
-    fn register_handler<A: Action>(
-        &mut self,
-        callback: fn(&mut BufferSearchBar, &A, &mut ViewContext<BufferSearchBar>),
-    ) {
-        self.div = self.div.take().map(|div| {
-            div.on_action(self.cx.listener(move |this, action, cx| {
-                this.toolbar
-                    .read(cx)
-                    .item_of_type::<BufferSearchBar>()
-                    .map(|search_bar| search_bar.update(cx, |this, cx| callback(this, action, cx)));
-            }))
-        });
-    }
-}
 impl Render for AssistantPanel {
     fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         if let Some(api_key_editor) = self.api_key_editor.clone() {
@@ -1177,12 +1157,12 @@ impl Render for AssistantPanel {
                 });
 
             let contents = if self.active_editor().is_some() {
-                let mut registrar = SearchRegistrar {
-                    div: Some(div()),
+                let mut registrar = DivRegistrar::new(
+                    |panel, cx| panel.toolbar.read(cx).item_of_type::<BufferSearchBar>(),
                     cx,
-                };
+                );
                 BufferSearchBar::register_inner(&mut registrar);
-                registrar.div.unwrap()
+                registrar.into_div()
             } else {
                 div()
             };

crates/search/src/buffer_search.rs 🔗

@@ -430,6 +430,46 @@ pub trait SearchActionsRegistrar {
     );
 }
 
+type GetSearchBar<T> =
+    for<'a, 'b> fn(&'a T, &'a mut ViewContext<'b, T>) -> Option<View<BufferSearchBar>>;
+
+/// Registers search actions on a div that can be taken out.
+pub struct DivRegistrar<'a, 'b, T: 'static> {
+    div: Option<Div>,
+    cx: &'a mut ViewContext<'b, T>,
+    search_getter: GetSearchBar<T>,
+}
+
+impl<'a, 'b, T: 'static> DivRegistrar<'a, 'b, T> {
+    pub fn new(search_getter: GetSearchBar<T>, cx: &'a mut ViewContext<'b, T>) -> Self {
+        Self {
+            div: Some(div()),
+            cx,
+            search_getter,
+        }
+    }
+    pub fn into_div(self) -> Div {
+        // This option is always Some; it's an option in the first place because we want to call methods
+        // on div that require ownership.
+        self.div.unwrap()
+    }
+}
+
+impl<T: 'static> SearchActionsRegistrar for DivRegistrar<'_, '_, T> {
+    fn register_handler<A: gpui::Action>(
+        &mut self,
+        callback: fn(&mut BufferSearchBar, &A, &mut ViewContext<BufferSearchBar>),
+    ) {
+        let getter = self.search_getter;
+        self.div = self.div.take().map(|div| {
+            div.on_action(self.cx.listener(move |this, action, cx| {
+                (getter)(this, cx)
+                    .clone()
+                    .map(|search_bar| search_bar.update(cx, |this, cx| callback(this, action, cx)));
+            }))
+        });
+    }
+}
 impl BufferSearchBar {
     pub fn register_inner(registrar: &mut impl SearchActionsRegistrar) {
         registrar.register_handler(|this, action: &ToggleCaseSensitive, cx| {

crates/terminal_view/src/terminal_panel.rs 🔗

@@ -3,13 +3,12 @@ use std::{path::PathBuf, sync::Arc};
 use crate::TerminalView;
 use db::kvp::KEY_VALUE_STORE;
 use gpui::{
-    actions, div, serde_json, AppContext, AsyncWindowContext, Div, Entity, EventEmitter,
-    ExternalPaths, FocusHandle, FocusableView, InteractiveElement, IntoElement, ParentElement,
-    Pixels, Render, Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView,
-    WindowContext,
+    actions, serde_json, AppContext, AsyncWindowContext, Entity, EventEmitter, ExternalPaths,
+    FocusHandle, FocusableView, IntoElement, ParentElement, Pixels, Render, Styled, Subscription,
+    Task, View, ViewContext, VisualContext, WeakView, WindowContext,
 };
 use project::Fs;
-use search::{buffer_search::SearchActionsRegistrar, BufferSearchBar};
+use search::{buffer_search::DivRegistrar, BufferSearchBar};
 use serde::{Deserialize, Serialize};
 use settings::{Settings, SettingsStore};
 use terminal::terminal_settings::{TerminalDockPosition, TerminalSettings};
@@ -330,36 +329,21 @@ impl TerminalPanel {
 
 impl EventEmitter<PanelEvent> for TerminalPanel {}
 
-struct ActionsRegistrar<'a, 'b>
-where
-    'b: 'a,
-{
-    div: Option<Div>,
-    cx: &'a mut ViewContext<'b, TerminalPanel>,
-}
-impl SearchActionsRegistrar for ActionsRegistrar<'_, '_> {
-    fn register_handler<A: gpui::Action>(
-        &mut self,
-        callback: fn(&mut BufferSearchBar, &A, &mut ViewContext<BufferSearchBar>),
-    ) {
-        self.div = self.div.take().map(|div| {
-            div.on_action(self.cx.listener(move |this, action, cx| {
-                this.pane
+impl Render for TerminalPanel {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+        let mut registrar = DivRegistrar::new(
+            |panel, cx| {
+                panel
+                    .pane
                     .read(cx)
                     .toolbar()
                     .read(cx)
                     .item_of_type::<BufferSearchBar>()
-                    .map(|search_bar| search_bar.update(cx, |this, cx| callback(this, action, cx)));
-            }))
-        });
-    }
-}
-impl Render for TerminalPanel {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
-        let div = div();
-        let mut registrar = ActionsRegistrar { div: Some(div), cx };
+            },
+            cx,
+        );
         BufferSearchBar::register_inner(&mut registrar);
-        registrar.div.unwrap().size_full().child(self.pane.clone())
+        registrar.into_div().size_full().child(self.pane.clone())
     }
 }