Skip to content

Commit e5c8fbb

Browse files
author
Kartik Raj
authored
Tie 'Python extension loading...' text with the new discovery component (#14925)
* Tie 'Python extension loading...' text with the new discovery component * Register via IExtensionSingleActivationService * Use addBinding instead
1 parent 979ffe6 commit e5c8fbb

File tree

3 files changed

+80
-39
lines changed

3 files changed

+80
-39
lines changed

src/client/interpreter/display/progressDisplay.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,23 @@
44
'use strict';
55

66
import { inject, injectable } from 'inversify';
7-
import { Disposable, ProgressLocation, ProgressOptions } from 'vscode';
7+
import { Disposable, Event, ProgressLocation, ProgressOptions } from 'vscode';
88
import { IApplicationShell } from '../../common/application/types';
99
import { traceDecorators } from '../../common/logger';
1010
import { IDisposableRegistry } from '../../common/types';
1111
import { createDeferred, Deferred } from '../../common/utils/async';
1212
import { Common, Interpreters } from '../../common/utils/localize';
13-
import { IInterpreterLocatorProgressHandler, IInterpreterLocatorProgressService } from '../contracts';
13+
import {
14+
IComponentAdapter,
15+
IInterpreterLocatorProgressHandler,
16+
IInterpreterLocatorProgressService
17+
} from '../contracts';
1418

19+
// The parts of IComponentAdapter used here.
20+
export interface IComponent {
21+
readonly onRefreshing: Event<void> | undefined;
22+
readonly onRefreshed: Event<void> | undefined;
23+
}
1524
@injectable()
1625
export class InterpreterLocatorProgressStatubarHandler implements IInterpreterLocatorProgressHandler {
1726
private deferred: Deferred<void> | undefined;
@@ -20,11 +29,14 @@ export class InterpreterLocatorProgressStatubarHandler implements IInterpreterLo
2029
@inject(IApplicationShell) private readonly shell: IApplicationShell,
2130
@inject(IInterpreterLocatorProgressService)
2231
private readonly progressService: IInterpreterLocatorProgressService,
23-
@inject(IDisposableRegistry) private readonly disposables: Disposable[]
32+
@inject(IDisposableRegistry) private readonly disposables: Disposable[],
33+
@inject(IComponentAdapter) private readonly pyenvs: IComponent
2434
) {}
2535
public register() {
26-
this.progressService.onRefreshing(() => this.showProgress(), this, this.disposables);
27-
this.progressService.onRefreshed(() => this.hideProgress(), this, this.disposables);
36+
const onRefreshing = this.pyenvs.onRefreshing ?? this.progressService.onRefreshing;
37+
const onRefreshed = this.pyenvs.onRefreshed ?? this.progressService.onRefreshed;
38+
onRefreshing(() => this.showProgress(), this, this.disposables);
39+
onRefreshed(() => this.hideProgress(), this, this.disposables);
2840
}
2941
@traceDecorators.verbose('Display locator refreshing progress')
3042
private showProgress(): void {

src/client/pythonEnvironments/legacyIOC.ts

Lines changed: 44 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import { injectable } from 'inversify';
55
import * as vscode from 'vscode';
6+
import { IExtensionSingleActivationService } from '../activation/types';
67
import { DiscoveryVariants } from '../common/experiments/groups';
78
import { getVersionString, parseVersion } from '../common/utils/version';
89
import {
@@ -135,21 +136,45 @@ function convertEnvInfo(info: PythonEnvInfo): PythonEnvironment {
135136
export interface IPythonEnvironments extends ILocator {}
136137

137138
@injectable()
138-
class ComponentAdapter implements IComponentAdapter {
139+
class ComponentAdapter implements IComponentAdapter, IExtensionSingleActivationService {
139140
// this will be set based on experiment
140-
private _enabled?: boolean;
141+
private enabled?: boolean;
142+
143+
private readonly refreshing = new vscode.EventEmitter<void>();
144+
145+
private readonly refreshed = new vscode.EventEmitter<void>();
141146

142147
constructor(
143148
// The adapter only wraps one thing: the component API.
144149
private readonly api: IPythonEnvironments,
145150
private readonly environmentsSecurity: IEnvironmentsSecurity,
146151
) {}
147152

153+
public async activate(): Promise<void> {
154+
this.enabled = (await Promise.all(
155+
[
156+
inExperiment(DiscoveryVariants.discoverWithFileWatching),
157+
inExperiment(DiscoveryVariants.discoveryWithoutFileWatching),
158+
],
159+
)).includes(true);
160+
}
161+
162+
// IInterpreterLocatorProgressHandler
163+
164+
// A result of `undefined` means "Fall back to the old code!"
165+
public get onRefreshing(): vscode.Event<void> | undefined {
166+
return this.enabled ? this.refreshing.event : undefined;
167+
}
168+
169+
public get onRefreshed(): vscode.Event<void> | undefined {
170+
return this.enabled ? this.refreshing.event : undefined;
171+
}
172+
148173
// IInterpreterHelper
149174

150175
// A result of `undefined` means "Fall back to the old code!"
151176
public async getInterpreterInformation(pythonPath: string): Promise<undefined | Partial<PythonEnvironment>> {
152-
if (!(await this.isEnabled())) {
177+
if (!this.enabled) {
153178
return undefined;
154179
}
155180
const env = await this.api.resolveEnv(pythonPath);
@@ -161,7 +186,7 @@ class ComponentAdapter implements IComponentAdapter {
161186

162187
// A result of `undefined` means "Fall back to the old code!"
163188
public async isMacDefaultPythonPath(pythonPath: string): Promise<boolean | undefined> {
164-
if (!(await this.isEnabled())) {
189+
if (!this.enabled) {
165190
return undefined;
166191
}
167192
const env = await this.api.resolveEnv(pythonPath);
@@ -180,7 +205,7 @@ class ComponentAdapter implements IComponentAdapter {
180205
pythonPath: string,
181206
resource?: vscode.Uri,
182207
): Promise<undefined | PythonEnvironment> {
183-
if (!(await this.isEnabled())) {
208+
if (!this.enabled) {
184209
return undefined;
185210
}
186211
const info = buildEnvInfo({ executable: pythonPath });
@@ -201,7 +226,7 @@ class ComponentAdapter implements IComponentAdapter {
201226

202227
// A result of `undefined` means "Fall back to the old code!"
203228
public async isCondaEnvironment(interpreterPath: string): Promise<boolean | undefined> {
204-
if (!(await this.isEnabled())) {
229+
if (!this.enabled) {
205230
return undefined;
206231
}
207232
const env = await this.api.resolveEnv(interpreterPath);
@@ -213,7 +238,7 @@ class ComponentAdapter implements IComponentAdapter {
213238

214239
// A result of `undefined` means "Fall back to the old code!"
215240
public async getCondaEnvironment(interpreterPath: string): Promise<CondaEnvironmentInfo | undefined> {
216-
if (!(await this.isEnabled())) {
241+
if (!this.enabled) {
217242
return undefined;
218243
}
219244
const env = await this.api.resolveEnv(interpreterPath);
@@ -234,7 +259,7 @@ class ComponentAdapter implements IComponentAdapter {
234259

235260
// A result of `undefined` means "Fall back to the old code!"
236261
public async isWindowsStoreInterpreter(pythonPath: string): Promise<boolean | undefined> {
237-
if (!(await this.isEnabled())) {
262+
if (!this.enabled) {
238263
return undefined;
239264
}
240265
const env = await this.api.resolveEnv(pythonPath);
@@ -248,13 +273,11 @@ class ComponentAdapter implements IComponentAdapter {
248273

249274
// A result of `undefined` means "Fall back to the old code!"
250275
public get hasInterpreters(): Promise<boolean | undefined> {
251-
return this.isEnabled().then((enabled) => {
252-
if (enabled) {
253-
const iterator = this.api.iterEnvs();
254-
return iterator.next().then((res) => !res.done);
255-
}
256-
return undefined;
257-
});
276+
if (!this.enabled) {
277+
return Promise.resolve(undefined);
278+
}
279+
const iterator = this.api.iterEnvs();
280+
return iterator.next().then((res) => !res.done);
258281
}
259282

260283
// A result of `undefined` means "Fall back to the old code!"
@@ -267,9 +290,10 @@ class ComponentAdapter implements IComponentAdapter {
267290
// onSuggestion?: boolean;
268291
// }
269292
): Promise<PythonEnvironment[] | undefined> {
270-
if (!(await this.isEnabled())) {
293+
if (!this.enabled) {
271294
return undefined;
272295
}
296+
this.refreshing.fire(); // Notify locators are locating.
273297
if (options?.onSuggestion) {
274298
// For now, until we have the concept of trusted workspaces, we assume all interpreters as safe
275299
// to run once user has triggered discovery, i.e interacted with the extension.
@@ -288,20 +312,9 @@ class ComponentAdapter implements IComponentAdapter {
288312

289313
const iterator = this.api.iterEnvs(query);
290314
const envs = await getEnvs(iterator);
291-
return envs.map(convertEnvInfo);
292-
}
293-
294-
private async isEnabled(): Promise<boolean> {
295-
if (this._enabled === undefined) {
296-
this._enabled = (await Promise.all(
297-
[
298-
inExperiment(DiscoveryVariants.discoverWithFileWatching),
299-
inExperiment(DiscoveryVariants.discoveryWithoutFileWatching),
300-
],
301-
)).includes(true);
302-
}
303-
304-
return this._enabled;
315+
const legacyEnvs = envs.map(convertEnvInfo);
316+
this.refreshed.fire(); // Notify all locators have completed locating.
317+
return legacyEnvs;
305318
}
306319
}
307320

@@ -399,4 +412,5 @@ export function registerNewDiscoveryForIOC(
399412
IComponentAdapter,
400413
new ComponentAdapter(api, environmentsSecurity),
401414
);
415+
serviceManager.addBinding(IComponentAdapter, IExtensionSingleActivationService);
402416
}

src/test/interpreters/display/progressDisplay.unit.test.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { CancellationToken, Disposable, Progress, ProgressOptions } from 'vscode
1111
import { ApplicationShell } from '../../../client/common/application/applicationShell';
1212
import { Common, Interpreters } from '../../../client/common/utils/localize';
1313
import { noop } from '../../../client/common/utils/misc';
14-
import { IInterpreterLocatorProgressService } from '../../../client/interpreter/contracts';
14+
import { IComponentAdapter, IInterpreterLocatorProgressService } from '../../../client/interpreter/contracts';
1515
import { InterpreterLocatorProgressStatubarHandler } from '../../../client/interpreter/display/progressDisplay';
1616

1717
type ProgressTask<R> = (
@@ -38,7 +38,12 @@ suite('Interpreters - Display Progress', () => {
3838

3939
test('Display loading message when refreshing interpreters for the first time', async () => {
4040
const shell = mock(ApplicationShell);
41-
const statusBar = new InterpreterLocatorProgressStatubarHandler(instance(shell), progressService, []);
41+
const statusBar = new InterpreterLocatorProgressStatubarHandler(
42+
instance(shell),
43+
progressService,
44+
[],
45+
instance(mock(IComponentAdapter))
46+
);
4247
when(shell.withProgress(anything(), anything())).thenResolve();
4348

4449
statusBar.register();
@@ -50,7 +55,12 @@ suite('Interpreters - Display Progress', () => {
5055

5156
test('Display refreshing message when refreshing interpreters for the second time', async () => {
5257
const shell = mock(ApplicationShell);
53-
const statusBar = new InterpreterLocatorProgressStatubarHandler(instance(shell), progressService, []);
58+
const statusBar = new InterpreterLocatorProgressStatubarHandler(
59+
instance(shell),
60+
progressService,
61+
[],
62+
instance(mock(IComponentAdapter))
63+
);
5464
when(shell.withProgress(anything(), anything())).thenResolve();
5565

5666
statusBar.register();
@@ -67,7 +77,12 @@ suite('Interpreters - Display Progress', () => {
6777

6878
test('Progress message is hidden when loading has completed', async () => {
6979
const shell = mock(ApplicationShell);
70-
const statusBar = new InterpreterLocatorProgressStatubarHandler(instance(shell), progressService, []);
80+
const statusBar = new InterpreterLocatorProgressStatubarHandler(
81+
instance(shell),
82+
progressService,
83+
[],
84+
instance(mock(IComponentAdapter))
85+
);
7186
when(shell.withProgress(anything(), anything())).thenResolve();
7287

7388
statusBar.register();

0 commit comments

Comments
 (0)