Compare commits

...

1 Commits

Author SHA1 Message Date
Nathan Sobo
2de61e1b4b Allow opening a new window for secondary worktrees
Previously, running `zed foo` when `foo` was already open as a
worktree in an existing window would reuse that window, even if `foo`
was not the primary worktree. This made it impossible to open a
dedicated window for a directory that was already added as a secondary
worktree elsewhere.

Now, we only reuse an existing window if the path matches its primary
worktree (the first visible worktree, typically the directory originally
opened to create that window). This allows users to run `zed foo` and
get a new window where `foo` is the primary worktree, even if `foo`
is already open as a secondary worktree in another window.
2025-12-12 01:27:21 -07:00
2 changed files with 95 additions and 10 deletions

View File

@@ -7574,7 +7574,6 @@ pub fn open_paths(
> {
let abs_paths = abs_paths.to_vec();
let mut existing = None;
let mut best_match = None;
let mut open_visible = OpenVisible::All;
#[cfg(target_os = "windows")]
let wsl_path = abs_paths
@@ -7593,16 +7592,23 @@ pub fn open_paths(
cx.update(|cx| {
for window in local_workspace_windows(cx) {
if let Ok(workspace) = window.read(cx) {
let m = workspace.project.read(cx).visibility_for_paths(
&abs_paths,
&all_metadatas,
open_options.open_new_workspace == None,
cx,
);
if m > best_match {
// Only reuse an existing window if the paths match its primary worktree
// (the first visible worktree, typically the directory originally opened).
// This allows `zed foo` to open a new window even if `foo` is already
// open as a secondary worktree in another window.
let dominated_by_this_window = workspace
.project
.read(cx)
.visible_worktrees(cx)
.next()
.and_then(|w| w.read(cx).as_local().map(|w| w.abs_path()))
.is_some_and(|primary_path| {
abs_paths.iter().any(|path| path.starts_with(&primary_path))
});
if dominated_by_this_window {
existing = Some(window);
best_match = m;
} else if best_match.is_none()
break;
} else if existing.is_none()
&& open_options.open_new_workspace == Some(false)
{
existing = Some(window)

View File

@@ -753,6 +753,85 @@ mod tests {
.unwrap();
}
#[gpui::test]
async fn test_open_secondary_worktree_in_new_window(cx: &mut TestAppContext) {
let app_state = init_test(cx);
app_state
.fs
.as_fake()
.insert_tree(
path!("/root"),
json!({
"dir1": {
"file1.txt": "content1",
},
"dir2": {
"file2.txt": "content2",
},
}),
)
.await;
assert_eq!(cx.windows().len(), 0);
// Open dir1 as the first worktree
open_workspace_file(path!("/root/dir1"), None, app_state.clone(), cx).await;
assert_eq!(cx.windows().len(), 1);
let workspace_1 = cx.windows()[0].downcast::<Workspace>().unwrap();
// Add dir2 as a secondary worktree to the same window
workspace_1
.update(cx, |workspace, _, cx| {
workspace.project().update(cx, |project, cx| {
project.find_or_create_worktree(path!("/root/dir2"), true, cx)
})
})
.unwrap()
.await
.unwrap();
// Verify both worktrees are in the same window
workspace_1
.update(cx, |workspace, _, cx| {
let worktrees: Vec<_> = workspace.worktrees(cx).collect();
assert_eq!(worktrees.len(), 2, "Should have two worktrees");
})
.unwrap();
assert_eq!(cx.windows().len(), 1);
// Now open dir2 via CLI - this should open a NEW window since dir2 is not
// the first worktree of the existing window
open_workspace_file(path!("/root/dir2"), None, app_state.clone(), cx).await;
assert_eq!(
cx.windows().len(),
2,
"Opening a secondary worktree should create a new window"
);
// Verify the new window has dir2 as its first (and only) worktree
let workspace_2 = cx.windows()[1].downcast::<Workspace>().unwrap();
workspace_2
.update(cx, |workspace, _, cx| {
let worktrees: Vec<_> = workspace.worktrees(cx).collect();
assert_eq!(worktrees.len(), 1, "New window should have one worktree");
assert_eq!(worktrees[0].read(cx).root_name(), "dir2");
})
.unwrap();
// Opening dir1 should still reuse the first window since it's the first worktree there
open_workspace_file(path!("/root/dir1/file1.txt"), None, app_state.clone(), cx).await;
assert_eq!(
cx.windows().len(),
2,
"Opening a file in the first worktree should reuse the existing window"
);
}
#[gpui::test]
async fn test_open_workspace_with_nonexistent_files(cx: &mut TestAppContext) {
let app_state = init_test(cx);