Use proper buffer versions when [de]serializing hint proto requests (#8502)

Kirill Bulatov and Conrad created

During inlay hint<->proto conversions, uses proper buffer versions and
never carries them across the .await points, to fix the
```
thread 'main' panicked at 'invalid anchor Anchor { timestamp: Lamport {0: 8558}, offset: 54, bias: Right, buffer_id: Some(BufferId(8)) }. buffer id: 8, version: Global {0: 8546, 1: 8378}'
/Users/administrator/actions-runner-2/_work/zed/zed/crates/text/src/text.rs:1919
<backtrace::capture::Backtrace>::create
<backtrace::capture::Backtrace>::new
Zed::init_panic_hook::{closure#0}
std::panicking::rust_panic_with_hook
std::panicking::begin_panic_handler::{{closure}}
std::sys_common::backtrace::__rust_end_short_backtrace
_rust_begin_unwind
core::panicking::panic_fmt
<text::BufferSnapshot>::summary_for_anchor::<usize>
<multi_buffer::anchor::Anchor as multi_buffer::ToOffset>::to_offset
<editor::display_map::DisplayMap>::splice_inlays
<editor::Editor>::splice_inlay_hints
editor::inlay_hint_cache::fetch_and_update_hints::{closure#0}::{closure#0}
<async_task::raw::RawTask<<async_task::runnable::Builder<_>>::spawn_local::Checked<core::pin::Pin<alloc::boxed::Box<dyn core::future::future::Future<Output = core::result::Result<(), anyhow::Error>>>>>, core::result::Result<(), anyhow::Error>, <gpui::executor::ForegroundExecutor>::spawn::inner<core::result::Result<(), anyhow::Error>>::{closure#0}, ()>>::run
<gpui::platform::mac::platform::MacPlatform as gpui::platform::Platform>::run
Zed::main
std::sys_common::backtrace::__rust_begin_short_backtrace::<fn(), ()>
std::rt::lang_start::<()>::{closure#0}
std::rt::lang_start_internal
_main
```
class of panics.


Release Notes:

- Fixed occasional panics during collaborative editing with inlay hints
on both sides

Co-authored-by: Conrad <conrad@zed.dev>

Change summary

crates/project/src/project.rs | 44 ++++++++++++++++++------------------
1 file changed, 22 insertions(+), 22 deletions(-)

Detailed changes

crates/project/src/project.rs 🔗

@@ -5756,7 +5756,6 @@ impl Project {
         let range_start = range.start;
         let range_end = range.end;
         let buffer_id = buffer.remote_id().into();
-        let buffer_version = buffer.version().clone();
         let lsp_request = InlayHints { range };
 
         if self.is_local() {
@@ -5782,23 +5781,22 @@ impl Project {
                 buffer_id,
                 start: Some(serialize_anchor(&range_start)),
                 end: Some(serialize_anchor(&range_end)),
-                version: serialize_version(&buffer_version),
+                version: serialize_version(&buffer_handle.read(cx).version()),
             };
             cx.spawn(move |project, cx| async move {
                 let response = client
                     .request(request)
                     .await
                     .context("inlay hints proto request")?;
-                let hints_request_result = LspCommand::response_from_proto(
+                LspCommand::response_from_proto(
                     lsp_request,
                     response,
                     project.upgrade().ok_or_else(|| anyhow!("No project"))?,
                     buffer_handle.clone(),
-                    cx,
+                    cx.clone(),
                 )
-                .await;
-
-                hints_request_result.context("inlay hints proto response conversion")
+                .await
+                .context("inlay hints proto response conversion")
             })
         } else {
             Task::ready(Err(anyhow!("project does not have a remote id")))
@@ -8074,20 +8072,12 @@ impl Project {
                 .and_then(|buffer| buffer.upgrade())
                 .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))
         })??;
-        let buffer_version = deserialize_version(&envelope.payload.version);
-
         buffer
             .update(&mut cx, |buffer, _| {
-                buffer.wait_for_version(buffer_version.clone())
+                buffer.wait_for_version(deserialize_version(&envelope.payload.version))
             })?
             .await
-            .with_context(|| {
-                format!(
-                    "waiting for version {:?} for buffer {}",
-                    buffer_version,
-                    buffer.entity_id()
-                )
-            })?;
+            .with_context(|| format!("waiting for version for buffer {}", buffer.entity_id()))?;
 
         let start = envelope
             .payload
@@ -8101,13 +8091,19 @@ impl Project {
             .context("missing range end")?;
         let buffer_hints = this
             .update(&mut cx, |project, cx| {
-                project.inlay_hints(buffer, start..end, cx)
+                project.inlay_hints(buffer.clone(), start..end, cx)
             })?
             .await
             .context("inlay hints fetch")?;
 
         Ok(this.update(&mut cx, |project, cx| {
-            InlayHints::response_to_proto(buffer_hints, project, sender_id, &buffer_version, cx)
+            InlayHints::response_to_proto(
+                buffer_hints,
+                project,
+                sender_id,
+                &buffer.read(cx).version(),
+                cx,
+            )
         })?)
     }
 
@@ -8183,10 +8179,14 @@ impl Project {
             cx.clone(),
         )
         .await?;
-        let buffer_version = buffer_handle.update(&mut cx, |buffer, _| buffer.version())?;
         let response = this
             .update(&mut cx, |this, cx| {
-                this.request_lsp(buffer_handle, LanguageServerToQuery::Primary, request, cx)
+                this.request_lsp(
+                    buffer_handle.clone(),
+                    LanguageServerToQuery::Primary,
+                    request,
+                    cx,
+                )
             })?
             .await?;
         this.update(&mut cx, |this, cx| {
@@ -8194,7 +8194,7 @@ impl Project {
                 response,
                 this,
                 sender_id,
-                &buffer_version,
+                &buffer_handle.read(cx).version(),
                 cx,
             ))
         })?