Respect UpdateBufferFile messages on guest buffers without file

Max Brunsfeld and Nathan Sobo created

Co-authored-by: Nathan Sobo <nathan@zed.dev>

Change summary

crates/collab/src/tests/integration_tests.rs | 27 +++++++++++++
crates/language/src/buffer.rs                | 43 ++++++++++-----------
2 files changed, 48 insertions(+), 22 deletions(-)

Detailed changes

crates/collab/src/tests/integration_tests.rs 🔗

@@ -2313,6 +2313,33 @@ async fn test_propagate_saves_and_fs_changes(
         assert_eq!(buffer.file().unwrap().path().to_str(), Some("file1.js"));
         assert_eq!(&*buffer.language().unwrap().name(), "JavaScript");
     });
+
+    let new_buffer_a = project_a
+        .update(cx_a, |p, cx| p.create_buffer("", None, cx))
+        .unwrap();
+    let new_buffer_id = new_buffer_a.read_with(cx_a, |buffer, _| buffer.remote_id());
+    let new_buffer_b = project_b
+        .update(cx_b, |p, cx| p.open_buffer_by_id(new_buffer_id, cx))
+        .await
+        .unwrap();
+    new_buffer_b.read_with(cx_b, |buffer, _| {
+        assert!(buffer.file().is_none());
+    });
+
+    project_a
+        .update(cx_a, |project, cx| {
+            project.save_buffer_as(new_buffer_a, "/a/file3.rs".into(), cx)
+        })
+        .await
+        .unwrap();
+
+    deterministic.run_until_parked();
+    new_buffer_b.read_with(cx_b, |buffer, _| {
+        assert_eq!(
+            buffer.file().unwrap().path().as_ref(),
+            Path::new("file3.rs")
+        );
+    });
 }
 
 #[gpui::test(iterations = 10)]

crates/language/src/buffer.rs 🔗

@@ -661,36 +661,35 @@ impl Buffer {
         new_file: Arc<dyn File>,
         cx: &mut ModelContext<Self>,
     ) -> Task<()> {
-        let old_file = if let Some(file) = self.file.as_ref() {
-            file
-        } else {
-            return Task::ready(());
-        };
         let mut file_changed = false;
         let mut task = Task::ready(());
 
-        if new_file.path() != old_file.path() {
-            file_changed = true;
-        }
-
-        if new_file.is_deleted() {
-            if !old_file.is_deleted() {
+        if let Some(old_file) = self.file.as_ref() {
+            if new_file.path() != old_file.path() {
                 file_changed = true;
-                if !self.is_dirty() {
-                    cx.emit(Event::DirtyChanged);
-                }
             }
-        } else {
-            let new_mtime = new_file.mtime();
-            if new_mtime != old_file.mtime() {
-                file_changed = true;
 
-                if !self.is_dirty() {
-                    let reload = self.reload(cx).log_err().map(drop);
-                    task = cx.foreground().spawn(reload);
+            if new_file.is_deleted() {
+                if !old_file.is_deleted() {
+                    file_changed = true;
+                    if !self.is_dirty() {
+                        cx.emit(Event::DirtyChanged);
+                    }
+                }
+            } else {
+                let new_mtime = new_file.mtime();
+                if new_mtime != old_file.mtime() {
+                    file_changed = true;
+
+                    if !self.is_dirty() {
+                        let reload = self.reload(cx).log_err().map(drop);
+                        task = cx.foreground().spawn(reload);
+                    }
                 }
             }
-        }
+        } else {
+            file_changed = true;
+        };
 
         if file_changed {
             self.file_update_count += 1;