@@ -469,7 +469,7 @@ mod tests {
workspace
.update(cx, |workspace, cx| {
workspace.open_paths(
- &[PathBuf::from("/root/dir1"), PathBuf::from("/root/dir2")],
+ vec![PathBuf::from("/root/dir1"), PathBuf::from("/root/dir2")],
cx,
)
})
@@ -49,7 +49,7 @@ pub fn new_journal_entry(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
let opened = workspace
.update(&mut cx, |workspace, cx| {
- workspace.open_paths(&[entry_path], cx)
+ workspace.open_paths(vec![entry_path], cx)
})
.await;
@@ -859,44 +859,49 @@ impl Workspace {
pub fn open_paths(
&mut self,
- abs_paths: &[PathBuf],
+ mut abs_paths: Vec<PathBuf>,
cx: &mut ViewContext<Self>,
) -> Task<Vec<Option<Result<Box<dyn ItemHandle>, Arc<anyhow::Error>>>>> {
- let entries = abs_paths
- .iter()
- .cloned()
- .map(|path| self.project_path_for_path(&path, cx))
- .collect::<Vec<_>>();
-
let fs = self.fs.clone();
- let tasks = abs_paths
- .iter()
- .cloned()
- .zip(entries.into_iter())
- .map(|(abs_path, project_path)| {
- cx.spawn(|this, mut cx| {
- let fs = fs.clone();
- async move {
- let project_path = project_path.await.ok()?;
- if fs.is_file(&abs_path).await {
- Some(
- this.update(&mut cx, |this, cx| this.open_path(project_path, cx))
+
+ // Sort the paths to ensure we add worktrees for parents before their children.
+ abs_paths.sort_unstable();
+ cx.spawn(|this, mut cx| async move {
+ let mut entries = Vec::new();
+ for path in &abs_paths {
+ entries.push(
+ this.update(&mut cx, |this, cx| this.project_path_for_path(path, cx))
+ .await
+ .ok(),
+ );
+ }
+
+ let tasks = abs_paths
+ .iter()
+ .cloned()
+ .zip(entries.into_iter())
+ .map(|(abs_path, project_path)| {
+ let this = this.clone();
+ cx.spawn(|mut cx| {
+ let fs = fs.clone();
+ async move {
+ let project_path = project_path?;
+ if fs.is_file(&abs_path).await {
+ Some(
+ this.update(&mut cx, |this, cx| {
+ this.open_path(project_path, cx)
+ })
.await,
- )
- } else {
- None
+ )
+ } else {
+ None
+ }
}
- }
+ })
})
- })
- .collect::<Vec<_>>();
+ .collect::<Vec<_>>();
- cx.foreground().spawn(async move {
- let mut items = Vec::new();
- for task in tasks {
- items.push(task.await);
- }
- items
+ futures::future::join_all(tasks).await
})
}
@@ -2152,7 +2157,9 @@ pub fn open_paths(
.1
});
- let task = workspace.update(cx, |workspace, cx| workspace.open_paths(abs_paths, cx));
+ let task = workspace.update(cx, |workspace, cx| {
+ workspace.open_paths(abs_paths.to_vec(), cx)
+ });
cx.spawn(|_| async move {
let items = task.await;
(workspace, items, is_new_workspace)
@@ -283,7 +283,7 @@ fn open_config_file(
workspace
.update(&mut cx, |workspace, cx| {
if workspace.project().read(cx).is_local() {
- workspace.open_paths(&[path.to_path_buf()], cx)
+ workspace.open_paths(vec![path.to_path_buf()], cx)
} else {
let (_, workspace) = cx.add_window((app_state.build_window_options)(), |cx| {
let project = Project::local(
@@ -296,7 +296,7 @@ fn open_config_file(
(app_state.build_workspace)(project, &app_state, cx)
});
workspace.update(cx, |workspace, cx| {
- workspace.open_paths(&[path.to_path_buf()], cx)
+ workspace.open_paths(vec![path.to_path_buf()], cx)
})
}
})
@@ -536,8 +536,10 @@ mod tests {
let fs = app_state.fs.as_fake();
fs.insert_dir("/dir1").await;
fs.insert_dir("/dir2").await;
+ fs.insert_dir("/dir3").await;
fs.insert_file("/dir1/a.txt", "".into()).await;
fs.insert_file("/dir2/b.txt", "".into()).await;
+ fs.insert_file("/dir3/c.txt", "".into()).await;
let params = cx.update(|cx| WorkspaceParams::local(&app_state, cx));
let (_, workspace) = cx.add_window(|cx| Workspace::new(¶ms, cx));
@@ -553,7 +555,9 @@ mod tests {
// Open a file within an existing worktree.
cx.update(|cx| {
- workspace.update(cx, |view, cx| view.open_paths(&["/dir1/a.txt".into()], cx))
+ workspace.update(cx, |view, cx| {
+ view.open_paths(vec!["/dir1/a.txt".into()], cx)
+ })
})
.await;
cx.read(|cx| {
@@ -575,7 +579,9 @@ mod tests {
// Open a file outside of any existing worktree.
cx.update(|cx| {
- workspace.update(cx, |view, cx| view.open_paths(&["/dir2/b.txt".into()], cx))
+ workspace.update(cx, |view, cx| {
+ view.open_paths(vec!["/dir2/b.txt".into()], cx)
+ })
})
.await;
cx.read(|cx| {
@@ -606,6 +612,42 @@ mod tests {
"b.txt"
);
});
+
+ // Ensure opening a directory and one of its children only adds one worktree.
+ cx.update(|cx| {
+ workspace.update(cx, |view, cx| {
+ view.open_paths(vec!["/dir3".into(), "/dir3/c.txt".into()], cx)
+ })
+ })
+ .await;
+ cx.read(|cx| {
+ let worktree_roots = workspace
+ .read(cx)
+ .worktrees(cx)
+ .map(|w| w.read(cx).as_local().unwrap().abs_path().as_ref())
+ .collect::<HashSet<_>>();
+ assert_eq!(
+ worktree_roots,
+ vec!["/dir1", "/dir2/b.txt", "/dir3"]
+ .into_iter()
+ .map(Path::new)
+ .collect(),
+ );
+ assert_eq!(
+ workspace
+ .read(cx)
+ .active_pane()
+ .read(cx)
+ .active_item()
+ .unwrap()
+ .to_any()
+ .downcast::<Editor>()
+ .unwrap()
+ .read(cx)
+ .title(cx),
+ "c.txt"
+ );
+ });
}
#[gpui::test]
@@ -627,7 +669,7 @@ mod tests {
// Open a file within an existing worktree.
cx.update(|cx| {
workspace.update(cx, |view, cx| {
- view.open_paths(&[PathBuf::from("/root/a.txt")], cx)
+ view.open_paths(vec![PathBuf::from("/root/a.txt")], cx)
})
})
.await;