project: Fix inability to open file after save as (#41012)

Update `project::buffer_store::BufferStore.save_buffer_as` in order to
correctly update the `path_to_buffer_id` hash map, ensuring that the
currently open file's path is dissociated from the buffer's id, to
prevent the new buffer from being open when trying to open the original
file.

Closes #29783 

Release Notes:

- Fixed issue where using `workspace: save as` would prevent users from
opening the original file from which the new file was created

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
This commit is contained in:
Dino
2025-10-24 15:27:34 +01:00
committed by GitHub
co-authored by Piotr Osiewicz
parent 762082b15a
commit 0ee6ca1767
2 changed files with 75 additions and 1 deletions
+8 -1
View File
@@ -909,7 +909,14 @@ impl BufferStore {
};
cx.spawn(async move |this, cx| {
task.await?;
this.update(cx, |_, cx| {
this.update(cx, |this, cx| {
old_file.clone().and_then(|file| {
this.path_to_buffer_id.remove(&ProjectPath {
worktree_id: file.worktree_id(cx),
path: file.path().clone(),
})
});
cx.emit(BufferStoreEvent::BufferChangedFilePath { buffer, old_file });
})
})
+67
View File
@@ -4251,6 +4251,73 @@ async fn test_save_as(cx: &mut gpui::TestAppContext) {
assert_eq!(opened_buffer, buffer);
}
#[gpui::test]
async fn test_save_as_existing_file(cx: &mut gpui::TestAppContext) {
init_test(cx);
let fs = FakeFs::new(cx.executor());
let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await;
fs.insert_tree(
path!("/dir"),
json!({
"data_a.txt": "data about a"
}),
)
.await;
let buffer = project
.update(cx, |project, cx| {
project.open_local_buffer(path!("/dir/data_a.txt"), cx)
})
.await
.unwrap();
buffer.update(cx, |buffer, cx| {
buffer.edit([(11..12, "b")], None, cx);
});
// Save buffer's contents as a new file and confirm that the buffer's now
// associated with `data_b.txt` instead of `data_a.txt`, confirming that the
// file associated with the buffer has now been updated to `data_b.txt`
project
.update(cx, |project, cx| {
let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id();
let new_path = ProjectPath {
worktree_id,
path: rel_path("data_b.txt").into(),
};
project.save_buffer_as(buffer.clone(), new_path, cx)
})
.await
.unwrap();
buffer.update(cx, |buffer, cx| {
assert_eq!(
buffer.file().unwrap().full_path(cx),
Path::new("dir/data_b.txt")
)
});
// Open the original `data_a.txt` file, confirming that its contents are
// unchanged and the resulting buffer's associated file is `data_a.txt`.
let original_buffer = project
.update(cx, |project, cx| {
project.open_local_buffer(path!("/dir/data_a.txt"), cx)
})
.await
.unwrap();
original_buffer.update(cx, |buffer, cx| {
assert_eq!(buffer.text(), "data about a");
assert_eq!(
buffer.file().unwrap().full_path(cx),
Path::new("dir/data_a.txt")
)
});
}
#[gpui::test(retries = 5)]
async fn test_rescan_and_remote_updates(cx: &mut gpui::TestAppContext) {
use worktree::WorktreeModelHandle as _;