diff --git a/crates/agent_ui/src/agent_panel.rs b/crates/agent_ui/src/agent_panel.rs index f9a136c10fe26ce1763fbde52c532f065e097463..23dc1dfcbc086f4b145bb5372929d9aa32f30fc5 100644 --- a/crates/agent_ui/src/agent_panel.rs +++ b/crates/agent_ui/src/agent_panel.rs @@ -87,7 +87,7 @@ use ui::{ use util::{ResultExt as _, debug_panic}; use workspace::{ CollaboratorId, DraggedSelection, DraggedSidebar, DraggedTab, FocusWorkspaceSidebar, - MultiWorkspace, SIDEBAR_RESIZE_HANDLE_SIZE, ToggleWorkspaceSidebar, ToggleZoom, + MultiWorkspace, OpenResult, SIDEBAR_RESIZE_HANDLE_SIZE, ToggleWorkspaceSidebar, ToggleZoom, ToolbarItemView, Workspace, WorkspaceId, dock::{DockPosition, Panel, PanelEvent}, multi_workspace_enabled, @@ -3025,21 +3025,16 @@ impl AgentPanel { workspace.set_dock_structure(dock_structure, window, cx); })); - let (new_window_handle, _) = cx + let OpenResult { + window: new_window_handle, + workspace: new_workspace, + .. + } = cx .update(|_window, cx| { Workspace::new_local(all_paths, app_state, window_handle, None, init, false, cx) })? .await?; - let new_workspace = new_window_handle.update(cx, |multi_workspace, _window, _cx| { - let workspaces = multi_workspace.workspaces(); - workspaces.last().cloned() - })?; - - let Some(new_workspace) = new_workspace else { - anyhow::bail!("New workspace was not added to MultiWorkspace"); - }; - let panels_task = new_window_handle.update(cx, |_, _, cx| { new_workspace.update(cx, |workspace, _cx| workspace.take_panels_task()) })?; diff --git a/crates/agent_ui/src/sidebar.rs b/crates/agent_ui/src/sidebar.rs index 7d7779e75504a93c7923ba26ec87e4fce4bbceb9..6dc684b3d30737dbce1b7d1c9c706341cf4ef11f 100644 --- a/crates/agent_ui/src/sidebar.rs +++ b/crates/agent_ui/src/sidebar.rs @@ -99,13 +99,19 @@ impl From<&ActiveThreadInfo> for acp_thread::AgentSessionInfo { } } +#[derive(Clone)] +enum ThreadEntryWorkspace { + Open(Entity), + Closed(PathList), +} + #[derive(Clone)] struct ThreadEntry { session_info: acp_thread::AgentSessionInfo, icon: IconName, icon_from_external_svg: Option, status: AgentThreadStatus, - workspace: Entity, + workspace: ThreadEntryWorkspace, is_live: bool, is_background: bool, highlight_positions: Vec, @@ -528,7 +534,8 @@ impl Sidebar { // main repo's header instead of getting their own. let mut main_repo_workspace: HashMap, usize> = HashMap::new(); let mut absorbed: HashMap = HashMap::new(); - let mut pending: HashMap, Vec<(usize, SharedString)>> = HashMap::new(); + let mut pending: HashMap, Vec<(usize, SharedString, Arc)>> = HashMap::new(); + let mut absorbed_workspace_by_path: HashMap, usize> = HashMap::new(); for (i, workspace) in workspaces.iter().enumerate() { for snapshot in root_repository_snapshots(workspace, cx) { @@ -537,8 +544,9 @@ impl Sidebar { .entry(snapshot.work_directory_abs_path.clone()) .or_insert(i); if let Some(waiting) = pending.remove(&snapshot.work_directory_abs_path) { - for (ws_idx, name) in waiting { + for (ws_idx, name, ws_path) in waiting { absorbed.insert(ws_idx, (i, name)); + absorbed_workspace_by_path.insert(ws_path, ws_idx); } } } else { @@ -553,11 +561,13 @@ impl Sidebar { main_repo_workspace.get(&snapshot.original_repo_abs_path) { absorbed.insert(i, (main_idx, name)); + absorbed_workspace_by_path + .insert(snapshot.work_directory_abs_path.clone(), i); } else { pending .entry(snapshot.original_repo_abs_path.clone()) .or_default() - .push((i, name)); + .push((i, name, snapshot.work_directory_abs_path.clone())); } } } @@ -586,7 +596,7 @@ impl Sidebar { icon: IconName::ZedAgent, icon_from_external_svg: None, status: AgentThreadStatus::default(), - workspace: workspace.clone(), + workspace: ThreadEntryWorkspace::Open(workspace.clone()), is_live: false, is_background: false, highlight_positions: Vec::new(), @@ -599,7 +609,8 @@ impl Sidebar { // Load threads from linked git worktrees of this workspace's repos. if let Some(ref thread_store) = thread_store { - let mut linked_worktree_queries: Vec<(PathList, SharedString)> = Vec::new(); + let mut linked_worktree_queries: Vec<(PathList, SharedString, Arc)> = + Vec::new(); for snapshot in root_repository_snapshots(workspace, cx) { if snapshot.work_directory_abs_path != snapshot.original_repo_abs_path { continue; @@ -614,11 +625,20 @@ impl Sidebar { linked_worktree_queries.push(( PathList::new(std::slice::from_ref(&git_worktree.path)), name.into(), + Arc::from(git_worktree.path.as_path()), )); } } - for (worktree_path_list, worktree_name) in &linked_worktree_queries { + for (worktree_path_list, worktree_name, worktree_path) in + &linked_worktree_queries + { + let target_workspace = + match absorbed_workspace_by_path.get(worktree_path.as_ref()) { + Some(&idx) => ThreadEntryWorkspace::Open(workspaces[idx].clone()), + None => ThreadEntryWorkspace::Closed(worktree_path_list.clone()), + }; + for meta in thread_store.read(cx).threads_for_paths(worktree_path_list) { if !seen_session_ids.insert(meta.id.clone()) { continue; @@ -628,7 +648,7 @@ impl Sidebar { icon: IconName::ZedAgent, icon_from_external_svg: None, status: AgentThreadStatus::default(), - workspace: workspace.clone(), + workspace: target_workspace.clone(), is_live: false, is_background: false, highlight_positions: Vec::new(), @@ -1347,8 +1367,20 @@ impl Sidebar { } ListEntry::Thread(thread) => { let session_info = thread.session_info.clone(); - let workspace = thread.workspace.clone(); - self.activate_thread(session_info, &workspace, window, cx); + match &thread.workspace { + ThreadEntryWorkspace::Open(workspace) => { + let workspace = workspace.clone(); + self.activate_thread(session_info, &workspace, window, cx); + } + ThreadEntryWorkspace::Closed(path_list) => { + self.open_workspace_and_activate_thread( + session_info, + path_list.clone(), + window, + cx, + ); + } + } } ListEntry::ViewMore { path_list, @@ -1403,6 +1435,32 @@ impl Sidebar { } } + fn open_workspace_and_activate_thread( + &mut self, + session_info: acp_thread::AgentSessionInfo, + path_list: PathList, + window: &mut Window, + cx: &mut Context, + ) { + let Some(multi_workspace) = self.multi_workspace.upgrade() else { + return; + }; + + let paths: Vec = + path_list.paths().iter().map(|p| p.to_path_buf()).collect(); + + let open_task = multi_workspace.update(cx, |mw, cx| mw.open_project(paths, window, cx)); + + cx.spawn_in(window, async move |this, cx| { + let workspace = open_task.await?; + this.update_in(cx, |this, window, cx| { + this.activate_thread(session_info, &workspace, window, cx); + })?; + anyhow::Ok(()) + }) + .detach_and_log_err(cx); + } + fn expand_selected_entry( &mut self, _: &ExpandSelectedEntry, @@ -1480,7 +1538,7 @@ impl Sidebar { .clone() .unwrap_or_else(|| "Untitled".into()); let session_info = thread.session_info.clone(); - let workspace = thread.workspace.clone(); + let thread_workspace = thread.workspace.clone(); let id = SharedString::from(format!("thread-entry-{}", ix)); @@ -1533,7 +1591,19 @@ impl Sidebar { .docked_right(docked_right) .on_click(cx.listener(move |this, _, window, cx| { this.selection = None; - this.activate_thread(session_info.clone(), &workspace, window, cx); + match &thread_workspace { + ThreadEntryWorkspace::Open(workspace) => { + this.activate_thread(session_info.clone(), workspace, window, cx); + } + ThreadEntryWorkspace::Closed(path_list) => { + this.open_workspace_and_activate_thread( + session_info.clone(), + path_list.clone(), + window, + cx, + ); + } + } })) .into_any_element() } @@ -2447,7 +2517,7 @@ mod tests { icon: IconName::ZedAgent, icon_from_external_svg: None, status: AgentThreadStatus::Completed, - workspace: workspace.clone(), + workspace: ThreadEntryWorkspace::Open(workspace.clone()), is_live: false, is_background: false, highlight_positions: Vec::new(), @@ -2468,7 +2538,7 @@ mod tests { icon: IconName::ZedAgent, icon_from_external_svg: None, status: AgentThreadStatus::Running, - workspace: workspace.clone(), + workspace: ThreadEntryWorkspace::Open(workspace.clone()), is_live: true, is_background: false, highlight_positions: Vec::new(), @@ -2489,7 +2559,7 @@ mod tests { icon: IconName::ZedAgent, icon_from_external_svg: None, status: AgentThreadStatus::Error, - workspace: workspace.clone(), + workspace: ThreadEntryWorkspace::Open(workspace.clone()), is_live: true, is_background: false, highlight_positions: Vec::new(), @@ -2510,7 +2580,7 @@ mod tests { icon: IconName::ZedAgent, icon_from_external_svg: None, status: AgentThreadStatus::WaitingForConfirmation, - workspace: workspace.clone(), + workspace: ThreadEntryWorkspace::Open(workspace.clone()), is_live: false, is_background: false, highlight_positions: Vec::new(), @@ -2531,7 +2601,7 @@ mod tests { icon: IconName::ZedAgent, icon_from_external_svg: None, status: AgentThreadStatus::Completed, - workspace: workspace.clone(), + workspace: ThreadEntryWorkspace::Open(workspace.clone()), is_live: true, is_background: true, highlight_positions: Vec::new(), @@ -4305,4 +4375,222 @@ mod tests { vec!["v [project]", " Thread A {wt-feature-a}",] ); } + + #[gpui::test] + async fn test_clicking_worktree_thread_opens_workspace_when_none_exists( + cx: &mut TestAppContext, + ) { + init_test(cx); + let fs = FakeFs::new(cx.executor()); + + fs.insert_tree( + "/project", + serde_json::json!({ + ".git": { + "worktrees": { + "feature-a": { + "commondir": "../../", + "HEAD": "ref: refs/heads/feature-a", + }, + }, + }, + "src": {}, + }), + ) + .await; + + fs.insert_tree( + "/wt-feature-a", + serde_json::json!({ + ".git": "gitdir: /project/.git/worktrees/feature-a", + "src": {}, + }), + ) + .await; + + fs.with_git_state(std::path::Path::new("/project/.git"), false, |state| { + state.worktrees.push(git::repository::Worktree { + path: std::path::PathBuf::from("/wt-feature-a"), + ref_name: "refs/heads/feature-a".into(), + sha: "aaa".into(), + }); + }) + .unwrap(); + + cx.update(|cx| ::set_global(fs.clone(), cx)); + + // Only open the main repo — no workspace for the worktree. + let main_project = project::Project::test(fs.clone(), ["/project".as_ref()], cx).await; + main_project + .update(cx, |p, cx| p.git_scans_complete(cx)) + .await; + + let (multi_workspace, cx) = cx.add_window_view(|window, cx| { + MultiWorkspace::test_new(main_project.clone(), window, cx) + }); + let sidebar = setup_sidebar(&multi_workspace, cx); + + // Save a thread for the worktree path (no workspace for it). + let paths_wt = PathList::new(&[std::path::PathBuf::from("/wt-feature-a")]); + save_named_thread("thread-wt", "WT Thread", &paths_wt, cx).await; + + multi_workspace.update_in(cx, |_, _window, cx| cx.notify()); + cx.run_until_parked(); + + // Thread should appear under the main repo with a worktree chip. + assert_eq!( + visible_entries_as_strings(&sidebar, cx), + vec!["v [project]", " WT Thread {wt-feature-a}"], + ); + + // Only 1 workspace should exist. + assert_eq!( + multi_workspace.read_with(cx, |mw, _| mw.workspaces().len()), + 1, + ); + + // Focus the sidebar and select the worktree thread. + open_and_focus_sidebar(&sidebar, cx); + sidebar.update_in(cx, |sidebar, _window, _cx| { + sidebar.selection = Some(1); // index 0 is header, 1 is the thread + }); + + // Confirm to open the worktree thread. + cx.dispatch_action(Confirm); + cx.run_until_parked(); + + // A new workspace should have been created for the worktree path. + let new_workspace = multi_workspace.read_with(cx, |mw, _| { + assert_eq!( + mw.workspaces().len(), + 2, + "confirming a worktree thread without a workspace should open one", + ); + mw.workspaces()[1].clone() + }); + + let (new_path_list, _) = new_workspace.read_with(cx, |_, cx| { + workspace_path_list_and_label(&new_workspace, cx) + }); + assert_eq!( + new_path_list, + PathList::new(&[std::path::PathBuf::from("/wt-feature-a")]), + "the new workspace should have been opened for the worktree path", + ); + } + + #[gpui::test] + async fn test_clicking_absorbed_worktree_thread_activates_worktree_workspace( + cx: &mut TestAppContext, + ) { + init_test(cx); + let fs = FakeFs::new(cx.executor()); + + fs.insert_tree( + "/project", + serde_json::json!({ + ".git": { + "worktrees": { + "feature-a": { + "commondir": "../../", + "HEAD": "ref: refs/heads/feature-a", + }, + }, + }, + "src": {}, + }), + ) + .await; + + fs.insert_tree( + "/wt-feature-a", + serde_json::json!({ + ".git": "gitdir: /project/.git/worktrees/feature-a", + "src": {}, + }), + ) + .await; + + fs.with_git_state(std::path::Path::new("/project/.git"), false, |state| { + state.worktrees.push(git::repository::Worktree { + path: std::path::PathBuf::from("/wt-feature-a"), + ref_name: "refs/heads/feature-a".into(), + sha: "aaa".into(), + }); + }) + .unwrap(); + + cx.update(|cx| ::set_global(fs.clone(), cx)); + + let main_project = project::Project::test(fs.clone(), ["/project".as_ref()], cx).await; + let worktree_project = + project::Project::test(fs.clone(), ["/wt-feature-a".as_ref()], cx).await; + + main_project + .update(cx, |p, cx| p.git_scans_complete(cx)) + .await; + worktree_project + .update(cx, |p, cx| p.git_scans_complete(cx)) + .await; + + let (multi_workspace, cx) = cx.add_window_view(|window, cx| { + MultiWorkspace::test_new(main_project.clone(), window, cx) + }); + + let worktree_workspace = multi_workspace.update_in(cx, |mw, window, cx| { + mw.test_add_workspace(worktree_project.clone(), window, cx) + }); + + // Activate the main workspace before setting up the sidebar. + multi_workspace.update_in(cx, |mw, window, cx| { + mw.activate_index(0, window, cx); + }); + + let sidebar = setup_sidebar(&multi_workspace, cx); + + let paths_main = PathList::new(&[std::path::PathBuf::from("/project")]); + let paths_wt = PathList::new(&[std::path::PathBuf::from("/wt-feature-a")]); + save_named_thread("thread-main", "Main Thread", &paths_main, cx).await; + save_named_thread("thread-wt", "WT Thread", &paths_wt, cx).await; + + multi_workspace.update_in(cx, |_, _window, cx| cx.notify()); + cx.run_until_parked(); + + // The worktree workspace should be absorbed under the main repo. + let entries = visible_entries_as_strings(&sidebar, cx); + assert_eq!(entries.len(), 3); + assert_eq!(entries[0], "v [project]"); + assert!(entries.contains(&" Main Thread".to_string())); + assert!(entries.contains(&" WT Thread {wt-feature-a}".to_string())); + + let wt_thread_index = entries + .iter() + .position(|e| e.contains("WT Thread")) + .expect("should find the worktree thread entry"); + + assert_eq!( + multi_workspace.read_with(cx, |mw, _| mw.active_workspace_index()), + 0, + "main workspace should be active initially" + ); + + // Focus the sidebar and select the absorbed worktree thread. + open_and_focus_sidebar(&sidebar, cx); + sidebar.update_in(cx, |sidebar, _window, _cx| { + sidebar.selection = Some(wt_thread_index); + }); + + // Confirm to activate the worktree thread. + cx.dispatch_action(Confirm); + cx.run_until_parked(); + + // The worktree workspace should now be active, not the main one. + let active_workspace = multi_workspace.read_with(cx, |mw, _| { + mw.workspaces()[mw.active_workspace_index()].clone() + }); + assert_eq!( + active_workspace, worktree_workspace, + "clicking an absorbed worktree thread should activate the worktree workspace" + ); + } } diff --git a/crates/journal/src/journal.rs b/crates/journal/src/journal.rs index ba97bcf66a77659fb3196ba45ebb3f831452e008..b8028c79b3d5da415a52d946d7601d8cbb40f738 100644 --- a/crates/journal/src/journal.rs +++ b/crates/journal/src/journal.rs @@ -9,7 +9,7 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; -use workspace::{AppState, OpenVisible, Workspace}; +use workspace::{AppState, OpenResult, OpenVisible, Workspace}; actions!( journal, @@ -107,7 +107,10 @@ pub fn new_journal_entry(workspace: &Workspace, window: &mut Window, cx: &mut Ap .spawn(cx, async move |cx| { let (journal_dir, entry_path) = create_entry.await?; let opened = if open_new_workspace { - let (new_workspace, _) = cx + let OpenResult { + window: new_workspace, + .. + } = cx .update(|_window, cx| { workspace::open_paths( &[journal_dir], diff --git a/crates/recent_projects/src/recent_projects.rs b/crates/recent_projects/src/recent_projects.rs index b5ae7b048276f671da48beaa52b0db5fbcdda61a..c9720af2aba7f4a27adf8e40745bb05012c4dafd 100644 --- a/crates/recent_projects/src/recent_projects.rs +++ b/crates/recent_projects/src/recent_projects.rs @@ -935,7 +935,14 @@ impl PickerDelegate for RecentProjectsDelegate { } return; } else { - workspace.open_workspace_for_paths(false, paths, window, cx) + workspace + .open_workspace_for_paths(false, paths, window, cx) + .detach_and_prompt_err( + "Failed to open project", + window, + cx, + |_, _, _| None, + ); } } SerializedWorkspaceLocation::Remote(mut connection) => { @@ -964,14 +971,14 @@ impl PickerDelegate for RecentProjectsDelegate { ) .await }) + .detach_and_prompt_err( + "Failed to open project", + window, + cx, + |_, _, _| None, + ); } } - .detach_and_prompt_err( - "Failed to open project", - window, - cx, - |_, _, _| None, - ); }); cx.emit(DismissEvent); } diff --git a/crates/workspace/src/multi_workspace.rs b/crates/workspace/src/multi_workspace.rs index adfc62a2bd210b4da24202d734ba9f9eedd17aef..cb60978d85220baa8519a7a1816434b4c06eb0c3 100644 --- a/crates/workspace/src/multi_workspace.rs +++ b/crates/workspace/src/multi_workspace.rs @@ -498,7 +498,7 @@ impl MultiWorkspace { paths: Vec, window: &mut Window, cx: &mut Context, - ) -> Task> { + ) -> Task>> { let workspace = self.workspace().clone(); if multi_workspace_enabled(cx) { @@ -519,7 +519,7 @@ impl MultiWorkspace { })? .await } else { - Ok(()) + Ok(workspace) } }) } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 949dc127a7465c4cf3941ee4c4982fad37d06281..19d02e9a8a6742ba04bc52a68568cb2bf994608a 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -659,7 +659,7 @@ fn prompt_and_open_paths(app_state: Arc, options: PathPromptOptions, c } else { let task = Workspace::new_local(Vec::new(), app_state.clone(), None, None, None, true, cx); cx.spawn(async move |cx| { - let (window, _) = task.await?; + let OpenResult { window, .. } = task.await?; window.update(cx, |multi_workspace, window, cx| { window.activate_window(); let workspace = multi_workspace.workspace().clone(); @@ -1752,12 +1752,7 @@ impl Workspace { init: Option) + Send>>, activate: bool, cx: &mut App, - ) -> Task< - anyhow::Result<( - WindowHandle, - Vec>>>, - )>, - > { + ) -> Task> { let project_handle = Project::local( app_state.client.clone(), app_state.node_runtime.clone(), @@ -1997,7 +1992,11 @@ impl Workspace { }); }) .log_err(); - Ok((window, opened_items)) + Ok(OpenResult { + window, + workspace, + opened_items, + }) }) } @@ -2685,7 +2684,10 @@ impl Workspace { cx, ); cx.spawn_in(window, async move |_vh, cx| { - let (multi_workspace_window, _) = task.await?; + let OpenResult { + window: multi_workspace_window, + .. + } = task.await?; multi_workspace_window.update(cx, |multi_workspace, window, cx| { let workspace = multi_workspace.workspace().clone(); workspace.update(cx, |workspace, cx| callback(workspace, window, cx)) @@ -2723,7 +2725,10 @@ impl Workspace { cx, ); cx.spawn_in(window, async move |_vh, cx| { - let (multi_workspace_window, _) = task.await?; + let OpenResult { + window: multi_workspace_window, + .. + } = task.await?; multi_workspace_window.update(cx, |multi_workspace, window, cx| { let workspace = multi_workspace.workspace().clone(); workspace.update(cx, |workspace, cx| callback(workspace, window, cx)) @@ -3102,7 +3107,7 @@ impl Workspace { paths: Vec, window: &mut Window, cx: &mut Context, - ) -> Task> { + ) -> Task>> { let window_handle = window.window_handle().downcast::(); let is_remote = self.project.read(cx).is_via_collab(); let has_worktree = self.project.read(cx).worktrees(cx).next().is_some(); @@ -3118,19 +3123,20 @@ impl Workspace { let app_state = self.app_state.clone(); cx.spawn(async move |_, cx| { - cx.update(|cx| { - open_paths( - &paths, - app_state, - OpenOptions { - replace_window: window_to_replace, - ..Default::default() - }, - cx, - ) - }) - .await?; - Ok(()) + let OpenResult { workspace, .. } = cx + .update(|cx| { + open_paths( + &paths, + app_state, + OpenOptions { + replace_window: window_to_replace, + ..Default::default() + }, + cx, + ) + }) + .await?; + Ok(workspace) }) } @@ -8210,7 +8216,7 @@ pub async fn restore_multiworkspace( cx.update(|cx| open_workspace_by_id(first.workspace_id, app_state.clone(), None, cx)) .await? } else { - let (window, _items) = cx + let OpenResult { window, .. } = cx .update(|cx| { Workspace::new_local( first.paths.paths().to_vec(), @@ -8503,7 +8509,10 @@ pub fn join_channel( let mut active_window = requesting_window.or_else(|| activate_any_workspace_window(cx)); if active_window.is_none() { // no open workspaces, make one to show the error in (blergh) - let (window_handle, _) = cx + let OpenResult { + window: window_handle, + .. + } = cx .update(|cx| { Workspace::new_local( vec![], @@ -8759,6 +8768,14 @@ pub struct OpenOptions { pub env: Option>, } +/// The result of opening a workspace via [`open_paths`], [`Workspace::new_local`], +/// or [`Workspace::open_workspace_for_paths`]. +pub struct OpenResult { + pub window: WindowHandle, + pub workspace: Entity, + pub opened_items: Vec>>>, +} + /// Opens a workspace by its database ID, used for restoring empty workspaces with unsaved content. pub fn open_workspace_by_id( workspace_id: WorkspaceId, @@ -8878,12 +8895,7 @@ pub fn open_paths( app_state: Arc, open_options: OpenOptions, cx: &mut App, -) -> Task< - anyhow::Result<( - WindowHandle, - Vec>>>, - )>, -> { +) -> Task> { let abs_paths = abs_paths.to_vec(); #[cfg(target_os = "windows")] let wsl_path = abs_paths @@ -8962,7 +8974,7 @@ pub fn open_paths( }); }); - Ok((existing, open_task)) + Ok(OpenResult { window: existing, workspace: target_workspace, opened_items: open_task }) } else { let result = cx .update(move |cx| { @@ -8978,8 +8990,8 @@ pub fn open_paths( }) .await; - if let Ok((ref window_handle, _)) = result { - window_handle + if let Ok(ref result) = result { + result.window .update(cx, |_, window, _cx| { window.activate_window(); }) @@ -8991,9 +9003,9 @@ pub fn open_paths( #[cfg(target_os = "windows")] if let Some(util::paths::WslPath{distro, path}) = wsl_path - && let Ok((multi_workspace_window, _)) = &result + && let Ok(ref result) = result { - multi_workspace_window + result.window .update(cx, move |multi_workspace, _window, cx| { struct OpenInWsl; let workspace = multi_workspace.workspace().clone(); @@ -9040,7 +9052,7 @@ pub fn open_new( cx, ); cx.spawn(async move |cx| { - let (window, _opened_paths) = task.await?; + let OpenResult { window, .. } = task.await?; window .update(cx, |_, window, _cx| { window.activate_window(); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 511b0edc6ac168fa47b52e66c9632487de86acf4..76930f627ecd6b8bf37729d9b48c0bacb300ecfb 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -3442,7 +3442,11 @@ mod tests { PathBuf::from(path!("/root/.git/HEAD")), PathBuf::from(path!("/root/excluded_dir/ignored_subdir")), ]; - let (opened_workspace, new_items) = cx + let workspace::OpenResult { + window: opened_workspace, + opened_items: new_items, + .. + } = cx .update(|cx| { workspace::open_paths( &paths_to_open, @@ -5866,7 +5870,9 @@ mod tests { // // Window A: workspace for dir1, workspace for dir2 // Window B: workspace for dir3 - let (window_a, _) = cx + let workspace::OpenResult { + window: window_a, .. + } = cx .update(|cx| { Workspace::new_local( vec![dir1.into()], @@ -5890,7 +5896,9 @@ mod tests { .expect("failed to open second workspace into window A"); cx.run_until_parked(); - let (window_b, _) = cx + let workspace::OpenResult { + window: window_b, .. + } = cx .update(|cx| { Workspace::new_local( vec![dir3.into()], diff --git a/crates/zed/src/zed/open_listener.rs b/crates/zed/src/zed/open_listener.rs index e8f8554482680c4a51fc182c58369de19184bcb0..ca376f300d97de83d0b4a9af7620ee98ba5b4215 100644 --- a/crates/zed/src/zed/open_listener.rs +++ b/crates/zed/src/zed/open_listener.rs @@ -29,7 +29,7 @@ use util::ResultExt; use util::paths::PathWithPosition; use workspace::PathList; use workspace::item::ItemHandle; -use workspace::{AppState, MultiWorkspace, OpenOptions, SerializedWorkspaceLocation}; +use workspace::{AppState, MultiWorkspace, OpenOptions, OpenResult, SerializedWorkspaceLocation}; #[derive(Default, Debug)] pub struct OpenRequest { @@ -345,7 +345,11 @@ pub async fn open_paths_with_positions( .map(|path_with_position| path_with_position.path.clone()) .collect::>(); - let (multi_workspace, mut items) = cx + let OpenResult { + window: multi_workspace, + opened_items: mut items, + .. + } = cx .update(|cx| workspace::open_paths(&paths, app_state, open_options, cx)) .await?;