From 29cad65ce00bfff660e848cfb54b7097b4730403 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 4 Mar 2022 14:47:52 +0100 Subject: [PATCH] Open untitled buffers via the `Project` This allows the registration of such buffers in the project, which is necessary to correctly support `::save_buffer_as` and opens the door to sharing untitled buffers with guests in the future. Note that, for now, this disallows guests to create untitled buffers in the current window and will create a new window instead. This is because we don't yet have a global way of allocating a buffer's remote id (nor a way of saving such buffers in the host's worktree) and we instead rely on the local model ID, which could clash with the host's buffer IDs. I think we should revisit this once guests can share their untitled buffers with the host and other remote peers, as well as once we start keying operations by entry id. --- crates/editor/src/editor.rs | 12 ++++++-- crates/project/src/project.rs | 54 +++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 4b75627ef3..857b33f625 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -940,9 +940,15 @@ impl Editor { _: &workspace::OpenNew, cx: &mut ViewContext, ) { - let buffer = cx - .add_model(|cx| Buffer::new(0, "", cx).with_language(language::PLAIN_TEXT.clone(), cx)); - workspace.open_item(BufferItemHandle(buffer), cx); + let project = workspace.project(); + if project.read(cx).is_remote() { + cx.propagate_action(); + } else if let Some(buffer) = project + .update(cx, |project, cx| project.create_buffer(cx)) + .log_err() + { + workspace.open_item(BufferItemHandle(buffer), cx); + } } pub fn replica_id(&self, cx: &AppContext) -> ReplicaId { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index a89317db90..501cca2822 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -687,6 +687,18 @@ impl Project { !self.is_local() } + pub fn create_buffer(&mut self, cx: &mut ModelContext) -> Result> { + if self.is_remote() { + return Err(anyhow!("creating buffers as a guest is not supported yet")); + } + + let buffer = cx.add_model(|cx| { + Buffer::new(self.replica_id(), "", cx).with_language(language::PLAIN_TEXT.clone(), cx) + }); + self.register_buffer(&buffer, None, cx)?; + Ok(buffer) + } + pub fn open_buffer( &mut self, path: impl Into, @@ -4113,6 +4125,48 @@ mod tests { assert_eq!(new_text, buffer.read_with(cx, |buffer, _| buffer.text())); } + #[gpui::test] + async fn test_save_as(cx: &mut gpui::TestAppContext) { + let fs = FakeFs::new(cx.background()); + fs.insert_tree("/dir", json!({})).await; + + let project = Project::test(fs.clone(), cx); + let (worktree, _) = project + .update(cx, |project, cx| { + project.find_or_create_local_worktree("/dir", true, cx) + }) + .await + .unwrap(); + let worktree_id = worktree.read_with(cx, |worktree, _| worktree.id()); + + let buffer = project.update(cx, |project, cx| project.create_buffer(cx).unwrap()); + buffer.update(cx, |buffer, cx| { + buffer.edit([0..0], "abc", cx); + assert!(buffer.is_dirty()); + assert!(!buffer.has_conflict()); + }); + project + .update(cx, |project, cx| { + project.save_buffer_as(buffer.clone(), "/dir/file1".into(), cx) + }) + .await + .unwrap(); + assert_eq!(fs.load(Path::new("/dir/file1")).await.unwrap(), "abc"); + buffer.read_with(cx, |buffer, cx| { + assert_eq!(buffer.file().unwrap().full_path(cx), Path::new("dir/file1")); + assert!(!buffer.is_dirty()); + assert!(!buffer.has_conflict()); + }); + + let opened_buffer = project + .update(cx, |project, cx| { + project.open_buffer((worktree_id, "file1"), cx) + }) + .await + .unwrap(); + assert_eq!(opened_buffer, buffer); + } + #[gpui::test(retries = 5)] async fn test_rescan_and_remote_updates(cx: &mut gpui::TestAppContext) { let dir = temp_tree(json!({