Return a future from WorkspaceView::open_paths

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Max Brunsfeld 2021-04-29 14:33:42 -07:00
parent b126938af7
commit 5826a976ef
3 changed files with 130 additions and 70 deletions

View file

@ -1717,6 +1717,10 @@ impl<'a, T: View> ViewContext<'a, T> {
self.window_id self.window_id
} }
pub fn foreground(&self) -> &Rc<executor::Foreground> {
self.app.foreground_executor()
}
pub fn background_executor(&self) -> &Arc<executor::Background> { pub fn background_executor(&self) -> &Arc<executor::Background> {
&self.app.ctx.background &self.app.ctx.background
} }

View file

@ -52,7 +52,8 @@ fn open_paths(params: &OpenParams, app: &mut MutableAppContext) {
if let Some(handle) = app.root_view::<WorkspaceView>(window_id) { if let Some(handle) = app.root_view::<WorkspaceView>(window_id) {
if handle.update(app, |view, ctx| { if handle.update(app, |view, ctx| {
if view.contains_paths(&params.paths, ctx.as_ref()) { if view.contains_paths(&params.paths, ctx.as_ref()) {
view.open_paths(&params.paths, ctx); let open_paths = view.open_paths(&params.paths, ctx);
ctx.foreground().spawn(open_paths).detach();
log::info!("open paths on existing workspace"); log::info!("open paths on existing workspace");
true true
} else { } else {
@ -70,7 +71,8 @@ fn open_paths(params: &OpenParams, app: &mut MutableAppContext) {
let workspace = app.add_model(|ctx| Workspace::new(vec![], ctx)); let workspace = app.add_model(|ctx| Workspace::new(vec![], ctx));
app.add_window(|ctx| { app.add_window(|ctx| {
let view = WorkspaceView::new(workspace, params.settings.clone(), ctx); let view = WorkspaceView::new(workspace, params.settings.clone(), ctx);
view.open_paths(&params.paths, ctx); let open_paths = view.open_paths(&params.paths, ctx);
ctx.foreground().spawn(open_paths).detach();
view view
}); });
} }

View file

@ -1,6 +1,6 @@
use super::{pane, Pane, PaneGroup, SplitDirection, Workspace}; use super::{pane, Pane, PaneGroup, SplitDirection, Workspace};
use crate::{settings::Settings, watch}; use crate::{settings::Settings, watch};
use futures_core::future::LocalBoxFuture; use futures_core::{future::LocalBoxFuture, Future};
use gpui::{ use gpui::{
color::rgbu, elements::*, json::to_string_pretty, keymap::Binding, AnyViewHandle, AppContext, color::rgbu, elements::*, json::to_string_pretty, keymap::Binding, AnyViewHandle, AppContext,
ClipboardItem, Entity, ModelHandle, MutableAppContext, View, ViewContext, ViewHandle, ClipboardItem, Entity, ModelHandle, MutableAppContext, View, ViewContext, ViewHandle,
@ -161,22 +161,38 @@ impl WorkspaceView {
self.workspace.read(app).contains_paths(paths, app) self.workspace.read(app).contains_paths(paths, app)
} }
pub fn open_paths(&self, paths: &[PathBuf], ctx: &mut ViewContext<Self>) { pub fn open_paths(
&self,
paths: &[PathBuf],
ctx: &mut ViewContext<Self>,
) -> impl Future<Output = ()> {
let entries = self let entries = self
.workspace .workspace
.update(ctx, |workspace, ctx| workspace.open_paths(paths, ctx)); .update(ctx, |workspace, ctx| workspace.open_paths(paths, ctx));
for (i, entry) in entries.into_iter().enumerate() { let bg = ctx.background_executor().clone();
let path = paths[i].clone(); let tasks = paths
ctx.spawn( .iter()
ctx.background_executor() .cloned()
.spawn(async move { path.is_file() }), .zip(entries.into_iter())
|me, is_file, ctx| { .map(|(path, entry)| {
if is_file { ctx.spawn(
me.open_entry(entry, ctx) bg.spawn(async move { path.is_file() }),
} |me, is_file, ctx| {
}, if is_file {
) me.open_entry(entry, ctx)
.detach(); } else {
None
}
},
)
})
.collect::<Vec<_>>();
async move {
for task in tasks {
if let Some(task) = task.await {
task.await;
}
}
} }
} }
@ -207,16 +223,20 @@ impl WorkspaceView {
} }
} }
pub fn open_entry(&mut self, entry: (usize, Arc<Path>), ctx: &mut ViewContext<Self>) { pub fn open_entry(
&mut self,
entry: (usize, Arc<Path>),
ctx: &mut ViewContext<Self>,
) -> Option<impl Future<Output = ()>> {
if self.loading_entries.contains(&entry) { if self.loading_entries.contains(&entry) {
return; return None;
} }
if self if self
.active_pane() .active_pane()
.update(ctx, |pane, ctx| pane.activate_entry(entry.clone(), ctx)) .update(ctx, |pane, ctx| pane.activate_entry(entry.clone(), ctx))
{ {
return; return None;
} }
self.loading_entries.insert(entry.clone()); self.loading_entries.insert(entry.clone());
@ -224,10 +244,13 @@ impl WorkspaceView {
match self.workspace.update(ctx, |workspace, ctx| { match self.workspace.update(ctx, |workspace, ctx| {
workspace.open_entry(entry.clone(), ctx) workspace.open_entry(entry.clone(), ctx)
}) { }) {
Err(error) => error!("{}", error), Err(error) => {
error!("{}", error);
None
}
Ok(item) => { Ok(item) => {
let settings = self.settings.clone(); let settings = self.settings.clone();
ctx.spawn(item, move |me, item, ctx| { Some(ctx.spawn(item, move |me, item, ctx| {
me.loading_entries.remove(&entry); me.loading_entries.remove(&entry);
match item { match item {
Ok(item) => { Ok(item) => {
@ -238,8 +261,7 @@ impl WorkspaceView {
error!("{}", error); error!("{}", error);
} }
} }
}) }))
.detach();
} }
} }
} }
@ -423,13 +445,23 @@ mod tests {
let pane = app.read(|ctx| workspace_view.read(ctx).active_pane().clone()); let pane = app.read(|ctx| workspace_view.read(ctx).active_pane().clone());
// Open the first entry // Open the first entry
workspace_view.update(&mut app, |w, ctx| w.open_entry(file1.clone(), ctx)); workspace_view
pane.condition(&app, |pane, _| pane.items().len() == 1) .update(&mut app, |w, ctx| w.open_entry(file1.clone(), ctx))
.unwrap()
.await; .await;
app.read(|ctx| {
let pane = pane.read(ctx);
assert_eq!(
pane.active_item().unwrap().entry_id(ctx),
Some(file1.clone())
);
assert_eq!(pane.items().len(), 1);
});
// Open the second entry // Open the second entry
workspace_view.update(&mut app, |w, ctx| w.open_entry(file2.clone(), ctx)); workspace_view
pane.condition(&app, |pane, _| pane.items().len() == 2) .update(&mut app, |w, ctx| w.open_entry(file2.clone(), ctx))
.unwrap()
.await; .await;
app.read(|ctx| { app.read(|ctx| {
let pane = pane.read(ctx); let pane = pane.read(ctx);
@ -437,25 +469,38 @@ mod tests {
pane.active_item().unwrap().entry_id(ctx), pane.active_item().unwrap().entry_id(ctx),
Some(file2.clone()) Some(file2.clone())
); );
assert_eq!(pane.items().len(), 2);
}); });
// Open the first entry again // Open the first entry again. The existing pane item is activated.
workspace_view.update(&mut app, |w, ctx| w.open_entry(file1.clone(), ctx));
pane.condition(&app, move |pane, ctx| {
pane.active_item().unwrap().entry_id(ctx) == Some(file1.clone())
})
.await;
app.read(|ctx| {
assert_eq!(pane.read(ctx).items().len(), 2);
});
// Open the third entry twice concurrently
workspace_view.update(&mut app, |w, ctx| { workspace_view.update(&mut app, |w, ctx| {
w.open_entry(file3.clone(), ctx); assert!(w.open_entry(file1.clone(), ctx).is_none())
w.open_entry(file3.clone(), ctx);
}); });
pane.condition(&app, |pane, _| pane.items().len() == 3) app.read(|ctx| {
let pane = pane.read(ctx);
assert_eq!(
pane.active_item().unwrap().entry_id(ctx),
Some(file1.clone())
);
assert_eq!(pane.items().len(), 2);
});
// Open the third entry twice concurrently. Only one pane item is added.
workspace_view
.update(&mut app, |w, ctx| {
let task = w.open_entry(file3.clone(), ctx).unwrap();
assert!(w.open_entry(file3.clone(), ctx).is_none());
task
})
.await; .await;
app.read(|ctx| {
let pane = pane.read(ctx);
assert_eq!(
pane.active_item().unwrap().entry_id(ctx),
Some(file3.clone())
);
assert_eq!(pane.items().len(), 3);
});
}); });
} }
@ -479,23 +524,29 @@ mod tests {
// Open a file within an existing worktree. // Open a file within an existing worktree.
app.update(|ctx| { app.update(|ctx| {
workspace_view.update(ctx, |view, ctx| { workspace_view.update(ctx, |view, ctx| {
view.open_paths(&[dir1.path().join("a.txt")], ctx); view.open_paths(&[dir1.path().join("a.txt")], ctx)
});
});
workspace_view
.condition(&app, |view, ctx| {
view.active_pane()
.read(ctx)
.active_item()
.map_or(false, |item| item.title(&ctx) == "a.txt")
}) })
.await; })
.await;
app.read(|ctx| {
workspace_view
.read(ctx)
.active_pane()
.read(ctx)
.active_item()
.unwrap()
.title(ctx)
== "a.txt"
});
// Open a file outside of any existing worktree. // Open a file outside of any existing worktree.
app.update(|ctx| { app.update(|ctx| {
workspace_view.update(ctx, |view, ctx| { workspace_view.update(ctx, |view, ctx| {
view.open_paths(&[dir2.path().join("b.txt")], ctx); view.open_paths(&[dir2.path().join("b.txt")], ctx)
}); })
})
.await;
app.update(|ctx| {
let worktree_roots = workspace let worktree_roots = workspace
.read(ctx) .read(ctx)
.worktrees() .worktrees()
@ -509,14 +560,16 @@ mod tests {
.collect(), .collect(),
); );
}); });
workspace_view app.read(|ctx| {
.condition(&app, |view, ctx| { workspace_view
view.active_pane() .read(ctx)
.read(ctx) .active_pane()
.active_item() .read(ctx)
.map_or(false, |item| item.title(&ctx) == "b.txt") .active_item()
}) .unwrap()
.await; .title(ctx)
== "b.txt"
});
}); });
} }
@ -544,15 +597,16 @@ mod tests {
app.add_window(|ctx| WorkspaceView::new(workspace.clone(), settings, ctx)); app.add_window(|ctx| WorkspaceView::new(workspace.clone(), settings, ctx));
let pane_1 = app.read(|ctx| workspace_view.read(ctx).active_pane().clone()); let pane_1 = app.read(|ctx| workspace_view.read(ctx).active_pane().clone());
workspace_view.update(&mut app, |w, ctx| w.open_entry(file1.clone(), ctx)); workspace_view
{ .update(&mut app, |w, ctx| w.open_entry(file1.clone(), ctx))
let file1 = file1.clone(); .unwrap()
pane_1 .await;
.condition(&app, move |pane, ctx| { app.read(|ctx| {
pane.active_item().and_then(|i| i.entry_id(ctx)) == Some(file1.clone()) assert_eq!(
}) pane_1.read(ctx).active_item().unwrap().entry_id(ctx),
.await; Some(file1.clone())
} );
});
app.dispatch_action(window_id, vec![pane_1.id()], "pane:split_right", ()); app.dispatch_action(window_id, vec![pane_1.id()], "pane:split_right", ());
app.update(|ctx| { app.update(|ctx| {