Skip to content

Commit 545209b

Browse files
Add WorkspaceLocators.
1 parent d5e1983 commit 545209b

File tree

1 file changed

+100
-1
lines changed
  • src/client/pythonEnvironments/discovery/locators

1 file changed

+100
-1
lines changed

src/client/pythonEnvironments/discovery/locators/index.ts

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Disposable, Event, EventEmitter, Uri } from 'vscode';
33
import { traceDecorators } from '../../../common/logger';
44
import { IPlatformService } from '../../../common/platform/types';
55
import { IDisposableRegistry } from '../../../common/types';
6-
import { createDeferred, Deferred } from '../../../common/utils/async';
6+
import { chain, createDeferred, Deferred } from '../../../common/utils/async';
77
import { OSType } from '../../../common/utils/platform';
88
import {
99
CONDA_ENV_FILE_SERVICE,
@@ -18,13 +18,112 @@ import {
1818
WORKSPACE_VIRTUAL_ENV_SERVICE
1919
} from '../../../interpreter/contracts';
2020
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';
2124
import { PythonEnvironment } from '../../info';
2225
import { isHiddenInterpreter } from './services/interpreterFilter';
2326
import { GetInterpreterLocatorOptions } from './types';
2427

2528
// tslint:disable-next-line:no-require-imports no-var-requires
2629
const flatten = require('lodash/flatten') as typeof import('lodash/flatten');
2730

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+
28127
/**
29128
* Facilitates locating Python interpreters.
30129
*/

0 commit comments

Comments
 (0)