project: Scope terminal toolchain lookup to the terminal's worktree (#56787)
`create_terminal_task` and `create_terminal_shell_internal` previously built their toolchain candidate list as `[active_editor_entry.path, ...visible_worktrees]` and used the first one with a persisted toolchain. A Python toolchain persisted for worktree A would leak into a terminal opened in worktree B of the same window: with a conda env selected for one project, every terminal in every other project of the window inherited that env's activation at startup. This scopes the candidate list to the worktree that contains the terminal's CWD. Both call sites now derive candidates from `find_worktree(path)`, where `path` is the terminal's resolved CWD (`spawn_task.cwd` or `active_project_directory`). If the path lives in a worktree, lookup runs scoped to that worktree's id. If not, no toolchain is applied. ## Design decision The previous fallback chain (active editor's worktree, then all visible worktrees) is removed entirely. Both fallbacks were sources of the leak: - "Active editor's worktree" is order-dependent: opening a terminal in worktree B picks up worktree A's toolchain because a file in A is focused. - "All visible worktrees" is worse, since the first worktree with any persisted toolchain wins. Workflows that relied on either fallback need to set the toolchain explicitly on the worktree where the terminal is opened. ## Coverage `cargo check -p project` is clean. There is no unit test for the cross-worktree case; that would require a multi-worktree Project fixture with persisted toolchains, which doesn't exist in the test suite today. Reproduction is straightforward: open a workspace with two folders, select a Python toolchain in one, open a terminal in the other, and observe the leaked activation script in the spawned shell. Release Notes: - Fixed Python toolchains persisted for one worktree leaking into terminals opened in other worktrees of the same workspace.
This commit is contained in:
1 parent
ea4bf744d0
commit
4d4e5ec420
1 file changed
+27
-22
@@ -115,19 +115,23 @@ impl Project {
|
||||
let env_task =
|
||||
self.resolve_directory_environment(&shell, path.clone(), remote_client.clone(), cx);
|
||||
|
||||
let project_path_contexts = self
|
||||
.active_entry()
|
||||
.and_then(|entry_id| self.path_for_entry(entry_id, cx))
|
||||
// Scope the toolchain lookup to the worktree the terminal is being
|
||||
// spawned in. Previously this iterated the active editor's worktree
|
||||
// and then every visible worktree, so a Python toolchain persisted
|
||||
// for worktree A would leak into a terminal opened in worktree B and
|
||||
// inject (e.g.) `conda activate base` into a shell that has no
|
||||
// business with conda.
|
||||
let project_path_contexts: Vec<ProjectPath> = path
|
||||
.as_ref()
|
||||
.and_then(|p| self.find_worktree(p, cx))
|
||||
.map(|(worktree, relative_path)| ProjectPath {
|
||||
worktree_id: worktree.read(cx).id(),
|
||||
path: relative_path,
|
||||
})
|
||||
.into_iter()
|
||||
.chain(
|
||||
self.visible_worktrees(cx)
|
||||
.map(|wt| wt.read(cx).id())
|
||||
.map(|worktree_id| ProjectPath {
|
||||
worktree_id,
|
||||
path: Arc::from(RelPath::empty()),
|
||||
}),
|
||||
);
|
||||
.collect();
|
||||
let toolchains = project_path_contexts
|
||||
.into_iter()
|
||||
.filter(|_| detect_venv)
|
||||
.map(|p| self.active_toolchain(p, LanguageName::new_static("Python"), cx))
|
||||
.collect::<Vec<_>>();
|
||||
@@ -333,19 +337,20 @@ impl Project {
|
||||
let detect_venv = settings.detect_venv.as_option().is_some();
|
||||
let local_path = if is_via_remote { None } else { path.clone() };
|
||||
|
||||
let project_path_contexts = self
|
||||
.active_entry()
|
||||
.and_then(|entry_id| self.path_for_entry(entry_id, cx))
|
||||
// See create_terminal_task: scope the toolchain lookup to the
|
||||
// worktree the terminal is opened in, not the active editor's
|
||||
// worktree or other visible worktrees.
|
||||
let project_path_contexts: Vec<ProjectPath> = path
|
||||
.as_ref()
|
||||
.and_then(|p| self.find_worktree(p, cx))
|
||||
.map(|(worktree, relative_path)| ProjectPath {
|
||||
worktree_id: worktree.read(cx).id(),
|
||||
path: relative_path,
|
||||
})
|
||||
.into_iter()
|
||||
.chain(
|
||||
self.visible_worktrees(cx)
|
||||
.map(|wt| wt.read(cx).id())
|
||||
.map(|worktree_id| ProjectPath {
|
||||
worktree_id,
|
||||
path: RelPath::empty().into(),
|
||||
}),
|
||||
);
|
||||
.collect();
|
||||
let toolchains = project_path_contexts
|
||||
.into_iter()
|
||||
.filter(|_| detect_venv)
|
||||
.map(|p| self.active_toolchain(p, LanguageName::new_static("Python"), cx))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Reference in new issue
Block a user