From fef95110bb4462156472a622e948089ccbb2732f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 31 Jul 2024 17:55:11 +0200 Subject: [PATCH] If paths from edit steps omit worktree root, search worktrees for relative path (#15543) Release Notes: - N/A Co-authored-by: Nathan --- crates/assistant/src/context.rs | 2 +- crates/project/src/project.rs | 49 ++++++++++++++++++++------------- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/crates/assistant/src/context.rs b/crates/assistant/src/context.rs index bd7af623b4..f6666a823c 100644 --- a/crates/assistant/src/context.rs +++ b/crates/assistant/src/context.rs @@ -528,7 +528,7 @@ impl EditOperation { let buffer = project .update(&mut cx, |project, cx| { 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))?; anyhow::Ok(project.open_buffer(project_path, cx)) })?? diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 73379e56a6..8b3eccb1b8 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -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 - /// the given full path against each worktree's root name. If a match is found, - /// it returns a `ProjectPath` containing the worktree ID and the relative path - /// within that worktree. + /// We use this to resolve edit steps, when there's a chance an LLM may omit the workree + /// root name from paths. /// /// # 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`. /// /// # Returns /// /// Returns `Some(ProjectPath)` if a matching worktree is found, otherwise `None`. - pub fn project_path_for_full_path( - &self, - full_path: &Path, - cx: &AppContext, - ) -> Option { - self.worktree_store.read_with(cx, |worktree_store, cx| { - worktree_store.worktrees().find_map(|worktree| { - let worktree_root_name = worktree.read(cx).root_name(); - let relative_path = full_path.strip_prefix(worktree_root_name).ok()?; - Some(ProjectPath { + pub fn find_project_path(&self, path: &Path, cx: &AppContext) -> Option { + let worktree_store = self.worktree_store.read(cx); + + 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) { + return Some(ProjectPath { worktree_id: worktree.read(cx).id(), 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(