Don't share query editor state after project find has been split

Antonio Scandurra and Nathan Sobo created

Co-Authored-By: Nathan Sobo <nathan@zed.dev>

Change summary

crates/editor/src/multi_buffer.rs | 32 ++++++++++++++++
crates/find/src/project_find.rs   | 64 +++++++++++++++++++-------------
2 files changed, 70 insertions(+), 26 deletions(-)

Detailed changes

crates/editor/src/multi_buffer.rs 🔗

@@ -43,6 +43,7 @@ pub struct MultiBuffer {
     title: Option<String>,
 }
 
+#[derive(Clone)]
 struct History {
     next_transaction_id: TransactionId,
     undo_stack: Vec<Transaction>,
@@ -168,6 +169,37 @@ impl MultiBuffer {
         }
     }
 
+    pub fn clone(&self, new_cx: &mut ModelContext<Self>) -> Self {
+        let mut buffers = HashMap::default();
+        for (buffer_id, buffer_state) in self.buffers.borrow().iter() {
+            buffers.insert(
+                *buffer_id,
+                BufferState {
+                    buffer: buffer_state.buffer.clone(),
+                    last_version: buffer_state.last_version.clone(),
+                    last_parse_count: buffer_state.last_parse_count,
+                    last_selections_update_count: buffer_state.last_selections_update_count,
+                    last_diagnostics_update_count: buffer_state.last_diagnostics_update_count,
+                    last_file_update_count: buffer_state.last_file_update_count,
+                    excerpts: buffer_state.excerpts.clone(),
+                    _subscriptions: [
+                        new_cx.observe(&buffer_state.buffer, |_, _, cx| cx.notify()),
+                        new_cx.subscribe(&buffer_state.buffer, Self::on_buffer_event),
+                    ],
+                },
+            );
+        }
+        Self {
+            snapshot: RefCell::new(self.snapshot.borrow().clone()),
+            buffers: RefCell::new(buffers),
+            subscriptions: Default::default(),
+            singleton: self.singleton,
+            replica_id: self.replica_id,
+            history: self.history.clone(),
+            title: self.title.clone(),
+        }
+    }
+
     pub fn with_title(mut self, title: String) -> Self {
         self.title = Some(title);
         self

crates/find/src/project_find.rs 🔗

@@ -34,7 +34,6 @@ pub fn init(cx: &mut MutableAppContext) {
 struct ProjectFind {
     project: ModelHandle<Project>,
     excerpts: ModelHandle<MultiBuffer>,
-    query: Option<SearchQuery>,
     pending_search: Option<Task<Option<()>>>,
     highlighted_ranges: Vec<Range<Anchor>>,
 }
@@ -60,17 +59,26 @@ impl ProjectFind {
         Self {
             project,
             excerpts: cx.add_model(|_| MultiBuffer::new(replica_id)),
-            query: Default::default(),
             pending_search: Default::default(),
             highlighted_ranges: Default::default(),
         }
     }
 
+    fn clone(&self, new_cx: &mut ModelContext<Self>) -> Self {
+        Self {
+            project: self.project.clone(),
+            excerpts: self
+                .excerpts
+                .update(new_cx, |excerpts, cx| cx.add_model(|cx| excerpts.clone(cx))),
+            pending_search: Default::default(),
+            highlighted_ranges: self.highlighted_ranges.clone(),
+        }
+    }
+
     fn search(&mut self, query: SearchQuery, cx: &mut ModelContext<Self>) {
         let search = self
             .project
             .update(cx, |project, cx| project.search(query.clone(), cx));
-        self.query = Some(query.clone());
         self.highlighted_ranges.clear();
         self.pending_search = Some(cx.spawn_weak(|this, mut cx| async move {
             let matches = search.await;
@@ -270,19 +278,38 @@ impl ItemView for ProjectFindView {
         Self: Sized,
     {
         let query_editor = cx.add_view(|cx| {
-            Editor::single_line(
+            let query = self.query_editor.read(cx).text(cx);
+            let editor = Editor::single_line(
                 self.settings.clone(),
                 Some(|theme| theme.find.editor.input.clone()),
                 cx,
-            )
+            );
+            editor
+                .buffer()
+                .update(cx, |buffer, cx| buffer.edit([0..0], query, cx));
+            editor
         });
-        let results_editor = self.results_editor.update(cx, |results_editor, cx| {
-            cx.add_view(|cx| results_editor.clone(nav_history, cx))
-        });
-        cx.observe(&self.model, |this, _, cx| this.model_changed(true, cx))
+        let model = self
+            .model
+            .update(cx, |model, cx| cx.add_model(|cx| model.clone(cx)));
+
+        cx.observe(&model, |this, _, cx| this.model_changed(true, cx))
             .detach();
+        let results_editor = cx.add_view(|cx| {
+            let model = model.read(cx);
+            let excerpts = model.excerpts.clone();
+            let project = model.project.clone();
+            let scroll_position = self
+                .results_editor
+                .update(cx, |editor, cx| editor.scroll_position(cx));
+
+            let mut editor = Editor::for_buffer(excerpts, Some(project), self.settings.clone(), cx);
+            editor.set_nav_history(Some(nav_history));
+            editor.set_scroll_position(scroll_position, cx);
+            editor
+        });
         let mut view = Self {
-            model: self.model.clone(),
+            model,
             query_editor,
             results_editor,
             case_sensitive: self.case_sensitive,
@@ -349,22 +376,7 @@ impl ProjectFindView {
     }
 
     fn model_changed(&mut self, reset_selections: bool, cx: &mut ViewContext<Self>) {
-        let model = self.model.read(cx);
-        let highlighted_ranges = model.highlighted_ranges.clone();
-        if let Some(query) = model.query.clone() {
-            self.case_sensitive = query.case_sensitive();
-            self.whole_word = query.whole_word();
-            self.regex = query.is_regex();
-            self.query_editor.update(cx, |query_editor, cx| {
-                if query_editor.text(cx) != query.as_str() {
-                    query_editor.buffer().update(cx, |query_buffer, cx| {
-                        let len = query_buffer.read(cx).len();
-                        query_buffer.edit([0..len], query.as_str(), cx);
-                    });
-                }
-            });
-        }
-
+        let highlighted_ranges = self.model.read(cx).highlighted_ranges.clone();
         if !highlighted_ranges.is_empty() {
             let theme = &self.settings.borrow().theme.find;
             self.results_editor.update(cx, |editor, cx| {