diff --git a/src/vs/platform/quickinput/browser/pickerQuickAccess.ts b/src/vs/platform/quickinput/browser/pickerQuickAccess.ts index cfda01a95e5..f016726556a 100644 --- a/src/vs/platform/quickinput/browser/pickerQuickAccess.ts +++ b/src/vs/platform/quickinput/browser/pickerQuickAccess.ts @@ -95,7 +95,12 @@ export abstract class PickerQuickAccessProvider 0) { + // Optimization: if there are no fast results + // we do nto simply unset all the existing items + // to reduce the flickering. + picker.items = res.picks; + } picker.busy = true; try { const additionalPicks = await res.additionalPicks; @@ -103,7 +108,9 @@ export abstract class PickerQuickAccessProvider 0) { + if (res.picks.length === 0 || additionalPicks.length > 0) { + // Optimization: we only update the picker items if we either + // did not update them earlier, or we actually got new results picker.items = [...res.picks, ...additionalPicks]; } } finally { @@ -187,5 +194,5 @@ export abstract class PickerQuickAccessProvider | Promise> | { picks: Array, additionalPicks: Promise> }; + protected abstract getPicks(filter: string, disposables: DisposableStore, token: CancellationToken): Array | Promise> | FastAndSlowPicksType; } diff --git a/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts b/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts index d7bcac67b7d..b04f60dae01 100644 --- a/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts @@ -105,7 +105,7 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider { + protected getPicks(filter: string, disposables: DisposableStore, token: CancellationToken): FastAndSlowPicksType { // Find a suitable range from the pattern looking for ":", "#" or "," let range: IRange | undefined = undefined; diff --git a/src/vs/workbench/test/browser/quickAccess.test.ts b/src/vs/workbench/test/browser/quickAccess.test.ts index d265406e99b..c8d507156ca 100644 --- a/src/vs/workbench/test/browser/quickAccess.test.ts +++ b/src/vs/workbench/test/browser/quickAccess.test.ts @@ -12,6 +12,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { TestServiceAccessor, workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; import { DisposableStore, toDisposable, IDisposable } from 'vs/base/common/lifecycle'; import { timeout } from 'vs/base/common/async'; +import { PickerQuickAccessProvider, FastAndSlowPicksType } from 'vs/platform/quickinput/browser/pickerQuickAccess'; suite('QuickAccess', () => { @@ -192,4 +193,122 @@ suite('QuickAccess', () => { restore(); }); + + let fastProviderCalled = false; + let slowProviderCalled = false; + let fastAndSlowProviderCalled = false; + + let slowProviderCanceled = false; + let fastAndSlowProviderCanceled = false; + + class FastTestQuickPickProvider extends PickerQuickAccessProvider { + + constructor() { + super('fast'); + } + + protected getPicks(filter: string, disposables: DisposableStore, token: CancellationToken): Array { + fastProviderCalled = true; + + return [{ label: 'Fast Pick' }]; + } + } + + class SlowTestQuickPickProvider extends PickerQuickAccessProvider { + + constructor() { + super('slow'); + } + + protected async getPicks(filter: string, disposables: DisposableStore, token: CancellationToken): Promise> { + slowProviderCalled = true; + + await timeout(1); + + if (token.isCancellationRequested) { + slowProviderCanceled = true; + } + + return [{ label: 'Slow Pick' }]; + } + } + + class FastAndSlowTestQuickPickProvider extends PickerQuickAccessProvider { + + constructor() { + super('bothFastAndSlow'); + } + + protected getPicks(filter: string, disposables: DisposableStore, token: CancellationToken): FastAndSlowPicksType { + fastAndSlowProviderCalled = true; + + return { + picks: [{ label: 'Fast Pick' }], + additionalPicks: (async () => { + await timeout(1); + + if (token.isCancellationRequested) { + fastAndSlowProviderCanceled = true; + } + + return [{ label: 'Slow Pick' }]; + })() + }; + } + } + + const fastProviderDescriptor = { ctor: FastTestQuickPickProvider, prefix: 'fast', helpEntries: [] }; + const slowProviderDescriptor = { ctor: SlowTestQuickPickProvider, prefix: 'slow', helpEntries: [] }; + const fastAndSlowProviderDescriptor = { ctor: FastAndSlowTestQuickPickProvider, prefix: 'bothFastAndSlow', helpEntries: [] }; + + test('quick pick access', async () => { + const registry = (Registry.as(Extensions.Quickaccess)); + const restore = (registry as QuickAccessRegistry).clear(); + + const disposables = new DisposableStore(); + + disposables.add(registry.registerQuickAccessProvider(fastProviderDescriptor)); + disposables.add(registry.registerQuickAccessProvider(slowProviderDescriptor)); + disposables.add(registry.registerQuickAccessProvider(fastAndSlowProviderDescriptor)); + + accessor.quickInputService.quickAccess.show('fast'); + assert.equal(fastProviderCalled, true); + assert.equal(slowProviderCalled, false); + assert.equal(fastAndSlowProviderCalled, false); + fastProviderCalled = false; + + accessor.quickInputService.quickAccess.show('slow'); + await timeout(2); + + assert.equal(fastProviderCalled, false); + assert.equal(slowProviderCalled, true); + assert.equal(slowProviderCanceled, false); + assert.equal(fastAndSlowProviderCalled, false); + slowProviderCalled = false; + + accessor.quickInputService.quickAccess.show('bothFastAndSlow'); + await timeout(2); + + assert.equal(fastProviderCalled, false); + assert.equal(slowProviderCalled, false); + assert.equal(fastAndSlowProviderCalled, true); + assert.equal(fastAndSlowProviderCanceled, false); + fastAndSlowProviderCalled = false; + + accessor.quickInputService.quickAccess.show('slow'); + accessor.quickInputService.quickAccess.show('bothFastAndSlow'); + accessor.quickInputService.quickAccess.show('fast'); + + assert.equal(fastProviderCalled, true); + assert.equal(slowProviderCalled, true); + assert.equal(fastAndSlowProviderCalled, true); + + await timeout(2); + assert.equal(slowProviderCanceled, true); + assert.equal(fastAndSlowProviderCanceled, true); + + disposables.dispose(); + + restore(); + }); });