Maintain workspace buffers state after saving untitled buffer
This commit is contained in:
@@ -436,7 +436,7 @@ impl Buffer {
|
||||
&mut self,
|
||||
file: &FileHandle,
|
||||
ctx: &mut ModelContext<Self>,
|
||||
) -> LocalBoxFuture<'static, Result<()>> {
|
||||
) -> LocalBoxFuture<'static, Result<u64>> {
|
||||
let snapshot = self.snapshot();
|
||||
let version = self.version.clone();
|
||||
let save_task = file.save(snapshot, ctx.as_ref());
|
||||
|
||||
@@ -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")) })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+56
-11
@@ -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());
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+14
-6
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user