SSH Remoting: Fix reload/save race (#19519)

Mikayla Maki and Conrad Irwin created

Release Notes:

- N/A

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>

Change summary

crates/language/src/buffer.rs                    |  4 +++-
crates/multi_buffer/src/multi_buffer.rs          |  3 ++-
crates/project/src/buffer_store.rs               | 11 ++++-------
crates/project/src/project.rs                    |  6 ++++++
crates/remote_server/src/remote_editing_tests.rs | 12 ++++++++++++
5 files changed, 27 insertions(+), 9 deletions(-)

Detailed changes

crates/language/src/buffer.rs 🔗

@@ -336,6 +336,8 @@ pub enum BufferEvent {
     FileHandleChanged,
     /// The buffer was reloaded.
     Reloaded,
+    /// The buffer is in need of a reload
+    ReloadNeeded,
     /// The buffer's diff_base changed.
     DiffBaseChanged,
     /// Buffer's excerpts for a certain diff base were recalculated.
@@ -1077,7 +1079,7 @@ impl Buffer {
                     file_changed = true;
 
                     if !self.is_dirty() {
-                        self.reload(cx).close();
+                        cx.emit(BufferEvent::ReloadNeeded);
                     }
                 }
             }

crates/multi_buffer/src/multi_buffer.rs 🔗

@@ -94,6 +94,7 @@ pub enum Event {
         transaction_id: TransactionId,
     },
     Reloaded,
+    ReloadNeeded,
     DiffBaseChanged,
     DiffUpdated {
         buffer: Model<Buffer>,
@@ -1735,6 +1736,7 @@ impl MultiBuffer {
             language::BufferEvent::Saved => Event::Saved,
             language::BufferEvent::FileHandleChanged => Event::FileHandleChanged,
             language::BufferEvent::Reloaded => Event::Reloaded,
+            language::BufferEvent::ReloadNeeded => Event::ReloadNeeded,
             language::BufferEvent::DiffBaseChanged => Event::DiffBaseChanged,
             language::BufferEvent::DiffUpdated => Event::DiffUpdated { buffer },
             language::BufferEvent::LanguageChanged => {
@@ -1748,7 +1750,6 @@ impl MultiBuffer {
                 self.capability = buffer.read(cx).capability();
                 Event::CapabilityChanged
             }
-
             //
             language::BufferEvent::Operation { .. } => return,
         });

crates/project/src/buffer_store.rs 🔗

@@ -54,7 +54,7 @@ trait BufferStoreImpl {
 
     fn reload_buffers(
         &self,
-        buffers: Vec<Model<Buffer>>,
+        buffers: HashSet<Model<Buffer>>,
         push_to_history: bool,
         cx: &mut ModelContext<BufferStore>,
     ) -> Task<Result<ProjectTransaction>>;
@@ -392,7 +392,7 @@ impl BufferStoreImpl for Model<RemoteBufferStore> {
 
     fn reload_buffers(
         &self,
-        buffers: Vec<Model<Buffer>>,
+        buffers: HashSet<Model<Buffer>>,
         push_to_history: bool,
         cx: &mut ModelContext<BufferStore>,
     ) -> Task<Result<ProjectTransaction>> {
@@ -938,7 +938,7 @@ impl BufferStoreImpl for Model<LocalBufferStore> {
 
     fn reload_buffers(
         &self,
-        buffers: Vec<Model<Buffer>>,
+        buffers: HashSet<Model<Buffer>>,
         push_to_history: bool,
         cx: &mut ModelContext<BufferStore>,
     ) -> Task<Result<ProjectTransaction>> {
@@ -1894,13 +1894,10 @@ impl BufferStore {
         push_to_history: bool,
         cx: &mut ModelContext<Self>,
     ) -> Task<Result<ProjectTransaction>> {
-        let buffers: Vec<Model<Buffer>> = buffers
-            .into_iter()
-            .filter(|buffer| buffer.read(cx).is_dirty())
-            .collect();
         if buffers.is_empty() {
             return Task::ready(Ok(ProjectTransaction::default()));
         }
+
         self.state.reload_buffers(buffers, push_to_history, cx)
     }
 

crates/project/src/project.rs 🔗

@@ -2312,6 +2312,12 @@ impl Project {
 
         let buffer_id = buffer.read(cx).remote_id();
         match event {
+            BufferEvent::ReloadNeeded => {
+                if !self.is_via_collab() {
+                    self.reload_buffers([buffer.clone()].into_iter().collect(), false, cx)
+                        .detach_and_log_err(cx);
+                }
+            }
             BufferEvent::Operation {
                 operation,
                 is_local: true,

crates/remote_server/src/remote_editing_tests.rs 🔗

@@ -479,7 +479,19 @@ async fn test_remote_reload(cx: &mut TestAppContext, server_cx: &mut TestAppCont
         })
         .await
         .unwrap();
+
+    fs.save(
+        &PathBuf::from("/code/project1/src/lib.rs"),
+        &("bangles".to_string().into()),
+        LineEnding::Unix,
+    )
+    .await
+    .unwrap();
+
+    cx.run_until_parked();
+
     buffer.update(cx, |buffer, cx| {
+        assert_eq!(buffer.text(), "bangles");
         buffer.edit([(0..0, "a")], None, cx);
     });