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.
This commit is contained in:
Antonio Scandurra 2022-03-04 14:47:52 +01:00
parent dc5a09b3f7
commit 29cad65ce0
2 changed files with 63 additions and 3 deletions

View File

@ -940,9 +940,15 @@ impl Editor {
_: &workspace::OpenNew, _: &workspace::OpenNew,
cx: &mut ViewContext<Workspace>, cx: &mut ViewContext<Workspace>,
) { ) {
let buffer = cx let project = workspace.project();
.add_model(|cx| Buffer::new(0, "", cx).with_language(language::PLAIN_TEXT.clone(), cx)); if project.read(cx).is_remote() {
workspace.open_item(BufferItemHandle(buffer), cx); 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 { pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {

View File

@ -687,6 +687,18 @@ impl Project {
!self.is_local() !self.is_local()
} }
pub fn create_buffer(&mut self, cx: &mut ModelContext<Self>) -> Result<ModelHandle<Buffer>> {
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( pub fn open_buffer(
&mut self, &mut self,
path: impl Into<ProjectPath>, path: impl Into<ProjectPath>,
@ -4113,6 +4125,48 @@ mod tests {
assert_eq!(new_text, buffer.read_with(cx, |buffer, _| buffer.text())); 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)] #[gpui::test(retries = 5)]
async fn test_rescan_and_remote_updates(cx: &mut gpui::TestAppContext) { async fn test_rescan_and_remote_updates(cx: &mut gpui::TestAppContext) {
let dir = temp_tree(json!({ let dir = temp_tree(json!({