Tolerate language servers reporting non-monotonic buffer versions

Antonio Scandurra and Nathan Sobo created

This isn't perfect but we'll retain up to 10 old versions just in case there
are race conditions in the language server. We haven't seen this in the wild
but we're concerned about diagnostic reporting racing with code action
resolution.

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

Change summary

crates/language/src/buffer.rs | 23 +++++++++++++++--------
crates/project/src/project.rs |  5 +----
2 files changed, 16 insertions(+), 12 deletions(-)

Detailed changes

crates/language/src/buffer.rs 🔗

@@ -1025,14 +1025,7 @@ impl Buffer {
         let version = version.map(|version| version as usize);
         let content =
             if let Some((version, language_server)) = version.zip(self.language_server.as_mut()) {
-                language_server
-                    .pending_snapshots
-                    .retain(|&v, _| v >= version);
-                let snapshot = language_server
-                    .pending_snapshots
-                    .get(&version)
-                    .ok_or_else(|| anyhow!("missing snapshot"))?;
-                &snapshot.buffer_snapshot
+                language_server.snapshot_for_version(version)?
             } else {
                 self.deref()
             };
@@ -2772,6 +2765,20 @@ impl operation_queue::Operation for Operation {
     }
 }
 
+impl LanguageServerState {
+    fn snapshot_for_version(&mut self, version: usize) -> Result<&text::BufferSnapshot> {
+        const OLD_VERSIONS_TO_RETAIN: usize = 10;
+
+        self.pending_snapshots
+            .retain(|&v, _| v + OLD_VERSIONS_TO_RETAIN >= version);
+        let snapshot = self
+            .pending_snapshots
+            .get(&version)
+            .ok_or_else(|| anyhow!("missing snapshot"))?;
+        Ok(&snapshot.buffer_snapshot)
+    }
+}
+
 impl Default for Diagnostic {
     fn default() -> Self {
         Self {

crates/project/src/project.rs 🔗

@@ -1241,10 +1241,7 @@ impl Project {
                         lsp::DocumentChangeOperation::Edit(edit) => todo!(),
                     }
                 }
-                // match edit {
-                //     Ok(edit) => edit.,
-                //     Err(_) => todo!(),
-                // }
+
                 Ok(Default::default())
             })
         } else {