Skip to content

Commit 752c0cc

Browse files
author
Kartik Raj
authored
Ensure a refresh is only triggered after all handlers are registered in quickpicker (#19579)
* Ensure a refresh is only triggered after all handlers are registered for the quickpick * Do not show install python item until we're sure no python is installed * Fix tests
1 parent fa0e60a commit 752c0cc

File tree

5 files changed

+35
-10
lines changed

5 files changed

+35
-10
lines changed

src/client/common/utils/multiStepInput.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ export interface IQuickPickParameters<T extends QuickPickItem, E = any> {
5454
keepScrollPosition?: boolean;
5555
sortByLabel?: boolean;
5656
acceptFilterBoxTextAsSelection?: boolean;
57+
/**
58+
* A method called only after quickpick has been created and all handlers are registered.
59+
*/
60+
initialize?: () => void;
5761
onChangeItem?: {
5862
callback: (event: E, quickPick: QuickPick<T>) => void;
5963
event: Event<E>;
@@ -120,6 +124,7 @@ export class MultiStepInput<S> implements IMultiStepInput<S> {
120124
onChangeItem,
121125
keepScrollPosition,
122126
sortByLabel,
127+
initialize,
123128
}: P): Promise<MultiStepInputQuickPicResponseType<T, P>> {
124129
const disposables: Disposable[] = [];
125130
try {
@@ -175,6 +180,9 @@ export class MultiStepInput<S> implements IMultiStepInput<S> {
175180
disposables.push(onChangeItem.event((e) => onChangeItem.callback(e, input)));
176181
}
177182
this.current.show();
183+
if (initialize) {
184+
initialize();
185+
}
178186
// Keep scroll position is only meant to keep scroll position when updating items,
179187
// so do it after initialization. This ensures quickpick starts with the active
180188
// item in focus when this is true, instead of having scroll position at top.

src/client/interpreter/configuration/interpreterSelector/commands/setInterpreter.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,6 @@ export class SetInterpreterCommand extends BaseInterpreterSelectorCommand {
126126
// times so that the visible items do not change.
127127
const preserveOrderWhenFiltering = !!this.interpreterService.refreshPromise;
128128
const suggestions = this.getItems(state.workspace);
129-
// Discovery is no longer guranteed to be auto-triggered on extension load, so trigger it when
130-
// user interacts with the interpreter picker but only once per session. Users can rely on the
131-
// refresh button if they want to trigger it more than once.
132-
this.interpreterService.triggerRefresh(undefined, { ifNotTriggerredAlready: true }).ignoreErrors();
133129
state.path = undefined;
134130
const currentInterpreterPathDisplay = this.pathUtils.getDisplayName(
135131
this.configurationService.getSettings(state.workspace).pythonPath,
@@ -155,6 +151,17 @@ export class SetInterpreterCommand extends BaseInterpreterSelectorCommand {
155151
},
156152
callback: () => this.interpreterService.triggerRefresh().ignoreErrors(),
157153
},
154+
initialize: () => {
155+
// Note discovery is no longer guranteed to be auto-triggered on extension load, so trigger it when
156+
// user interacts with the interpreter picker but only once per session. Users can rely on the
157+
// refresh button if they want to trigger it more than once. However if no envs were found previously,
158+
// always trigger a refresh.
159+
if (this.interpreterService.getInterpreters().length === 0) {
160+
this.interpreterService.triggerRefresh().ignoreErrors();
161+
} else {
162+
this.interpreterService.triggerRefresh(undefined, { ifNotTriggerredAlready: true }).ignoreErrors();
163+
}
164+
},
158165
onChangeItem: {
159166
event: this.interpreterService.onDidChangeInterpreters,
160167
// It's essential that each callback is handled synchronously, as result of the previous
@@ -341,7 +348,8 @@ export class SetInterpreterCommand extends BaseInterpreterSelectorCommand {
341348

342349
private finalizeItems(items: QuickPickType[], resource: Resource) {
343350
const interpreterSuggestions = this.interpreterSelector.getSuggestions(resource, true);
344-
if (!this.interpreterService.refreshPromise) {
351+
const r = this.interpreterService.refreshPromise;
352+
if (!r) {
345353
if (interpreterSuggestions.length) {
346354
this.setRecommendedItem(interpreterSuggestions, items, resource);
347355
// Add warning label to certain environments
@@ -357,8 +365,13 @@ export class SetInterpreterCommand extends BaseInterpreterSelectorCommand {
357365
}
358366
});
359367
} else {
360-
items.push(this.noPythonInstalled);
361-
if (this.wasNoPythonInstalledItemClicked) {
368+
if (!items.some((i) => isSpecialQuickPickItem(i) && i.label === this.noPythonInstalled.label)) {
369+
items.push(this.noPythonInstalled);
370+
}
371+
if (
372+
this.wasNoPythonInstalledItemClicked &&
373+
!items.some((i) => isSpecialQuickPickItem(i) && i.label === this.tipToReloadWindow.label)
374+
) {
362375
items.push(this.tipToReloadWindow);
363376
}
364377
}

src/client/pythonEnvironments/base/locator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ export type TriggerRefreshOptions = {
199199
*/
200200
clearCache?: boolean;
201201
/**
202-
* Only trigger a refresh if it hasn't already been triggered for this session, or if no envs were found previously.
202+
* Only trigger a refresh if it hasn't already been triggered for this session.
203203
*/
204204
ifNotTriggerredAlready?: boolean;
205205
};

src/client/pythonEnvironments/base/locators/composite/envsCollectionService.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,8 @@ export class EnvsCollectionService extends PythonEnvsWatcher<PythonEnvCollection
104104
const stopWatch = new StopWatch();
105105
let refreshPromise = this.getRefreshPromiseForQuery(query);
106106
if (!refreshPromise) {
107-
if (options?.ifNotTriggerredAlready && this.hasRefreshFinished(query) && this.getEnvs().length) {
108-
// Do not trigger another refresh if a refresh has previously finished and envs were found.
107+
if (options?.ifNotTriggerredAlready && this.hasRefreshFinished(query)) {
108+
// Do not trigger another refresh if a refresh has previously finished.
109109
return Promise.resolve();
110110
}
111111
refreshPromise = this.startRefresh(query, options);

src/test/configuration/interpreterSelector/commands/setInterpreter.unit.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ suite('Set Interpreter Command', () => {
263263
expect(actualParameters).to.not.equal(undefined, 'Parameters not set');
264264
const refreshButtonCallback = actualParameters!.customButtonSetup?.callback;
265265
expect(refreshButtonCallback).to.not.equal(undefined, 'Callback not set');
266+
delete actualParameters!.initialize;
266267
delete actualParameters!.customButtonSetup;
267268
delete actualParameters!.onChangeItem;
268269
assert.deepStrictEqual(actualParameters, expectedParameters, 'Params not equal');
@@ -303,6 +304,7 @@ suite('Set Interpreter Command', () => {
303304
expect(actualParameters).to.not.equal(undefined, 'Parameters not set');
304305
const refreshButtonCallback = actualParameters!.customButtonSetup?.callback;
305306
expect(refreshButtonCallback).to.not.equal(undefined, 'Callback not set');
307+
delete actualParameters!.initialize;
306308
delete actualParameters!.customButtonSetup;
307309
delete actualParameters!.onChangeItem;
308310
assert.deepStrictEqual(actualParameters, expectedParameters, 'Params not equal');
@@ -458,6 +460,7 @@ suite('Set Interpreter Command', () => {
458460
expect(actualParameters).to.not.equal(undefined, 'Parameters not set');
459461
const refreshButtonCallback = actualParameters!.customButtonSetup?.callback;
460462
expect(refreshButtonCallback).to.not.equal(undefined, 'Callback not set');
463+
delete actualParameters!.initialize;
461464
delete actualParameters!.customButtonSetup;
462465
delete actualParameters!.onChangeItem;
463466
assert.deepStrictEqual(actualParameters?.items, expectedParameters.items, 'Params not equal');
@@ -542,6 +545,7 @@ suite('Set Interpreter Command', () => {
542545
const refreshButtonCallback = actualParameters!.customButtonSetup?.callback;
543546
expect(refreshButtonCallback).to.not.equal(undefined, 'Callback not set');
544547

548+
delete actualParameters!.initialize;
545549
delete actualParameters!.customButtonSetup;
546550
delete actualParameters!.onChangeItem;
547551

0 commit comments

Comments
 (0)