1
1
// Copyright (c) Microsoft Corporation. All rights reserved.
2
2
// Licensed under the MIT License.
3
3
4
+ import { isEqual } from 'lodash' ;
4
5
import { Event , EventEmitter } from 'vscode' ;
6
+ import { traceVerbose } from '../../common/logger' ;
5
7
import { areSameEnvironment , PythonEnvInfo , PythonEnvKind } from '../base/info' ;
6
8
import {
7
9
ILocator , IPythonEnvsIterator , PythonEnvUpdatedEvent , QueryForEvent ,
8
10
} from '../base/locator' ;
9
11
import { PythonEnvsChangedEvent } from '../base/watcher' ;
10
12
13
+ /**
14
+ * Combines duplicate environments received from the incoming locator into one and passes on unique environments
15
+ */
11
16
export class PythonEnvsReducer implements ILocator {
12
17
public get onChanged ( ) : Event < PythonEnvsChangedEvent > {
13
18
return this . pythonEnvsManager . onChanged ;
@@ -21,68 +26,75 @@ export class PythonEnvsReducer implements ILocator {
21
26
22
27
public iterEnvs ( query ?: QueryForEvent < PythonEnvsChangedEvent > ) : IPythonEnvsIterator {
23
28
const didUpdate = new EventEmitter < PythonEnvUpdatedEvent | null > ( ) ;
24
- const iterator : IPythonEnvsIterator = this . iterEnvsIterator ( didUpdate , query ) ;
29
+ const incomingIterator = this . pythonEnvsManager . iterEnvs ( query ) ;
30
+ const iterator : IPythonEnvsIterator = iterEnvsIterator ( incomingIterator , didUpdate ) ;
25
31
iterator . onUpdated = didUpdate . event ;
26
32
return iterator ;
27
33
}
34
+ }
28
35
29
- private async * iterEnvsIterator (
30
- didUpdate : EventEmitter < PythonEnvUpdatedEvent | null > ,
31
- query ?: QueryForEvent < PythonEnvsChangedEvent > ,
32
- ) : AsyncIterator < PythonEnvInfo , void > {
33
- const state = {
34
- done : false ,
35
- pending : 0 ,
36
- } ;
37
- const seen : PythonEnvInfo [ ] = [ ] ;
38
- const iterator = this . pythonEnvsManager . iterEnvs ( query ) ;
36
+ async function * iterEnvsIterator (
37
+ iterator : IPythonEnvsIterator ,
38
+ didUpdate : EventEmitter < PythonEnvUpdatedEvent | null > ,
39
+ ) : AsyncIterator < PythonEnvInfo , void > {
40
+ const state = {
41
+ done : false ,
42
+ pending : 0 ,
43
+ } ;
44
+ const seen : PythonEnvInfo [ ] = [ ] ;
39
45
40
- if ( iterator . onUpdated !== undefined ) {
41
- iterator . onUpdated ( ( event ) => {
42
- if ( event === null ) {
43
- state . done = true ;
44
- checkIfFinishedAndNotify ( state , didUpdate ) ;
46
+ if ( iterator . onUpdated !== undefined ) {
47
+ iterator . onUpdated ( ( event ) => {
48
+ if ( event === null ) {
49
+ state . done = true ;
50
+ checkIfFinishedAndNotify ( state , didUpdate ) ;
51
+ } else {
52
+ const oldIndex = seen . findIndex ( ( s ) => areSameEnvironment ( s , event . old ) ) ;
53
+ if ( oldIndex !== - 1 ) {
54
+ state . pending += 1 ;
55
+ resolveDifferencesInBackground ( oldIndex , event . new , state , didUpdate , seen ) . ignoreErrors ( ) ;
45
56
} else {
46
- const old = seen . find ( ( s ) => areSameEnvironment ( s , event . old ) ) ;
47
- if ( old !== undefined ) {
48
- state . pending += 1 ;
49
- resolveDifferencesInBackground ( old , event . new , state , didUpdate , seen ) . ignoreErrors ( ) ;
50
- }
57
+ // This implies a problem in a downstream locator
58
+ traceVerbose ( `Expected already iterated env, got ${ event . old } ` ) ;
51
59
}
52
- } ) ;
53
- }
54
-
55
- let result = await iterator . next ( ) ;
56
- while ( ! result . done ) {
57
- const currEnv = result . value ;
58
- const old = seen . find ( ( s ) => areSameEnvironment ( s , currEnv ) ) ;
59
- if ( old !== undefined ) {
60
- state . pending += 1 ;
61
- resolveDifferencesInBackground ( old , currEnv , state , didUpdate , seen ) . ignoreErrors ( ) ;
62
- } else {
63
- yield currEnv ;
64
- seen . push ( currEnv ) ;
65
60
}
66
- // eslint-disable-next-line no-await-in-loop
67
- result = await iterator . next ( ) ;
68
- }
69
- if ( iterator . onUpdated === undefined ) {
70
- state . done = true ;
71
- checkIfFinishedAndNotify ( state , didUpdate ) ;
61
+ } ) ;
62
+ }
63
+
64
+ let result = await iterator . next ( ) ;
65
+ while ( ! result . done ) {
66
+ const currEnv = result . value ;
67
+ const oldIndex = seen . findIndex ( ( s ) => areSameEnvironment ( s , currEnv ) ) ;
68
+ if ( oldIndex !== - 1 ) {
69
+ state . pending += 1 ;
70
+ resolveDifferencesInBackground ( oldIndex , currEnv , state , didUpdate , seen ) . ignoreErrors ( ) ;
71
+ } else {
72
+ // We haven't yielded a matching env so yield this one as-is.
73
+ yield currEnv ;
74
+ seen . push ( currEnv ) ;
72
75
}
76
+ // eslint-disable-next-line no-await-in-loop
77
+ result = await iterator . next ( ) ;
78
+ }
79
+ if ( iterator . onUpdated === undefined ) {
80
+ state . done = true ;
81
+ checkIfFinishedAndNotify ( state , didUpdate ) ;
73
82
}
74
83
}
75
84
76
85
async function resolveDifferencesInBackground (
77
- oldEnv : PythonEnvInfo ,
86
+ oldIndex : number ,
78
87
newEnv : PythonEnvInfo ,
79
88
state : { done : boolean ; pending : number } ,
80
89
didUpdate : EventEmitter < PythonEnvUpdatedEvent | null > ,
81
90
seen : PythonEnvInfo [ ] ,
82
91
) {
92
+ const oldEnv = seen [ oldIndex ] ;
83
93
const merged = mergeEnvironments ( oldEnv , newEnv ) ;
84
- didUpdate . fire ( { old : oldEnv , new : merged } ) ;
85
- seen [ seen . indexOf ( oldEnv ) ] = merged ;
94
+ if ( ! isEqual ( oldEnv , merged ) ) {
95
+ didUpdate . fire ( { old : oldEnv , new : merged } ) ;
96
+ seen [ oldIndex ] = merged ;
97
+ }
86
98
state . pending -= 1 ;
87
99
checkIfFinishedAndNotify ( state , didUpdate ) ;
88
100
}
0 commit comments