If paths from edit steps omit worktree root, search worktrees for relative path (#15543)

Release Notes:

- N/A

Co-authored-by: Nathan <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra 2024-07-31 17:55:11 +02:00 committed by GitHub
parent 3c404dec92
commit fef95110bb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 31 additions and 20 deletions

View file

@ -528,7 +528,7 @@ impl EditOperation {
let buffer = project let buffer = project
.update(&mut cx, |project, cx| { .update(&mut cx, |project, cx| {
let project_path = project let project_path = project
.project_path_for_full_path(Path::new(&path), cx) .find_project_path(Path::new(&path), cx)
.with_context(|| format!("worktree not found for {:?}", path))?; .with_context(|| format!("worktree not found for {:?}", path))?;
anyhow::Ok(project.open_buffer(project_path, cx)) anyhow::Ok(project.open_buffer(project_path, cx))
})?? })??

View file

@ -8341,36 +8341,47 @@ impl Project {
}) })
} }
/// Attempts to find a `ProjectPath` corresponding to the given full path. /// Attempts to find a `ProjectPath` corresponding to the given path. If the path
/// is a *full path*, meaning it starts with the root name of a worktree, we'll locate
/// it in that worktree. Otherwise, we'll attempt to find it as a relative path in
/// the first visible worktree that has an entry for that relative path.
/// ///
/// This method iterates through all worktrees in the project, trying to match /// We use this to resolve edit steps, when there's a chance an LLM may omit the workree
/// the given full path against each worktree's root name. If a match is found, /// root name from paths.
/// it returns a `ProjectPath` containing the worktree ID and the relative path
/// within that worktree.
/// ///
/// # Arguments /// # Arguments
/// ///
/// * `full_path` - A reference to a `Path` representing the full path to resolve. /// * `path` - A full path that starts with a worktree root name, or alternatively a
/// relative path within a visible worktree.
/// * `cx` - A reference to the `AppContext`. /// * `cx` - A reference to the `AppContext`.
/// ///
/// # Returns /// # Returns
/// ///
/// Returns `Some(ProjectPath)` if a matching worktree is found, otherwise `None`. /// Returns `Some(ProjectPath)` if a matching worktree is found, otherwise `None`.
pub fn project_path_for_full_path( pub fn find_project_path(&self, path: &Path, cx: &AppContext) -> Option<ProjectPath> {
&self, let worktree_store = self.worktree_store.read(cx);
full_path: &Path,
cx: &AppContext, for worktree in worktree_store.visible_worktrees(cx) {
) -> Option<ProjectPath> { let worktree_root_name = worktree.read(cx).root_name();
self.worktree_store.read_with(cx, |worktree_store, cx| { if let Ok(relative_path) = path.strip_prefix(worktree_root_name) {
worktree_store.worktrees().find_map(|worktree| { return Some(ProjectPath {
let worktree_root_name = worktree.read(cx).root_name();
let relative_path = full_path.strip_prefix(worktree_root_name).ok()?;
Some(ProjectPath {
worktree_id: worktree.read(cx).id(), worktree_id: worktree.read(cx).id(),
path: relative_path.into(), path: relative_path.into(),
}) });
}) }
}) }
for worktree in worktree_store.visible_worktrees(cx) {
let worktree = worktree.read(cx);
if let Some(entry) = worktree.entry_for_path(path) {
return Some(ProjectPath {
worktree_id: worktree.id(),
path: entry.path.clone(),
});
}
}
None
} }
pub fn get_workspace_root( pub fn get_workspace_root(