diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index b90972b3489c25f8a2bf10d7dbdb6d6cfe0c4c6c..4d1d4a5da809559a36829b1c171556e9ad4eccd8 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -143,6 +143,7 @@ use worktree::{CreatedEntry, Snapshot, Traversal}; pub use worktree::{ Entry, EntryKind, FS_WATCH_LATENCY, File, LocalWorktree, PathChange, ProjectEntryId, UpdatedEntriesSet, UpdatedGitRepositoriesSet, Worktree, WorktreeId, WorktreeSettings, + discover_root_repo_common_dir, }; use worktree_store::{WorktreeStore, WorktreeStoreEvent}; diff --git a/crates/workspace/src/multi_workspace.rs b/crates/workspace/src/multi_workspace.rs index d5308124a8d076914643fcb215c1acfa780c849c..8c0cf287bff8fd0a8ace8a484de031884b3b99f7 100644 --- a/crates/workspace/src/multi_workspace.rs +++ b/crates/workspace/src/multi_workspace.rs @@ -605,7 +605,12 @@ impl MultiWorkspace { } pub fn restore_project_group_keys(&mut self, keys: Vec) { - let mut restored = keys; + let mut restored: Vec = Vec::with_capacity(keys.len()); + for key in keys { + if !restored.contains(&key) { + restored.push(key); + } + } for existing_key in &self.project_group_keys { if !restored.contains(existing_key) { restored.push(existing_key.clone()); diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 29684a282476b438e8e60b74e0fccf340cd17edf..ba25f8abda6dbb9cfe58fc8be542f593bbba183a 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -8712,34 +8712,92 @@ pub async fn restore_multiworkspace( .. } = state; - let window_handle = if active_workspace.paths.is_empty() { + let workspace_result = if active_workspace.paths.is_empty() { cx.update(|cx| { open_workspace_by_id(active_workspace.workspace_id, app_state.clone(), None, cx) }) - .await? + .await } else { - let OpenResult { window, .. } = cx - .update(|cx| { - Workspace::new_local( - active_workspace.paths.paths().to_vec(), - app_state.clone(), - None, - None, - None, - OpenMode::Activate, - cx, - ) - }) - .await?; - window + cx.update(|cx| { + Workspace::new_local( + active_workspace.paths.paths().to_vec(), + app_state.clone(), + None, + None, + None, + OpenMode::Activate, + cx, + ) + }) + .await + .map(|result| result.window) + }; + + let window_handle = match workspace_result { + Ok(handle) => handle, + Err(err) => { + log::error!("Failed to restore active workspace: {err:#}"); + + // Try each project group's paths as a fallback. + let mut fallback_handle = None; + for key in &project_group_keys { + let key: ProjectGroupKey = key.clone().into(); + let paths = key.path_list().paths().to_vec(); + match cx + .update(|cx| { + Workspace::new_local( + paths, + app_state.clone(), + None, + None, + None, + OpenMode::Activate, + cx, + ) + }) + .await + { + Ok(OpenResult { window, .. }) => { + fallback_handle = Some(window); + break; + } + Err(fallback_err) => { + log::error!("Fallback project group also failed: {fallback_err:#}"); + } + } + } + + fallback_handle.ok_or(err)? + } }; if !project_group_keys.is_empty() { - let restored_keys: Vec = - project_group_keys.into_iter().map(Into::into).collect(); + let fs = app_state.fs.clone(); + + // Resolve linked worktree paths to their main repo paths so + // stale keys from previous sessions get normalized and deduped. + let mut resolved_keys: Vec = Vec::new(); + for key in project_group_keys.into_iter().map(ProjectGroupKey::from) { + let mut resolved_paths = Vec::new(); + for path in key.path_list().paths() { + if let Some(common_dir) = + project::discover_root_repo_common_dir(path, fs.as_ref()).await + { + let main_path = common_dir.parent().unwrap_or(&common_dir); + resolved_paths.push(main_path.to_path_buf()); + } else { + resolved_paths.push(path.to_path_buf()); + } + } + let resolved = ProjectGroupKey::new(key.host(), PathList::new(&resolved_paths)); + if !resolved_keys.contains(&resolved) { + resolved_keys.push(resolved); + } + } + window_handle .update(cx, |multi_workspace, _window, _cx| { - multi_workspace.restore_project_group_keys(restored_keys); + multi_workspace.restore_project_group_keys(resolved_keys); }) .ok(); } diff --git a/crates/worktree/src/worktree.rs b/crates/worktree/src/worktree.rs index 864858073db70c984e61dbf43bf98be44f6c1c58..1494ba5fce2e46bdc2d199324ee5021b19f99408 100644 --- a/crates/worktree/src/worktree.rs +++ b/crates/worktree/src/worktree.rs @@ -6109,7 +6109,7 @@ fn parse_gitfile(content: &str) -> anyhow::Result<&Path> { Ok(Path::new(path.trim())) } -async fn discover_root_repo_common_dir(root_abs_path: &Path, fs: &dyn Fs) -> Option> { +pub async fn discover_root_repo_common_dir(root_abs_path: &Path, fs: &dyn Fs) -> Option> { let root_dot_git = root_abs_path.join(DOT_GIT); if !fs.metadata(&root_dot_git).await.is_ok_and(|m| m.is_some()) { return None;