Restore logic for storing operations on buffers that are still being opened

Max Brunsfeld created

Change summary

crates/project/src/project.rs | 48 +++++++++++++++++++++++++++---------
crates/server/src/rpc.rs      |  1 
2 files changed, 36 insertions(+), 13 deletions(-)

Detailed changes

crates/project/src/project.rs 🔗

@@ -16,8 +16,8 @@ use language::{
     point_from_lsp,
     proto::{deserialize_anchor, serialize_anchor},
     range_from_lsp, AnchorRangeExt, Bias, Buffer, CodeAction, Completion, CompletionLabel,
-    Diagnostic, DiagnosticEntry, File as _, Language, LanguageRegistry, PointUtf16, ToLspPosition,
-    ToOffset, ToPointUtf16, Transaction,
+    Diagnostic, DiagnosticEntry, File as _, Language, LanguageRegistry, Operation, PointUtf16,
+    ToLspPosition, ToOffset, ToPointUtf16, Transaction,
 };
 use lsp::{DiagnosticSeverity, LanguageServer};
 use postage::{prelude::Stream, watch};
@@ -46,7 +46,7 @@ pub struct Project {
     collaborators: HashMap<PeerId, Collaborator>,
     subscriptions: Vec<client::Subscription>,
     language_servers_with_diagnostics_running: isize,
-    open_buffers: HashMap<usize, WeakModelHandle<Buffer>>,
+    open_buffers: HashMap<usize, OpenBuffer>,
     loading_buffers: HashMap<
         ProjectPath,
         postage::watch::Receiver<Option<Result<ModelHandle<Buffer>, Arc<anyhow::Error>>>>,
@@ -54,6 +54,11 @@ pub struct Project {
     shared_buffers: HashMap<PeerId, HashMap<u64, ModelHandle<Buffer>>>,
 }
 
+enum OpenBuffer {
+    Loaded(WeakModelHandle<Buffer>),
+    Operations(Vec<Operation>),
+}
+
 enum WorktreeHandle {
     Strong(ModelHandle<Worktree>),
     Weak(WeakModelHandle<Worktree>),
@@ -737,12 +742,15 @@ impl Project {
         worktree: Option<&ModelHandle<Worktree>>,
         cx: &mut ModelContext<Self>,
     ) -> Result<()> {
-        if self
-            .open_buffers
-            .insert(buffer.read(cx).remote_id() as usize, buffer.downgrade())
-            .is_some()
-        {
-            return Err(anyhow!("registered the same buffer twice"));
+        match self.open_buffers.insert(
+            buffer.read(cx).remote_id() as usize,
+            OpenBuffer::Loaded(buffer.downgrade()),
+        ) {
+            None => {}
+            Some(OpenBuffer::Operations(operations)) => {
+                buffer.update(cx, |buffer, cx| buffer.apply_ops(operations, cx))?
+            }
+            Some(OpenBuffer::Loaded(_)) => Err(anyhow!("registered the same buffer twice"))?,
         }
         self.assign_language_to_buffer(&buffer, worktree, cx);
         Ok(())
@@ -2194,10 +2202,17 @@ impl Project {
                 .into_iter()
                 .map(|op| language::proto::deserialize_operation(op))
                 .collect::<Result<Vec<_>, _>>()?;
-            if let Some(buffer) = this.open_buffers.get_mut(&buffer_id) {
-                if let Some(buffer) = buffer.upgrade(cx) {
-                    buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx))?;
+            let buffer = this
+                .open_buffers
+                .entry(buffer_id)
+                .or_insert_with(|| OpenBuffer::Operations(Vec::new()));
+            match buffer {
+                OpenBuffer::Loaded(buffer) => {
+                    if let Some(buffer) = buffer.upgrade(cx) {
+                        buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx))?;
+                    }
                 }
+                OpenBuffer::Operations(operations) => operations.extend_from_slice(&ops),
             }
             Ok(())
         })
@@ -2719,6 +2734,15 @@ impl WorktreeHandle {
     }
 }
 
+impl OpenBuffer {
+    pub fn upgrade(&self, cx: &AppContext) -> Option<ModelHandle<Buffer>> {
+        match self {
+            OpenBuffer::Loaded(handle) => handle.upgrade(cx),
+            OpenBuffer::Operations(_) => None,
+        }
+    }
+}
+
 struct CandidateSet {
     snapshot: Snapshot,
     include_ignored: bool,

crates/server/src/rpc.rs 🔗

@@ -1082,7 +1082,6 @@ mod tests {
         github, AppState, Config,
     };
     use ::rpc::Peer;
-    use async_std::task;
     use gpui::{executor, ModelHandle, TestAppContext};
     use parking_lot::Mutex;
     use postage::{mpsc, watch};