@@ -20,6 +20,7 @@ import { dirname, join } from 'path';
2020import { resolveEnvironment , useEnvExtension } from '../envExt/api.internal' ;
2121import { ErrorWithTelemetrySafeReason } from '../common/errors/errorUtils' ;
2222import { getWorkspaceFolders } from '../common/vscodeApis/workspaceApis' ;
23+ import { arePathsSame } from '../common/platform/fs-paths' ;
2324
2425export interface IResourceReference {
2526 resourcePath ?: string ;
@@ -49,12 +50,24 @@ export function resolveFilePath(filepath?: string): Uri | undefined {
4950 * @see {@link raceCancellation }
5051 */
5152export function raceCancellationError < T > ( promise : Promise < T > , token : CancellationToken ) : Promise < T > {
53+ if ( token . isCancellationRequested ) {
54+ return Promise . reject ( new CancellationError ( ) ) ;
55+ }
5256 return new Promise ( ( resolve , reject ) => {
5357 const ref = token . onCancellationRequested ( ( ) => {
5458 ref . dispose ( ) ;
5559 reject ( new CancellationError ( ) ) ;
5660 } ) ;
57- promise . then ( resolve , reject ) . finally ( ( ) => ref . dispose ( ) ) ;
61+ promise . then (
62+ ( value ) => {
63+ ref . dispose ( ) ;
64+ resolve ( value ) ;
65+ } ,
66+ ( error ) => {
67+ ref . dispose ( ) ;
68+ reject ( error ) ;
69+ } ,
70+ ) ;
5871 } ) ;
5972}
6073
@@ -68,13 +81,17 @@ export function raceCancellationError<T>(promise: Promise<T>, token: Cancellatio
6881export function waitForActiveEnvironmentChange (
6982 api : PythonExtension [ 'environments' ] ,
7083 pythonPath : string ,
84+ resource : Uri | undefined ,
7185 token : CancellationToken ,
7286 timeoutMs = 5000 ,
7387) : Promise < void > {
88+ if ( token . isCancellationRequested ) {
89+ return Promise . resolve ( ) ;
90+ }
7491 return new Promise < void > ( ( resolve ) => {
7592 let settled = false ;
7693 const listener = api . onDidChangeActiveEnvironmentPath ( ( e ) => {
77- if ( e . path === pythonPath || e . id === pythonPath ) {
94+ if ( isEnvironmentPathMatch ( e , pythonPath ) && isResourceMatch ( e . resource , resource ) ) {
7895 settle ( ) ;
7996 }
8097 } ) ;
@@ -93,49 +110,63 @@ export function waitForActiveEnvironmentChange(
93110 } ) ;
94111}
95112
113+ function isResourceMatch (
114+ eventResource : { uri : Uri } | Uri | undefined ,
115+ requestedResource : Uri | undefined ,
116+ ) : boolean {
117+ const eventUri = eventResource && 'uri' in eventResource ? eventResource . uri : eventResource ;
118+ const requestedUri = requestedResource
119+ ? workspace . getWorkspaceFolder ( requestedResource ) ?. uri ?? requestedResource
120+ : undefined ;
121+ return eventUri === undefined
122+ ? requestedUri === undefined
123+ : requestedUri !== undefined && arePathsSame ( eventUri . fsPath , requestedUri . fsPath ) ;
124+ }
125+
126+ function isEnvironmentPathMatch ( environment : { path : string ; id : string } , pythonPath : string ) : boolean {
127+ return arePathsSame ( environment . path , pythonPath ) || environment . id === pythonPath ;
128+ }
129+
96130/**
97131 * Sets the active Python interpreter to `pythonPath` without any UI, waits for the
98132 * asynchronous environment switch to settle (via `onDidChangeActiveEnvironmentPath`),
99- * resolves the environment, and returns a tool result describing it.
133+ * resolves the environment, and returns it.
100134 *
101135 * Returns `undefined` if the path cannot be resolved to a valid environment so callers
102136 * can produce a tool-specific error message.
103137 */
104138export async function setEnvironmentDirectlyByPath (
105139 pythonPath : string ,
106140 api : PythonExtension [ 'environments' ] ,
107- terminalExecutionService : TerminalCodeExecutionProvider ,
108- terminalHelper : ITerminalHelper ,
109141 resource : Uri | undefined ,
110142 token : CancellationToken ,
111- ) : Promise < LanguageModelToolResult | undefined > {
143+ ) : Promise < ResolvedEnvironment | undefined > {
112144 // Validate the path resolves to a real environment BEFORE mutating user settings.
113145 // updateActiveEnvironmentPath persists unconditionally, so an invalid path would
114146 // permanently overwrite the user's selected interpreter.
115147 const candidate = await raceCancellationError ( api . resolveEnvironment ( pythonPath ) , token ) ;
116148 if ( ! candidate ) {
117149 return undefined ;
118150 }
151+ if ( isEnvironmentPathMatch ( api . getActiveEnvironmentPath ( resource ) , pythonPath ) ) {
152+ return candidate ;
153+ }
119154
120155 // Subscribe to the change event BEFORE triggering the update so we don't miss it.
121156 // updateActiveEnvironmentPath only persists the setting; the active interpreter switch
122157 // is asynchronous, so we wait for the event before resolving env details to avoid
123158 // returning details for the previously-active interpreter.
124- const activeChanged = waitForActiveEnvironmentChange ( api , pythonPath , token ) ;
159+ const activeChanged = waitForActiveEnvironmentChange ( api , pythonPath , resource , token ) ;
125160 await raceCancellationError ( api . updateActiveEnvironmentPath ( pythonPath , resource ) , token ) ;
126161 await raceCancellationError ( activeChanged , token ) ;
127162
128163 // Verify the active env actually switched. If the change event timed out and the
129164 // active path is still the previous one, don't report success for the wrong env.
130165 const envPath = api . getActiveEnvironmentPath ( resource ) ;
131- if ( envPath . path !== pythonPath && envPath . id !== pythonPath ) {
132- return undefined ;
133- }
134- const environment = await raceCancellationError ( api . resolveEnvironment ( envPath ) , token ) ;
135- if ( ! environment ) {
166+ if ( ! isEnvironmentPathMatch ( envPath , pythonPath ) ) {
136167 return undefined ;
137168 }
138- return getEnvDetailsForResponse ( environment , api , terminalExecutionService , terminalHelper , resource , token ) ;
169+ return raceCancellationError ( api . resolveEnvironment ( envPath ) , token ) ;
139170}
140171
141172export async function getEnvDisplayName (
0 commit comments