@@ -3,7 +3,7 @@ import { Disposable, Event, EventEmitter, Uri } from 'vscode';
3
3
import { traceDecorators } from '../../../common/logger' ;
4
4
import { IPlatformService } from '../../../common/platform/types' ;
5
5
import { IDisposableRegistry } from '../../../common/types' ;
6
- import { createDeferred , Deferred } from '../../../common/utils/async' ;
6
+ import { chain , createDeferred , Deferred } from '../../../common/utils/async' ;
7
7
import { OSType } from '../../../common/utils/platform' ;
8
8
import {
9
9
CONDA_ENV_FILE_SERVICE ,
@@ -18,13 +18,112 @@ import {
18
18
WORKSPACE_VIRTUAL_ENV_SERVICE
19
19
} from '../../../interpreter/contracts' ;
20
20
import { IServiceContainer } from '../../../ioc/types' ;
21
+ import { PythonEnvInfo } from '../../base/info' ;
22
+ import { FullLocator , ILocator , NOOP_ITERATOR , PythonEnvsIterator , PythonLocatorQuery } from '../../base/locator' ;
23
+ import { DisableableLocator , Locators } from '../../base/locators' ;
21
24
import { PythonEnvironment } from '../../info' ;
22
25
import { isHiddenInterpreter } from './services/interpreterFilter' ;
23
26
import { GetInterpreterLocatorOptions } from './types' ;
24
27
25
28
// tslint:disable-next-line:no-require-imports no-var-requires
26
29
const flatten = require ( 'lodash/flatten' ) as typeof import ( 'lodash/flatten' ) ;
27
30
31
+ type WorkspaceLocatorFactory = ( root : Uri ) => ILocator [ ] ;
32
+
33
+ interface IWorkspaceFolders {
34
+ readonly roots : ReadonlyArray < Uri > ;
35
+ readonly onAdded : Event < Uri > ;
36
+ readonly onRemoved : Event < Uri > ;
37
+ }
38
+
39
+ type RootURI = string ;
40
+
41
+ /**
42
+ * The collection of all workspace-specific locators used by the extension.
43
+ *
44
+ * The factories are used to produce the locators for each workspace folder.
45
+ */
46
+ export class WorkspaceLocators extends FullLocator {
47
+ private readonly locators : Record < RootURI , DisableableLocator > = { } ;
48
+ private readonly roots : Record < RootURI , Uri > = { } ;
49
+ constructor (
50
+ // used to produce the per-root locators:
51
+ private readonly factories : WorkspaceLocatorFactory [ ]
52
+ ) {
53
+ super ( ) ;
54
+ }
55
+
56
+ /**
57
+ * Activate the locator.
58
+ *
59
+ * @param folders - the info used to keep track of the workspace folders
60
+ */
61
+ public activate ( folders : IWorkspaceFolders ) {
62
+ for ( const root of folders . roots ) {
63
+ this . addRoot ( root ) ;
64
+ }
65
+ folders . onAdded ( ( root : Uri ) => this . addRoot ( root ) ) ;
66
+ folders . onRemoved ( ( root : Uri ) => this . removeRoot ( root ) ) ;
67
+ }
68
+
69
+ public iterEnvs ( query : PythonLocatorQuery ) : PythonEnvsIterator {
70
+ const iterators = Object . keys ( this . locators ) . map ( ( key ) => {
71
+ if ( query . searchLocations ) {
72
+ const root = this . roots [ key ] ;
73
+ if ( ! matchURI ( root , ...query . searchLocations ) ) {
74
+ return NOOP_ITERATOR ;
75
+ }
76
+ }
77
+ // The query matches or was not location-specific.
78
+ const locator = this . locators [ key ] ;
79
+ return locator . iterEnvs ( query ) ;
80
+ } ) ;
81
+ return chain < PythonEnvInfo > ( iterators ) ;
82
+ }
83
+
84
+ private addRoot ( root : Uri ) {
85
+ // Drop the old one, necessary.
86
+ this . removeRoot ( root ) ;
87
+ // Create the root's locator, wrapping each factory-generated locator.
88
+ const locators : ILocator [ ] = [ ] ;
89
+ for ( const create of this . factories ) {
90
+ locators . push ( ...create ( root ) ) ;
91
+ }
92
+ const locator = new DisableableLocator ( new Locators ( locators ) ) ;
93
+ // Cache it.
94
+ const key = root . toString ( ) ;
95
+ this . locators [ key ] = locator ;
96
+ this . roots [ key ] = root ;
97
+ // Hook up the watchers.
98
+ locator . onChanged ( ( e ) => this . emitter . fire ( e ) ) ;
99
+ }
100
+
101
+ private removeRoot ( root : Uri ) {
102
+ const key = root . toString ( ) ;
103
+ const locator = this . locators [ key ] ;
104
+ if ( locator === undefined ) {
105
+ return ;
106
+ }
107
+ delete this . locators [ key ] ;
108
+ delete this . roots [ key ] ;
109
+ locator . disable ( ) ;
110
+ }
111
+ }
112
+
113
+ function matchURI ( uri : Uri , ...candidates : Uri [ ] ) : boolean {
114
+ const uriPath = uri . path . endsWith ( '/' ) ? uri . path : `{uri.path}/` ;
115
+ for ( const candidate of candidates ) {
116
+ if ( candidate . scheme === uri . scheme ) {
117
+ if ( candidate . path === uri . path ) {
118
+ return true ;
119
+ } else if ( candidate . path . startsWith ( uriPath ) ) {
120
+ return true ;
121
+ }
122
+ }
123
+ }
124
+ return false ;
125
+ }
126
+
28
127
/**
29
128
* Facilitates locating Python interpreters.
30
129
*/
0 commit comments