@@ -51,23 +51,42 @@ function generateId(name: string, extensionId?: string): string {
5151export class PythonEnvironmentManagers implements EnvironmentManagers {
5252 private _environmentManagers : Map < string , InternalEnvironmentManager > = new Map ( ) ;
5353 private _packageManagers : Map < string , InternalPackageManager > = new Map ( ) ;
54- private readonly _previousEnvironments = new Map < string , PythonEnvironment | undefined > ( ) ;
54+
55+ /**
56+ * The last environment announced as "active" for each scope.
57+ * Keyed by project URI string or 'global'.
58+ *
59+ * Used for: (1) change detection before firing onDidChangeActiveEnvironment,
60+ * (2) fallback manager routing when no explicit setting exists.
61+ *
62+ * Only mutated by setEnvironment() / setEnvironments() / refreshEnvironment().
63+ */
64+ private readonly _activeSelection = new Map < string , PythonEnvironment | undefined > ( ) ;
5565
5666 private _onDidChangeEnvironmentManager = new EventEmitter < DidChangeEnvironmentManagerEventArgs > ( ) ;
5767 private _onDidChangePackageManager = new EventEmitter < DidChangePackageManagerEventArgs > ( ) ;
5868 private _onDidChangeEnvironments = new EventEmitter < InternalDidChangeEnvironmentsEventArgs > ( ) ;
59- private _onDidChangeEnvironment = new EventEmitter < DidChangeEnvironmentEventArgs > ( ) ;
60- private _onDidChangeEnvironmentFiltered = new EventEmitter < DidChangeEnvironmentEventArgs > ( ) ;
69+
70+ /** Fires when ANY manager reports a selection change, regardless of whether that manager is selected. */
71+ private _onDidChangeManagerEnvironment = new EventEmitter < DidChangeEnvironmentEventArgs > ( ) ;
72+
73+ /** Fires when the active (selected) environment for a scope actually changes. */
74+ private _onDidChangeActiveEnvironment = new EventEmitter < DidChangeEnvironmentEventArgs > ( ) ;
6175 private _onDidChangePackages = new EventEmitter < InternalDidChangePackagesEventArgs > ( ) ;
6276
6377 public onDidChangeEnvironmentManager : Event < DidChangeEnvironmentManagerEventArgs > =
6478 this . _onDidChangeEnvironmentManager . event ;
6579 public onDidChangePackageManager : Event < DidChangePackageManagerEventArgs > = this . _onDidChangePackageManager . event ;
6680 public onDidChangeEnvironments : Event < InternalDidChangeEnvironmentsEventArgs > = this . _onDidChangeEnvironments . event ;
67- public onDidChangeEnvironment : Event < DidChangeEnvironmentEventArgs > = this . _onDidChangeEnvironment . event ;
81+
82+ /** Fires when any registered manager reports a change — even if that manager is not the selected one. */
83+ public onDidChangeManagerEnvironment : Event < DidChangeEnvironmentEventArgs > =
84+ this . _onDidChangeManagerEnvironment . event ;
6885 public onDidChangePackages : Event < InternalDidChangePackagesEventArgs > = this . _onDidChangePackages . event ;
69- public onDidChangeEnvironmentFiltered : Event < DidChangeEnvironmentEventArgs > =
70- this . _onDidChangeEnvironmentFiltered . event ;
86+
87+ /** Fires only when the *selected* manager's environment for a scope actually changes. */
88+ public onDidChangeActiveEnvironment : Event < DidChangeEnvironmentEventArgs > =
89+ this . _onDidChangeActiveEnvironment . event ;
7190
7291 constructor ( private readonly pm : PythonProjectManager ) { }
7392
@@ -99,7 +118,7 @@ export class PythonEnvironmentManagers implements EnvironmentManagers {
99118 return ;
100119 }
101120
102- setImmediate ( ( ) => this . _onDidChangeEnvironment . fire ( e ) ) ;
121+ setImmediate ( ( ) => this . _onDidChangeManagerEnvironment . fire ( e ) ) ;
103122 } ) ,
104123 ) ;
105124
@@ -196,7 +215,7 @@ export class PythonEnvironmentManagers implements EnvironmentManagers {
196215 // Fall back to cached environment's manager if no user-configured settings
197216 const project = context ? this . pm . get ( context ) : undefined ;
198217 const key = project ? project . uri . toString ( ) : 'global' ;
199- const cachedEnv = this . _previousEnvironments . get ( key ) ;
218+ const cachedEnv = this . _activeSelection . get ( key ) ;
200219 if ( cachedEnv ) {
201220 const cachedManager = this . _environmentManagers . get ( cachedEnv . envId . managerId ) ;
202221 if ( cachedManager ) {
@@ -339,11 +358,11 @@ export class PythonEnvironmentManagers implements EnvironmentManagers {
339358 }
340359
341360 const key = project ? project . uri . toString ( ) : 'global' ;
342- const oldEnv = this . _previousEnvironments . get ( key ) ;
361+ const oldEnv = this . _activeSelection . get ( key ) ;
343362 if ( oldEnv ?. envId . id !== environment ?. envId . id ) {
344- this . _previousEnvironments . set ( key , environment ) ;
363+ this . _activeSelection . set ( key , environment ) ;
345364 setImmediate ( ( ) =>
346- this . _onDidChangeEnvironmentFiltered . fire ( { uri : project ?. uri , new : environment , old : oldEnv } ) ,
365+ this . _onDidChangeActiveEnvironment . fire ( { uri : project ?. uri , new : environment , old : oldEnv } ) ,
347366 ) ;
348367 }
349368 }
@@ -393,9 +412,9 @@ export class PythonEnvironmentManagers implements EnvironmentManagers {
393412
394413 const project = this . pm . get ( uri ) ;
395414 const key = project ? project . uri . toString ( ) : 'global' ;
396- const oldEnv = this . _previousEnvironments . get ( key ) ;
415+ const oldEnv = this . _activeSelection . get ( key ) ;
397416 if ( oldEnv ?. envId . id !== environment ?. envId . id ) {
398- this . _previousEnvironments . set ( key , environment ) ;
417+ this . _activeSelection . set ( key , environment ) ;
399418 events . push ( { uri : project ?. uri , new : environment , old : oldEnv } ) ;
400419 }
401420 } ) ;
@@ -411,9 +430,9 @@ export class PythonEnvironmentManagers implements EnvironmentManagers {
411430 } ) ;
412431 }
413432
414- const oldEnv = this . _previousEnvironments . get ( 'global' ) ;
433+ const oldEnv = this . _activeSelection . get ( 'global' ) ;
415434 if ( oldEnv ?. envId . id !== environment ?. envId . id ) {
416- this . _previousEnvironments . set ( 'global' , environment ) ;
435+ this . _activeSelection . set ( 'global' , environment ) ;
417436 events . push ( { uri : undefined , new : environment , old : oldEnv } ) ;
418437 }
419438 }
@@ -422,7 +441,7 @@ export class PythonEnvironmentManagers implements EnvironmentManagers {
422441 if ( shouldPersistSettings ) {
423442 await setAllManagerSettings ( settings ) ;
424443 }
425- setImmediate ( ( ) => events . forEach ( ( e ) => this . _onDidChangeEnvironmentFiltered . fire ( e ) ) ) ;
444+ setImmediate ( ( ) => events . forEach ( ( e ) => this . _onDidChangeActiveEnvironment . fire ( e ) ) ) ;
426445 } else {
427446 const promises : Promise < void > [ ] = [ ] ;
428447 const events : DidChangeEnvironmentEventArgs [ ] = [ ] ;
@@ -439,9 +458,9 @@ export class PythonEnvironmentManagers implements EnvironmentManagers {
439458 // events. But it ensures that we always get the latest environment at the time of this call.
440459 const newEnv = await manager . get ( uri ) ;
441460 const key = project ? project . uri . toString ( ) : 'global' ;
442- const oldEnv = this . _previousEnvironments . get ( key ) ;
461+ const oldEnv = this . _activeSelection . get ( key ) ;
443462 if ( oldEnv ?. envId . id !== newEnv ?. envId . id ) {
444- this . _previousEnvironments . set ( key , newEnv ) ;
463+ this . _activeSelection . set ( key , newEnv ) ;
445464 events . push ( { uri : project ?. uri , new : newEnv , old : oldEnv } ) ;
446465 }
447466 } ;
@@ -457,17 +476,17 @@ export class PythonEnvironmentManagers implements EnvironmentManagers {
457476 // Always get the new first, then compare with the old. This has minor impact on the ordering of
458477 // events. But it ensures that we always get the latest environment at the time of this call.
459478 const newEnv = await manager . get ( undefined ) ;
460- const oldEnv = this . _previousEnvironments . get ( 'global' ) ;
479+ const oldEnv = this . _activeSelection . get ( 'global' ) ;
461480 if ( oldEnv ?. envId . id !== newEnv ?. envId . id ) {
462- this . _previousEnvironments . set ( 'global' , newEnv ) ;
481+ this . _activeSelection . set ( 'global' , newEnv ) ;
463482 events . push ( { uri : undefined , new : newEnv , old : oldEnv } ) ;
464483 }
465484 } ;
466485 promises . push ( setAndAddEvent ( ) ) ;
467486 }
468487 }
469488 await Promise . all ( promises ) ;
470- setImmediate ( ( ) => events . forEach ( ( e ) => this . _onDidChangeEnvironmentFiltered . fire ( e ) ) ) ;
489+ setImmediate ( ( ) => events . forEach ( ( e ) => this . _onDidChangeActiveEnvironment . fire ( e ) ) ) ;
471490 }
472491 }
473492
@@ -503,8 +522,9 @@ export class PythonEnvironmentManagers implements EnvironmentManagers {
503522 /**
504523 * Gets the current Python environment for the given scope URI or undefined for 'global'.
505524 *
506- * This method queries the appropriate environment manager for the latest environment for the scope.
507- * It also updates the internal cache and fires an event if the environment has changed since last check.
525+ * This is a pure read: it queries the environment manager but does NOT update the
526+ * internal selection cache or fire change events. Selection state is only mutated
527+ * through setEnvironment() / setEnvironments() / refreshEnvironment().
508528 *
509529 * @param scope The scope to get the environment.
510530 * @returns The current PythonEnvironment for the scope, or undefined if none is set.
@@ -519,21 +539,35 @@ export class PythonEnvironmentManagers implements EnvironmentManagers {
519539 return undefined ;
520540 }
521541
522- const project = scope ? this . pm . get ( scope ) : undefined ;
542+ return manager . get ( scope ) ;
543+ }
544+
545+ /**
546+ * Refreshes the cached environment for the given scope by querying the selected manager.
547+ * If the manager's current environment differs from the cache, updates the cache and
548+ * fires onDidChangeActiveEnvironment. Use this when a manager signals that its
549+ * selection has changed (e.g., via onDidChangeManagerEnvironment).
550+ *
551+ * Unlike getEnvironment(), this IS a mutation — it updates internal state.
552+ * Unlike setEnvironment(), it does NOT call manager.set() or persist to settings.
553+ */
554+ async refreshEnvironment ( scope : GetEnvironmentScope ) : Promise < void > {
555+ const manager = this . getEnvironmentManager ( scope ) ;
556+ if ( ! manager ) {
557+ return ;
558+ }
523559
524- // Always get the new first, then compare with the old. This has minor impact on the ordering of
525- // events. But it ensures that we always get the latest environment at the time of this call.
560+ const project = scope ? this . pm . get ( scope ) : undefined ;
526561 const newEnv = await manager . get ( scope ) ;
527562
528563 const key = project ? project . uri . toString ( ) : 'global' ;
529- const oldEnv = this . _previousEnvironments . get ( key ) ;
564+ const oldEnv = this . _activeSelection . get ( key ) ;
530565 if ( oldEnv ?. envId . id !== newEnv ?. envId . id ) {
531- this . _previousEnvironments . set ( key , newEnv ) ;
566+ this . _activeSelection . set ( key , newEnv ) ;
532567 setImmediate ( ( ) =>
533- this . _onDidChangeEnvironmentFiltered . fire ( { uri : project ?. uri , new : newEnv , old : oldEnv } ) ,
568+ this . _onDidChangeActiveEnvironment . fire ( { uri : project ?. uri , new : newEnv , old : oldEnv } ) ,
534569 ) ;
535570 }
536- return newEnv ;
537571 }
538572
539573 getProjectEnvManagers ( uris : Uri [ ] ) : InternalEnvironmentManager [ ] {
0 commit comments