Enable manual worktree organization (#11504)

Release Notes:

- Preserve order of worktrees in project
([#10883](https://github.com/zed-industries/zed/issues/10883)).
- Enable drag-and-drop reordering for project worktrees

Note: worktree order is not synced during collaboration but guests can
reorder their own project panels.

![Reordering
worktrees](https://github.com/zed-industries/zed/assets/1347854/1c63d83c-5d4e-4b55-b840-bfbf32521b2a)

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
This commit is contained in:
Elliot Thomas 2024-05-24 10:15:48 +01:00 committed by GitHub
parent 1e5389a2be
commit b9697fb487
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 479 additions and 54 deletions

View file

@ -2409,7 +2409,7 @@ async fn test_definition(cx: &mut gpui::TestAppContext) {
assert_eq!(definition.target.range.to_offset(target_buffer), 9..10);
assert_eq!(
list_worktrees(&project, cx),
[("/dir/b.rs".as_ref(), true), ("/dir/a.rs".as_ref(), false)]
[("/dir/a.rs".as_ref(), false), ("/dir/b.rs".as_ref(), true)],
);
drop(definition);
@ -4909,6 +4909,204 @@ async fn test_multiple_language_server_actions(cx: &mut gpui::TestAppContext) {
);
}
#[gpui::test]
async fn test_reordering_worktrees(cx: &mut gpui::TestAppContext) {
init_test(cx);
let fs = FakeFs::new(cx.executor());
fs.insert_tree(
"/dir",
json!({
"a.rs": "let a = 1;",
"b.rs": "let b = 2;",
"c.rs": "let c = 2;",
}),
)
.await;
let project = Project::test(
fs,
[
"/dir/a.rs".as_ref(),
"/dir/b.rs".as_ref(),
"/dir/c.rs".as_ref(),
],
cx,
)
.await;
// check the initial state and get the worktrees
let (worktree_a, worktree_b, worktree_c) = project.update(cx, |project, cx| {
let worktrees = project.visible_worktrees(cx).collect::<Vec<_>>();
assert_eq!(worktrees.len(), 3);
let worktree_a = worktrees[0].read(cx);
let worktree_b = worktrees[1].read(cx);
let worktree_c = worktrees[2].read(cx);
// check they start in the right order
assert_eq!(worktree_a.abs_path().to_str().unwrap(), "/dir/a.rs");
assert_eq!(worktree_b.abs_path().to_str().unwrap(), "/dir/b.rs");
assert_eq!(worktree_c.abs_path().to_str().unwrap(), "/dir/c.rs");
(
worktrees[0].clone(),
worktrees[1].clone(),
worktrees[2].clone(),
)
});
// move first worktree to after the second
// [a, b, c] -> [b, a, c]
project
.update(cx, |project, cx| {
let first = worktree_a.read(cx);
let second = worktree_b.read(cx);
project.move_worktree(first.id(), second.id(), cx)
})
.expect("moving first after second");
// check the state after moving
project.update(cx, |project, cx| {
let worktrees = project.visible_worktrees(cx).collect::<Vec<_>>();
assert_eq!(worktrees.len(), 3);
let first = worktrees[0].read(cx);
let second = worktrees[1].read(cx);
let third = worktrees[2].read(cx);
// check they are now in the right order
assert_eq!(first.abs_path().to_str().unwrap(), "/dir/b.rs");
assert_eq!(second.abs_path().to_str().unwrap(), "/dir/a.rs");
assert_eq!(third.abs_path().to_str().unwrap(), "/dir/c.rs");
});
// move the second worktree to before the first
// [b, a, c] -> [a, b, c]
project
.update(cx, |project, cx| {
let second = worktree_a.read(cx);
let first = worktree_b.read(cx);
project.move_worktree(first.id(), second.id(), cx)
})
.expect("moving second before first");
// check the state after moving
project.update(cx, |project, cx| {
let worktrees = project.visible_worktrees(cx).collect::<Vec<_>>();
assert_eq!(worktrees.len(), 3);
let first = worktrees[0].read(cx);
let second = worktrees[1].read(cx);
let third = worktrees[2].read(cx);
// check they are now in the right order
assert_eq!(first.abs_path().to_str().unwrap(), "/dir/a.rs");
assert_eq!(second.abs_path().to_str().unwrap(), "/dir/b.rs");
assert_eq!(third.abs_path().to_str().unwrap(), "/dir/c.rs");
});
// move the second worktree to after the third
// [a, b, c] -> [a, c, b]
project
.update(cx, |project, cx| {
let second = worktree_b.read(cx);
let third = worktree_c.read(cx);
project.move_worktree(second.id(), third.id(), cx)
})
.expect("moving second after third");
// check the state after moving
project.update(cx, |project, cx| {
let worktrees = project.visible_worktrees(cx).collect::<Vec<_>>();
assert_eq!(worktrees.len(), 3);
let first = worktrees[0].read(cx);
let second = worktrees[1].read(cx);
let third = worktrees[2].read(cx);
// check they are now in the right order
assert_eq!(first.abs_path().to_str().unwrap(), "/dir/a.rs");
assert_eq!(second.abs_path().to_str().unwrap(), "/dir/c.rs");
assert_eq!(third.abs_path().to_str().unwrap(), "/dir/b.rs");
});
// move the third worktree to before the second
// [a, c, b] -> [a, b, c]
project
.update(cx, |project, cx| {
let third = worktree_c.read(cx);
let second = worktree_b.read(cx);
project.move_worktree(third.id(), second.id(), cx)
})
.expect("moving third before second");
// check the state after moving
project.update(cx, |project, cx| {
let worktrees = project.visible_worktrees(cx).collect::<Vec<_>>();
assert_eq!(worktrees.len(), 3);
let first = worktrees[0].read(cx);
let second = worktrees[1].read(cx);
let third = worktrees[2].read(cx);
// check they are now in the right order
assert_eq!(first.abs_path().to_str().unwrap(), "/dir/a.rs");
assert_eq!(second.abs_path().to_str().unwrap(), "/dir/b.rs");
assert_eq!(third.abs_path().to_str().unwrap(), "/dir/c.rs");
});
// move the first worktree to after the third
// [a, b, c] -> [b, c, a]
project
.update(cx, |project, cx| {
let first = worktree_a.read(cx);
let third = worktree_c.read(cx);
project.move_worktree(first.id(), third.id(), cx)
})
.expect("moving first after third");
// check the state after moving
project.update(cx, |project, cx| {
let worktrees = project.visible_worktrees(cx).collect::<Vec<_>>();
assert_eq!(worktrees.len(), 3);
let first = worktrees[0].read(cx);
let second = worktrees[1].read(cx);
let third = worktrees[2].read(cx);
// check they are now in the right order
assert_eq!(first.abs_path().to_str().unwrap(), "/dir/b.rs");
assert_eq!(second.abs_path().to_str().unwrap(), "/dir/c.rs");
assert_eq!(third.abs_path().to_str().unwrap(), "/dir/a.rs");
});
// move the third worktree to before the first
// [b, c, a] -> [a, b, c]
project
.update(cx, |project, cx| {
let third = worktree_a.read(cx);
let first = worktree_b.read(cx);
project.move_worktree(third.id(), first.id(), cx)
})
.expect("moving third before first");
// check the state after moving
project.update(cx, |project, cx| {
let worktrees = project.visible_worktrees(cx).collect::<Vec<_>>();
assert_eq!(worktrees.len(), 3);
let first = worktrees[0].read(cx);
let second = worktrees[1].read(cx);
let third = worktrees[2].read(cx);
// check they are now in the right order
assert_eq!(first.abs_path().to_str().unwrap(), "/dir/a.rs");
assert_eq!(second.abs_path().to_str().unwrap(), "/dir/b.rs");
assert_eq!(third.abs_path().to_str().unwrap(), "/dir/c.rs");
});
}
async fn search(
project: &Model<Project>,
query: SearchQuery,