@@ -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<FileHandle>,
ctx: &mut ViewContext<Self>,
- ) -> LocalBoxFuture<'static, Result<()>> {
+ ) -> LocalBoxFuture<'static, Result<u64>> {
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")) })
}
}
@@ -114,9 +114,7 @@ pub trait ItemView: View {
&mut self,
_: Option<FileHandle>,
_: &mut ViewContext<Self>,
- ) -> LocalBoxFuture<'static, anyhow::Result<()>> {
- Box::pin(async { Ok(()) })
- }
+ ) -> LocalBoxFuture<'static, anyhow::Result<u64>>;
fn should_activate_item_on_event(_: &Self::Event) -> bool {
false
}
@@ -138,7 +136,7 @@ pub trait ItemViewHandle: Send + Sync {
&self,
file: Option<FileHandle>,
ctx: &mut MutableAppContext,
- ) -> LocalBoxFuture<'static, anyhow::Result<()>>;
+ ) -> LocalBoxFuture<'static, anyhow::Result<u64>>;
}
impl<T: ItemView> ItemViewHandle for ViewHandle<T> {
@@ -181,7 +179,7 @@ impl<T: ItemView> ItemViewHandle for ViewHandle<T> {
&self,
file: Option<FileHandle>,
ctx: &mut MutableAppContext,
- ) -> LocalBoxFuture<'static, anyhow::Result<()>> {
+ ) -> LocalBoxFuture<'static, anyhow::Result<u64>> {
self.update(ctx, |item, ctx| item.save(file, ctx))
}
@@ -223,7 +221,7 @@ pub struct Workspace {
(usize, u64),
postage::watch::Receiver<Option<Result<ModelHandle<Buffer>, Arc<anyhow::Error>>>>,
>,
- untitled_buffers: HashSet<ModelHandle<Buffer>>,
+ untitled_buffers: HashMap<usize, ModelHandle<Buffer>>,
}
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::<BufferView>()
+ .unwrap()
+ });
+ app.read(|ctx| {
+ assert_eq!(editor.read(ctx).buffer(), editor2.read(ctx).buffer());
+ })
});
}
@@ -191,17 +191,18 @@ impl Worktree {
path: &Path,
content: BufferSnapshot,
ctx: &AppContext,
- ) -> Task<Result<()>> {
+ ) -> Task<Result<u64>> {
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<Output = Result<History>> {
self.worktree.read(ctx).load_history(&self.path(), ctx)
}
- pub fn save<'a>(&self, content: BufferSnapshot, ctx: &AppContext) -> Task<Result<()>> {
+ pub fn save<'a>(&self, content: BufferSnapshot, ctx: &AppContext) -> Task<Result<u64>> {
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<Path>) {
(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