From 12852537f195c1a3f27ca1e97efe5599e5858a83 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 13 Mar 2026 08:47:48 +0100 Subject: [PATCH] project: Support resolving paths with worktree names prefixed (#50692) Release Notes: - N/A *or* Added/Fixed/Improved ... --- crates/editor/src/hover_links.rs | 63 ++++++++++++++++++++++++++++++++ crates/project/src/project.rs | 33 ++++++++++++++--- crates/worktree/src/worktree.rs | 4 ++ 3 files changed, 95 insertions(+), 5 deletions(-) diff --git a/crates/editor/src/hover_links.rs b/crates/editor/src/hover_links.rs index 3a6ff4ec0e4fc53d19bfb51a10b1f7790933b175..4cbd3d77cf09ccfebd48f50b6b26413837b24b2c 100644 --- a/crates/editor/src/hover_links.rs +++ b/crates/editor/src/hover_links.rs @@ -1889,6 +1889,69 @@ mod tests { }); } + #[gpui::test] + async fn test_hover_filenames_with_worktree_prefix(cx: &mut gpui::TestAppContext) { + init_test(cx, |_| {}); + let mut cx = EditorLspTestContext::new_rust( + lsp::ServerCapabilities { + ..Default::default() + }, + cx, + ) + .await; + + let fs = cx.update_workspace(|workspace, _, cx| workspace.project().read(cx).fs().clone()); + fs.as_fake() + .insert_file( + path!("/root/dir/file2.rs"), + "This is file2.rs".as_bytes().to_vec(), + ) + .await; + + #[cfg(not(target_os = "windows"))] + cx.set_state(indoc! {" + Go to root/dir/file2.rs if you want.ˇ + "}); + #[cfg(target_os = "windows")] + cx.set_state(indoc! {" + Go to root/dir/file2.rs if you want.ˇ + "}); + + let screen_coord = cx.pixel_position(indoc! {" + Go to root/diˇr/file2.rs if you want. + "}); + + cx.simulate_mouse_move(screen_coord, None, Modifiers::secondary_key()); + cx.assert_editor_text_highlights( + HighlightKey::HoveredLinkState, + indoc! {" + Go to «root/dir/file2.rsˇ» if you want. + "}, + ); + + cx.simulate_click(screen_coord, Modifiers::secondary_key()); + + cx.update_workspace(|workspace, _, cx| assert_eq!(workspace.items(cx).count(), 2)); + cx.update_workspace(|workspace, _, cx| { + let active_editor = workspace.active_item_as::(cx).unwrap(); + + let buffer = active_editor + .read(cx) + .buffer() + .read(cx) + .as_singleton() + .unwrap(); + + let file = buffer.read(cx).file().unwrap(); + let file_path = file.as_local().unwrap().abs_path(cx); + + assert_eq!( + file_path, + std::path::PathBuf::from(path!("/root/dir/file2.rs")) + ); + }); + } + #[gpui::test] async fn test_hover_directories(cx: &mut gpui::TestAppContext) { init_test(cx, |_| {}); diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index ed8884cd68c6df32375686dd5ceb41b21cbb5cdd..14379e20fd45c0460f54ea3d33fbfe8a04917c7a 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4590,24 +4590,38 @@ impl Project { let worktrees_with_ids: Vec<_> = self .worktrees(cx) .map(|worktree| { - let id = worktree.read(cx).id(); - (worktree, id) + let read = worktree.read(cx); + let id = read.id(); + ( + worktree, + id, + read.is_visible().then(|| read.root_name_arc()), + ) }) .collect(); cx.spawn(async move |_, cx| { if let Some(buffer_worktree_id) = buffer_worktree_id - && let Some((worktree, _)) = worktrees_with_ids + && let Some((worktree, _, root_name)) = worktrees_with_ids .iter() - .find(|(_, id)| *id == buffer_worktree_id) + .find(|(_, id, _)| *id == buffer_worktree_id) { for candidate in candidates.iter() { if let Some(path) = Self::resolve_path_in_worktree(worktree, candidate, cx) { return Some(path); } + if let Some(root_name) = root_name { + if let Ok(candidate) = candidate.strip_prefix(root_name) { + if let Some(path) = + Self::resolve_path_in_worktree(worktree, candidate, cx) + { + return Some(path); + } + } + } } } - for (worktree, id) in worktrees_with_ids { + for (worktree, id, root_name) in worktrees_with_ids { if Some(id) == buffer_worktree_id { continue; } @@ -4615,6 +4629,15 @@ impl Project { if let Some(path) = Self::resolve_path_in_worktree(&worktree, candidate, cx) { return Some(path); } + if let Some(root_name) = &root_name { + if let Ok(candidate) = candidate.strip_prefix(root_name) { + if let Some(path) = + Self::resolve_path_in_worktree(&worktree, candidate, cx) + { + return Some(path); + } + } + } } } None diff --git a/crates/worktree/src/worktree.rs b/crates/worktree/src/worktree.rs index 44ba4e752cff778b7918b9a29935d0f0e1ebb614..518bf5b4620fdf1f65793ca912bba21f614c67ee 100644 --- a/crates/worktree/src/worktree.rs +++ b/crates/worktree/src/worktree.rs @@ -2466,6 +2466,10 @@ impl Snapshot { &self.root_name } + pub fn root_name_arc(&self) -> Arc { + self.root_name.clone() + } + pub fn root_name_str(&self) -> &str { self.root_name.as_unix_str() }