Support find_project_path being given absolute paths (#30283)

Sometimes models return absolute paths even though we ask them not to
(including sometimes returning `/dev/null`). Currently we assume we're
always given a relative path, which leads to a panic in debug builds.
Now we just support being given absolute paths.

Release Notes:

- N/A

---------

Co-authored-by: Agus Zubiaga <hi@aguz.me>
This commit is contained in:
Richard Feldman 2025-05-08 15:04:51 -04:00 committed by GitHub
parent 6827bf114a
commit 77945fc905
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 115 additions and 16 deletions

View file

@ -4163,6 +4163,18 @@ impl Project {
let path = path.as_ref();
let worktree_store = self.worktree_store.read(cx);
if path.is_absolute() {
for worktree in worktree_store.visible_worktrees(cx) {
let worktree_abs_path = worktree.read(cx).abs_path();
if let Ok(relative_path) = path.strip_prefix(worktree_abs_path) {
return Some(ProjectPath {
worktree_id: worktree.read(cx).id(),
path: relative_path.into(),
});
}
}
} else {
for worktree in worktree_store.visible_worktrees(cx) {
let worktree_root_name = worktree.read(cx).root_name();
if let Ok(relative_path) = path.strip_prefix(worktree_root_name) {
@ -4182,6 +4194,7 @@ impl Project {
});
}
}
}
None
}

View file

@ -8766,3 +8766,89 @@ fn git_status(repo: &git2::Repository) -> collections::HashMap<String, git2::Sta
.map(|status| (status.path().unwrap().to_string(), status.status()))
.collect()
}
#[gpui::test]
async fn test_find_project_path_abs(
background_executor: BackgroundExecutor,
cx: &mut gpui::TestAppContext,
) {
// find_project_path should work with absolute paths
init_test(cx);
let fs = FakeFs::new(background_executor);
fs.insert_tree(
path!("/root"),
json!({
"project1": {
"file1.txt": "content1",
"subdir": {
"file2.txt": "content2"
}
},
"project2": {
"file3.txt": "content3"
}
}),
)
.await;
let project = Project::test(
fs.clone(),
[
path!("/root/project1").as_ref(),
path!("/root/project2").as_ref(),
],
cx,
)
.await;
// Make sure the worktrees are fully initialized
for worktree in project.read_with(cx, |project, cx| project.worktrees(cx).collect::<Vec<_>>()) {
worktree
.read_with(cx, |tree, _| tree.as_local().unwrap().scan_complete())
.await;
}
cx.run_until_parked();
let (project1_abs_path, project1_id, project2_abs_path, project2_id) =
project.read_with(cx, |project, cx| {
let worktrees: Vec<_> = project.worktrees(cx).collect();
let abs_path1 = worktrees[0].read(cx).abs_path().to_path_buf();
let id1 = worktrees[0].read(cx).id();
let abs_path2 = worktrees[1].read(cx).abs_path().to_path_buf();
let id2 = worktrees[1].read(cx).id();
(abs_path1, id1, abs_path2, id2)
});
project.update(cx, |project, cx| {
let abs_path = project1_abs_path.join("file1.txt");
let found_path = project.find_project_path(abs_path, cx).unwrap();
assert_eq!(found_path.worktree_id, project1_id);
assert_eq!(found_path.path.as_ref(), Path::new("file1.txt"));
let abs_path = project1_abs_path.join("subdir").join("file2.txt");
let found_path = project.find_project_path(abs_path, cx).unwrap();
assert_eq!(found_path.worktree_id, project1_id);
assert_eq!(found_path.path.as_ref(), Path::new("subdir/file2.txt"));
let abs_path = project2_abs_path.join("file3.txt");
let found_path = project.find_project_path(abs_path, cx).unwrap();
assert_eq!(found_path.worktree_id, project2_id);
assert_eq!(found_path.path.as_ref(), Path::new("file3.txt"));
let abs_path = project1_abs_path.join("nonexistent.txt");
let found_path = project.find_project_path(abs_path, cx);
assert!(
found_path.is_some(),
"Should find project path for nonexistent file in worktree"
);
// Test with an absolute path outside any worktree
let abs_path = Path::new("/some/other/path");
let found_path = project.find_project_path(abs_path, cx);
assert!(
found_path.is_none(),
"Should not find project path for path outside any worktree"
);
});
}