From 78a5d0a37873ba2462a0c3c86d0a0d5d6a472ba5 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 5 May 2021 13:12:51 -0700 Subject: [PATCH] Maintain workspace buffers state after saving untitled buffer --- zed/src/editor/buffer/mod.rs | 2 +- zed/src/editor/buffer_view.rs | 8 +++-- zed/src/workspace.rs | 67 +++++++++++++++++++++++++++++------ zed/src/worktree.rs | 20 +++++++---- 4 files changed, 77 insertions(+), 20 deletions(-) diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index fc0de35ca413609c509cbf94b3220b1af487805b..b1cf5a09575bd64f20a04521e9c9d277f5effed2 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -436,7 +436,7 @@ impl Buffer { &mut self, file: &FileHandle, ctx: &mut ModelContext, - ) -> LocalBoxFuture<'static, Result<()>> { + ) -> LocalBoxFuture<'static, Result> { let snapshot = self.snapshot(); let version = self.version.clone(); let save_task = file.save(snapshot, ctx.as_ref()); diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index 27cb9b4be5fa505cc7d0bd94330b7149ecbc42dd..15ce63120ef6832966d3bbbf15c77ff8286a1aee 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -2058,6 +2058,10 @@ impl BufferView { buffer::Event::FileHandleChanged => ctx.emit(Event::FileHandleChanged), } } + + pub fn file(&self) -> Option<&FileHandle> { + self.file.as_ref() + } } pub enum Event { @@ -2138,7 +2142,7 @@ impl workspace::ItemView for BufferView { &mut self, new_file: Option, ctx: &mut ViewContext, - ) -> LocalBoxFuture<'static, Result<()>> { + ) -> LocalBoxFuture<'static, Result> { if let Some(file) = new_file.as_ref().or(self.file.as_ref()) { let save = self.buffer.update(ctx, |b, ctx| b.save(file, ctx)); ctx.spawn(save, move |this, result, ctx| { @@ -2151,7 +2155,7 @@ impl workspace::ItemView for BufferView { }) .boxed_local() } else { - Box::pin(async { Ok(()) }) + Box::pin(async { Err(anyhow::anyhow!("can't save a buffer with no file")) }) } } diff --git a/zed/src/workspace.rs b/zed/src/workspace.rs index 5d570a845a9a6086395e722b607536ccc4960ab6..24b6d8447b3be4593a9f2b688aeb60ffc51c4ec5 100644 --- a/zed/src/workspace.rs +++ b/zed/src/workspace.rs @@ -114,9 +114,7 @@ pub trait ItemView: View { &mut self, _: Option, _: &mut ViewContext, - ) -> LocalBoxFuture<'static, anyhow::Result<()>> { - Box::pin(async { Ok(()) }) - } + ) -> LocalBoxFuture<'static, anyhow::Result>; fn should_activate_item_on_event(_: &Self::Event) -> bool { false } @@ -138,7 +136,7 @@ pub trait ItemViewHandle: Send + Sync { &self, file: Option, ctx: &mut MutableAppContext, - ) -> LocalBoxFuture<'static, anyhow::Result<()>>; + ) -> LocalBoxFuture<'static, anyhow::Result>; } impl ItemViewHandle for ViewHandle { @@ -181,7 +179,7 @@ impl ItemViewHandle for ViewHandle { &self, file: Option, ctx: &mut MutableAppContext, - ) -> LocalBoxFuture<'static, anyhow::Result<()>> { + ) -> LocalBoxFuture<'static, anyhow::Result> { self.update(ctx, |item, ctx| item.save(file, ctx)) } @@ -223,7 +221,7 @@ pub struct Workspace { (usize, u64), postage::watch::Receiver, Arc>>>, >, - untitled_buffers: HashSet>, + untitled_buffers: HashMap>, } impl Workspace { @@ -372,7 +370,7 @@ impl Workspace { let buffer_view = ctx.add_view(|ctx| { BufferView::for_buffer(buffer.clone(), None, self.settings.clone(), ctx) }); - self.untitled_buffers.insert(buffer); + self.untitled_buffers.insert(buffer_view.id(), buffer); self.add_item(Box::new(buffer_view), ctx); } @@ -486,11 +484,20 @@ impl Workspace { if let Some(path) = path { handle.update(ctx, move |this, ctx| { let file = this.file_for_path(&path, ctx); + let worktree_id = file.worktree_id(); let task = item.save(Some(file), ctx.as_mut()); - ctx.spawn(task, |_, result, _| { - if let Err(e) = result { + let item_id = item.id(); + ctx.spawn(task, move |this, result, _| match result { + Err(e) => { error!("failed to save item: {:?}, ", e); } + Ok(inode) => { + if let Some(buffer) = this.untitled_buffers.remove(&item_id) { + let (_, rx) = + postage::watch::channel_with(Some(Ok(buffer))); + this.buffers.insert((worktree_id, inode), rx); + } + } }) .detach() }) @@ -984,9 +991,47 @@ mod tests { app.read(|ctx| assert_eq!(editor.title(ctx), "untitled")); // When the save completes, the buffer's title is updated. - editor - .condition(&app, |editor, ctx| editor.title(ctx) == "the-new-name") + let worktree = app.read(|ctx| { + workspace + .read(ctx) + .worktrees() + .iter() + .next() + .unwrap() + .clone() + }); + worktree + .condition(&app, |worktree, _| { + worktree.inode_for_path("the-new-name").is_some() + }) .await; + + // Open the same newly-created file in another pane item. + // The new editor should reuse the same buffer. + workspace + .update(&mut app, |workspace, ctx| { + workspace.open_new_file(&(), ctx); + workspace.split_pane( + workspace.active_pane().clone(), + SplitDirection::Right, + ctx, + ); + workspace + .open_entry((worktree.id(), Path::new("the-new-name").into()), ctx) + .unwrap() + }) + .await; + let editor2 = workspace.update(&mut app, |workspace, ctx| { + workspace + .active_item(ctx) + .unwrap() + .to_any() + .downcast::() + .unwrap() + }); + app.read(|ctx| { + assert_eq!(editor.read(ctx).buffer(), editor2.read(ctx).buffer()); + }) }); } diff --git a/zed/src/worktree.rs b/zed/src/worktree.rs index b7fbdb3544ddbdf49d8a8c00f65d4dca3a4eb8df..00900cdb9d35b0ba6c6f8ca1939868d8e2eb0b51 100644 --- a/zed/src/worktree.rs +++ b/zed/src/worktree.rs @@ -191,17 +191,18 @@ impl Worktree { path: &Path, content: BufferSnapshot, ctx: &AppContext, - ) -> Task> { + ) -> Task> { let abs_path = self.snapshot.abs_path.join(path); ctx.background_executor().spawn(async move { let buffer_size = content.text_summary().bytes.min(10 * 1024); let file = std::fs::File::create(&abs_path)?; + let metadata = file.metadata()?; let mut writer = std::io::BufWriter::with_capacity(buffer_size, file); for chunk in content.fragments() { writer.write(chunk.as_bytes())?; } writer.flush()?; - Ok(()) + Ok(metadata.ino()) }) } } @@ -406,15 +407,23 @@ impl FileHandle { self.state.lock().is_deleted } + pub fn exists(&self) -> bool { + !self.is_deleted() + } + pub fn load_history(&self, ctx: &AppContext) -> impl Future> { self.worktree.read(ctx).load_history(&self.path(), ctx) } - pub fn save<'a>(&self, content: BufferSnapshot, ctx: &AppContext) -> Task> { + pub fn save<'a>(&self, content: BufferSnapshot, ctx: &AppContext) -> Task> { let worktree = self.worktree.read(ctx); worktree.save(&self.path(), content, ctx) } + pub fn worktree_id(&self) -> usize { + self.worktree.id() + } + pub fn entry_id(&self) -> (usize, Arc) { (self.worktree.id(), self.path()) } @@ -971,9 +980,8 @@ impl BackgroundScanner { let snapshot = self.snapshot.lock(); handles.retain(|path, handle_state| { if let Some(handle_state) = Weak::upgrade(&handle_state) { - if snapshot.entry_for_path(&path).is_none() { - handle_state.lock().is_deleted = true; - } + let mut handle_state = handle_state.lock(); + handle_state.is_deleted = snapshot.entry_for_path(&path).is_none(); true } else { false