@@ -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
@@ -165,6 +184,8 @@ export class PythonEnvironmentManagers implements EnvironmentManagers {
165184 this . _onDidChangeEnvironmentManager . dispose ( ) ;
166185 this . _onDidChangePackageManager . dispose ( ) ;
167186 this . _onDidChangeEnvironments . dispose ( ) ;
187+ this . _onDidChangeManagerEnvironment . dispose ( ) ;
188+ this . _onDidChangeActiveEnvironment . dispose ( ) ;
168189 this . _onDidChangePackages . dispose ( ) ;
169190 }
170191
@@ -196,7 +217,7 @@ export class PythonEnvironmentManagers implements EnvironmentManagers {
196217 // Fall back to cached environment's manager if no user-configured settings
197218 const project = context ? this . pm . get ( context ) : undefined ;
198219 const key = project ? project . uri . toString ( ) : 'global' ;
199- const cachedEnv = this . _previousEnvironments . get ( key ) ;
220+ const cachedEnv = this . _activeSelection . get ( key ) ;
200221 if ( cachedEnv ) {
201222 const cachedManager = this . _environmentManagers . get ( cachedEnv . envId . managerId ) ;
202223 if ( cachedManager ) {
@@ -339,11 +360,11 @@ export class PythonEnvironmentManagers implements EnvironmentManagers {
339360 }
340361
341362 const key = project ? project . uri . toString ( ) : 'global' ;
342- const oldEnv = this . _previousEnvironments . get ( key ) ;
363+ const oldEnv = this . _activeSelection . get ( key ) ;
343364 if ( oldEnv ?. envId . id !== environment ?. envId . id ) {
344- this . _previousEnvironments . set ( key , environment ) ;
365+ this . _activeSelection . set ( key , environment ) ;
345366 setImmediate ( ( ) =>
346- this . _onDidChangeEnvironmentFiltered . fire ( { uri : project ?. uri , new : environment , old : oldEnv } ) ,
367+ this . _onDidChangeActiveEnvironment . fire ( { uri : project ?. uri , new : environment , old : oldEnv } ) ,
347368 ) ;
348369 }
349370 }
@@ -393,9 +414,9 @@ export class PythonEnvironmentManagers implements EnvironmentManagers {
393414
394415 const project = this . pm . get ( uri ) ;
395416 const key = project ? project . uri . toString ( ) : 'global' ;
396- const oldEnv = this . _previousEnvironments . get ( key ) ;
417+ const oldEnv = this . _activeSelection . get ( key ) ;
397418 if ( oldEnv ?. envId . id !== environment ?. envId . id ) {
398- this . _previousEnvironments . set ( key , environment ) ;
419+ this . _activeSelection . set ( key , environment ) ;
399420 events . push ( { uri : project ?. uri , new : environment , old : oldEnv } ) ;
400421 }
401422 } ) ;
@@ -411,9 +432,9 @@ export class PythonEnvironmentManagers implements EnvironmentManagers {
411432 } ) ;
412433 }
413434
414- const oldEnv = this . _previousEnvironments . get ( 'global' ) ;
435+ const oldEnv = this . _activeSelection . get ( 'global' ) ;
415436 if ( oldEnv ?. envId . id !== environment ?. envId . id ) {
416- this . _previousEnvironments . set ( 'global' , environment ) ;
437+ this . _activeSelection . set ( 'global' , environment ) ;
417438 events . push ( { uri : undefined , new : environment , old : oldEnv } ) ;
418439 }
419440 }
@@ -422,7 +443,7 @@ export class PythonEnvironmentManagers implements EnvironmentManagers {
422443 if ( shouldPersistSettings ) {
423444 await setAllManagerSettings ( settings ) ;
424445 }
425- setImmediate ( ( ) => events . forEach ( ( e ) => this . _onDidChangeEnvironmentFiltered . fire ( e ) ) ) ;
446+ setImmediate ( ( ) => events . forEach ( ( e ) => this . _onDidChangeActiveEnvironment . fire ( e ) ) ) ;
426447 } else {
427448 const promises : Promise < void > [ ] = [ ] ;
428449 const events : DidChangeEnvironmentEventArgs [ ] = [ ] ;
@@ -439,9 +460,9 @@ export class PythonEnvironmentManagers implements EnvironmentManagers {
439460 // events. But it ensures that we always get the latest environment at the time of this call.
440461 const newEnv = await manager . get ( uri ) ;
441462 const key = project ? project . uri . toString ( ) : 'global' ;
442- const oldEnv = this . _previousEnvironments . get ( key ) ;
463+ const oldEnv = this . _activeSelection . get ( key ) ;
443464 if ( oldEnv ?. envId . id !== newEnv ?. envId . id ) {
444- this . _previousEnvironments . set ( key , newEnv ) ;
465+ this . _activeSelection . set ( key , newEnv ) ;
445466 events . push ( { uri : project ?. uri , new : newEnv , old : oldEnv } ) ;
446467 }
447468 } ;
@@ -457,17 +478,17 @@ export class PythonEnvironmentManagers implements EnvironmentManagers {
457478 // Always get the new first, then compare with the old. This has minor impact on the ordering of
458479 // events. But it ensures that we always get the latest environment at the time of this call.
459480 const newEnv = await manager . get ( undefined ) ;
460- const oldEnv = this . _previousEnvironments . get ( 'global' ) ;
481+ const oldEnv = this . _activeSelection . get ( 'global' ) ;
461482 if ( oldEnv ?. envId . id !== newEnv ?. envId . id ) {
462- this . _previousEnvironments . set ( 'global' , newEnv ) ;
483+ this . _activeSelection . set ( 'global' , newEnv ) ;
463484 events . push ( { uri : undefined , new : newEnv , old : oldEnv } ) ;
464485 }
465486 } ;
466487 promises . push ( setAndAddEvent ( ) ) ;
467488 }
468489 }
469490 await Promise . all ( promises ) ;
470- setImmediate ( ( ) => events . forEach ( ( e ) => this . _onDidChangeEnvironmentFiltered . fire ( e ) ) ) ;
491+ setImmediate ( ( ) => events . forEach ( ( e ) => this . _onDidChangeActiveEnvironment . fire ( e ) ) ) ;
471492 }
472493 }
473494
@@ -503,8 +524,9 @@ export class PythonEnvironmentManagers implements EnvironmentManagers {
503524 /**
504525 * Gets the current Python environment for the given scope URI or undefined for 'global'.
505526 *
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.
527+ * This is a pure read: it queries the environment manager but does NOT update the
528+ * internal selection cache or fire change events. Selection state is only mutated
529+ * through setEnvironment() / setEnvironments() / refreshEnvironment().
508530 *
509531 * @param scope The scope to get the environment.
510532 * @returns The current PythonEnvironment for the scope, or undefined if none is set.
@@ -519,21 +541,35 @@ export class PythonEnvironmentManagers implements EnvironmentManagers {
519541 return undefined ;
520542 }
521543
522- const project = scope ? this . pm . get ( scope ) : undefined ;
544+ return manager . get ( scope ) ;
545+ }
546+
547+ /**
548+ * Refreshes the cached environment for the given scope by querying the selected manager.
549+ * If the manager's current environment differs from the cache, updates the cache and
550+ * fires onDidChangeActiveEnvironment. Use this when a manager signals that its
551+ * selection has changed (e.g., via onDidChangeManagerEnvironment).
552+ *
553+ * Unlike getEnvironment(), this IS a mutation — it updates internal state.
554+ * Unlike setEnvironment(), it does NOT call manager.set() or persist to settings.
555+ */
556+ async refreshEnvironment ( scope : GetEnvironmentScope ) : Promise < void > {
557+ const manager = this . getEnvironmentManager ( scope ) ;
558+ if ( ! manager ) {
559+ return ;
560+ }
523561
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.
562+ const project = scope ? this . pm . get ( scope ) : undefined ;
526563 const newEnv = await manager . get ( scope ) ;
527564
528565 const key = project ? project . uri . toString ( ) : 'global' ;
529- const oldEnv = this . _previousEnvironments . get ( key ) ;
566+ const oldEnv = this . _activeSelection . get ( key ) ;
530567 if ( oldEnv ?. envId . id !== newEnv ?. envId . id ) {
531- this . _previousEnvironments . set ( key , newEnv ) ;
568+ this . _activeSelection . set ( key , newEnv ) ;
532569 setImmediate ( ( ) =>
533- this . _onDidChangeEnvironmentFiltered . fire ( { uri : project ?. uri , new : newEnv , old : oldEnv } ) ,
570+ this . _onDidChangeActiveEnvironment . fire ( { uri : project ?. uri , new : newEnv , old : oldEnv } ) ,
534571 ) ;
535572 }
536- return newEnv ;
537573 }
538574
539575 getProjectEnvManagers ( uris : Uri [ ] ) : InternalEnvironmentManager [ ] {
0 commit comments