From 3cd051ac7c363bf983349ca10e5c48cb3fd4c381 Mon Sep 17 00:00:00 2001 From: Thanh Nguyen <74597207+ThanhNguyxn@users.noreply.github.com> Date: Fri, 13 Feb 2026 10:46:18 -0500 Subject: [PATCH] terminal: Normalize path-like targets with leading `..` (#47289) Fixes #28339 ## What - Normalize cwd-relative path-like targets that include leading `..` before worktree lookup. - Update issue #28339 tests to expect `WorktreeExact` for local and remote resolution. ## Why - Prevent terminal links like `../foo/bar.txt` from creating invalid entries; resolve to the correct worktree file instead. ## How to test - `cargo test -p terminal_view issue_28339 -- --nocapture` - Manual: open a workspace, run `echo ../foo/bar.txt` from a subdir in the terminal, then cmd/ctrl-click and confirm the correct file opens. ## Risk - Low: only changes terminal path-like resolution for `..` paths; expected to match normalized behavior. ## Checklist - [x] Tests run (`cargo test -p terminal_view issue_28339 -- --nocapture`) - [x] Docs updated (not needed) - [x] Backwards compatibility considered Release Notes: - Fixed an issue where relative paths starting with `..` would not resolve correctly when clicking the link in the terminal --------- Co-authored-by: Ben Kunkle --- .../src/terminal_path_like_target.rs | 45 ++++++++++++------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/crates/terminal_view/src/terminal_path_like_target.rs b/crates/terminal_view/src/terminal_path_like_target.rs index e3384323062aaac2cff39d9904688dcf0edba718..d6a10cd05f980320af2da3d4f2a001398d9a4007 100644 --- a/crates/terminal_view/src/terminal_path_like_target.rs +++ b/crates/terminal_view/src/terminal_path_like_target.rs @@ -8,7 +8,7 @@ use std::path::PathBuf; use terminal::PathLikeTarget; use util::{ ResultExt, debug_panic, - paths::{PathStyle, PathWithPosition}, + paths::{PathStyle, PathWithPosition, normalize_lexically}, rel_path::RelPath, }; use workspace::{OpenOptions, OpenVisible, Workspace}; @@ -222,17 +222,33 @@ fn possible_open_target( } }; - if let Ok(relative_path_to_check) = - RelPath::new(&path_to_check.path, PathStyle::local()) - && !worktree.read(cx).is_single_file() - && let Some(entry) = relative_cwd - .clone() - .and_then(|relative_cwd| { - worktree - .read(cx) - .entry_for_path(&relative_cwd.join(&relative_path_to_check)) + // Normalize the path by joining with cwd if available (handles `.` and `..` segments) + let normalized_path = if path_to_check.path.is_relative() { + relative_cwd.as_ref().and_then(|relative_cwd| { + let joined = relative_cwd + .as_ref() + .as_std_path() + .join(&path_to_check.path); + normalize_lexically(&joined).ok().and_then(|p| { + RelPath::new(&p, PathStyle::local()) + .ok() + .map(std::borrow::Cow::into_owned) + }) + }) + } else { + None + }; + let original_path = RelPath::new(&path_to_check.path, PathStyle::local()).ok(); + + if !worktree.read(cx).is_single_file() + && let Some(entry) = normalized_path + .as_ref() + .and_then(|p| worktree.read(cx).entry_for_path(p)) + .or_else(|| { + original_path + .as_ref() + .and_then(|p| worktree.read(cx).entry_for_path(p.as_ref())) }) - .or_else(|| worktree.read(cx).entry_for_path(&relative_path_to_check)) { open_target = Some(OpenTarget::Worktree( PathWithPosition { @@ -999,8 +1015,6 @@ mod tests { } // https://github.com/zed-industries/zed/issues/28339 - // Note: These could all be found by WorktreeExact if we used - // `fs::normalize_path(&maybe_path)` #[gpui::test] async fn issue_28339(cx: &mut TestAppContext) { test_path_likes!( @@ -1051,17 +1065,14 @@ mod tests { "../foo/bar.txt", "/tmp/issue28339/foo/bar.txt", "/tmp/issue28339/foo", - FileSystemBackground + WorktreeExact ); } ) } // https://github.com/zed-industries/zed/issues/28339 - // Note: These could all be found by WorktreeExact if we used - // `fs::normalize_path(&maybe_path)` #[gpui::test] - #[should_panic(expected = "Hover target should not be `None`")] async fn issue_28339_remote(cx: &mut TestAppContext) { test_path_likes!( cx,