Fix bug where guest would drop BufferSaved messages while opening the buffer

Max Brunsfeld created

Change summary

crates/collab/src/tests/randomized_integration_tests.rs |  2 
crates/project/src/lsp_command.rs                       | 20 ++--
crates/project/src/project.rs                           | 44 ++++++-----
3 files changed, 35 insertions(+), 31 deletions(-)

Detailed changes

crates/project/src/lsp_command.rs 🔗

@@ -4,7 +4,7 @@ use crate::{
 use anyhow::{anyhow, Result};
 use async_trait::async_trait;
 use client::proto::{self, PeerId};
-use gpui::{AppContext, AsyncAppContext, ModelHandle};
+use gpui::{AppContext, AsyncAppContext, ModelHandle, MutableAppContext};
 use language::{
     point_from_lsp, point_to_lsp,
     proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version},
@@ -49,7 +49,7 @@ pub(crate) trait LspCommand: 'static + Sized {
         project: &mut Project,
         peer_id: PeerId,
         buffer_version: &clock::Global,
-        cx: &AppContext,
+        cx: &mut MutableAppContext,
     ) -> <Self::ProtoRequest as proto::RequestMessage>::Response;
     async fn response_from_proto(
         self,
@@ -175,7 +175,7 @@ impl LspCommand for PrepareRename {
         _: &mut Project,
         _: PeerId,
         buffer_version: &clock::Global,
-        _: &AppContext,
+        _: &mut MutableAppContext,
     ) -> proto::PrepareRenameResponse {
         proto::PrepareRenameResponse {
             can_rename: range.is_some(),
@@ -296,7 +296,7 @@ impl LspCommand for PerformRename {
         project: &mut Project,
         peer_id: PeerId,
         _: &clock::Global,
-        cx: &AppContext,
+        cx: &mut MutableAppContext,
     ) -> proto::PerformRenameResponse {
         let transaction = project.serialize_project_transaction_for_peer(response, peer_id, cx);
         proto::PerformRenameResponse {
@@ -391,7 +391,7 @@ impl LspCommand for GetDefinition {
         project: &mut Project,
         peer_id: PeerId,
         _: &clock::Global,
-        cx: &AppContext,
+        cx: &mut MutableAppContext,
     ) -> proto::GetDefinitionResponse {
         let links = location_links_to_proto(response, project, peer_id, cx);
         proto::GetDefinitionResponse { links }
@@ -477,7 +477,7 @@ impl LspCommand for GetTypeDefinition {
         project: &mut Project,
         peer_id: PeerId,
         _: &clock::Global,
-        cx: &AppContext,
+        cx: &mut MutableAppContext,
     ) -> proto::GetTypeDefinitionResponse {
         let links = location_links_to_proto(response, project, peer_id, cx);
         proto::GetTypeDefinitionResponse { links }
@@ -658,7 +658,7 @@ fn location_links_to_proto(
     links: Vec<LocationLink>,
     project: &mut Project,
     peer_id: PeerId,
-    cx: &AppContext,
+    cx: &mut MutableAppContext,
 ) -> Vec<proto::LocationLink> {
     links
         .into_iter()
@@ -787,7 +787,7 @@ impl LspCommand for GetReferences {
         project: &mut Project,
         peer_id: PeerId,
         _: &clock::Global,
-        cx: &AppContext,
+        cx: &mut MutableAppContext,
     ) -> proto::GetReferencesResponse {
         let locations = response
             .into_iter()
@@ -928,7 +928,7 @@ impl LspCommand for GetDocumentHighlights {
         _: &mut Project,
         _: PeerId,
         _: &clock::Global,
-        _: &AppContext,
+        _: &mut MutableAppContext,
     ) -> proto::GetDocumentHighlightsResponse {
         let highlights = response
             .into_iter()
@@ -1130,7 +1130,7 @@ impl LspCommand for GetHover {
         _: &mut Project,
         _: PeerId,
         _: &clock::Global,
-        _: &AppContext,
+        _: &mut MutableAppContext,
     ) -> proto::GetHoverResponse {
         if let Some(response) = response {
             let (start, end) = if let Some(range) = response.range {

crates/project/src/project.rs 🔗

@@ -5858,7 +5858,7 @@ impl Project {
         &mut self,
         project_transaction: ProjectTransaction,
         peer_id: proto::PeerId,
-        cx: &AppContext,
+        cx: &mut MutableAppContext,
     ) -> proto::ProjectTransaction {
         let mut serialized_transaction = proto::ProjectTransaction {
             buffer_ids: Default::default(),
@@ -5916,27 +5916,27 @@ impl Project {
         &mut self,
         buffer: &ModelHandle<Buffer>,
         peer_id: proto::PeerId,
-        cx: &AppContext,
+        cx: &mut MutableAppContext,
     ) -> u64 {
         let buffer_id = buffer.read(cx).remote_id();
         if let Some(project_id) = self.remote_id() {
             let shared_buffers = self.shared_buffers.entry(peer_id).or_default();
             if shared_buffers.insert(buffer_id) {
-                let buffer = buffer.read(cx);
-                let state = buffer.to_proto();
-                let operations = buffer.serialize_ops(None, cx);
+                let buffer = buffer.clone();
+                let operations = buffer.read(cx).serialize_ops(None, cx);
                 let client = self.client.clone();
-                cx.background()
-                    .spawn(
-                        async move {
-                            let operations = operations.await;
+                cx.spawn(move |cx| async move {
+                    let operations = operations.await;
+                    let state = buffer.read_with(&cx, |buffer, _| buffer.to_proto());
 
-                            client.send(proto::CreateBufferForPeer {
-                                project_id,
-                                peer_id: Some(peer_id),
-                                variant: Some(proto::create_buffer_for_peer::Variant::State(state)),
-                            })?;
+                    client.send(proto::CreateBufferForPeer {
+                        project_id,
+                        peer_id: Some(peer_id),
+                        variant: Some(proto::create_buffer_for_peer::Variant::State(state)),
+                    })?;
 
+                    cx.background()
+                        .spawn(async move {
                             let mut chunks = split_operations(operations).peekable();
                             while let Some(chunk) = chunks.next() {
                                 let is_last = chunks.peek().is_none();
@@ -5952,12 +5952,11 @@ impl Project {
                                     )),
                                 })?;
                             }
-
                             anyhow::Ok(())
-                        }
-                        .log_err(),
-                    )
-                    .detach();
+                        })
+                        .await
+                })
+                .detach()
             }
         }
 
@@ -6231,7 +6230,12 @@ impl Project {
             let buffer = this
                 .opened_buffers
                 .get(&envelope.payload.buffer_id)
-                .and_then(|buffer| buffer.upgrade(cx));
+                .and_then(|buffer| buffer.upgrade(cx))
+                .or_else(|| {
+                    this.incomplete_remote_buffers
+                        .get(&envelope.payload.buffer_id)
+                        .and_then(|b| b.clone())
+                });
             if let Some(buffer) = buffer {
                 buffer.update(cx, |buffer, cx| {
                     buffer.did_save(version, fingerprint, mtime, cx);