Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

quick access - allow a Promise<FastAndSlowPicks<T>> and adopt for commands #180664

Merged
merged 2 commits into from
Apr 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion extensions/microsoft-authentication/src/AADHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ export class AzureActiveDirectoryService {
"login" : {
"owner": "TylerLeonhardt",
"comment": "Used to determine the usage of the Microsoft Auth Provider.",
"scopes": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight", "comment": "Used to determine what scope combinations are being requested." }
"scopes": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight", "comment": "Used to determine what scope combinations are being requested." },
"accountType": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight", "comment": "Used to determine what account types are being used." }
}
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import { themeColorFromId } from 'vs/platform/theme/common/themeService';
import { alert } from 'vs/base/browser/ui/aria/aria';

interface IEditorLineDecoration {
rangeHighlightId: string;
overviewRulerDecorationId: string;
readonly rangeHighlightId: string;
readonly overviewRulerDecorationId: string;
}

export interface IEditorNavigationQuickAccessOptions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,14 @@ export class StandaloneCommandsQuickAccessProvider extends AbstractEditorCommand
super({ showAlias: false }, instantiationService, keybindingService, commandService, telemetryService, dialogService);
}

protected getCommandPicks(): Array<ICommandQuickPick> {
protected async getCommandPicks(): Promise<Array<ICommandQuickPick>> {
return this.getCodeEditorCommandPicks();
}

protected hasAdditionalCommandPicks(): boolean {
return false;
}

protected async getAdditionalCommandPicks(): Promise<ICommandQuickPick[]> {
return [];
}
Expand Down
46 changes: 26 additions & 20 deletions src/vs/platform/quickinput/browser/commandsQuickAccess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';

export interface ICommandQuickPick extends IPickerQuickAccessItem {
commandId: string;
commandAlias?: string;
readonly commandId: string;
readonly commandAlias?: string;
}

export interface ICommandsQuickAccessOptions extends IPickerQuickAccessProviderOptions<ICommandQuickPick> {
showAlias: boolean;
readonly showAlias: boolean;
suggestedCommandIds?: Set<string>;
}

Expand All @@ -56,10 +56,10 @@ export abstract class AbstractCommandsQuickAccessProvider extends PickerQuickAcc
this.options = options;
}

protected _getPicks(filter: string, _disposables: DisposableStore, token: CancellationToken, runOptions?: IQuickAccessProviderRunOptions): Picks<ICommandQuickPick> | FastAndSlowPicks<ICommandQuickPick> {
protected async _getPicks(filter: string, _disposables: DisposableStore, token: CancellationToken, runOptions?: IQuickAccessProviderRunOptions): Promise<Picks<ICommandQuickPick> | FastAndSlowPicks<ICommandQuickPick>> {

// Ask subclass for all command picks
const allCommandPicks = this.getCommandPicks(token);
const allCommandPicks = await this.getCommandPicks(token);

if (token.isCancellationRequested) {
return [];
Expand Down Expand Up @@ -166,17 +166,28 @@ export abstract class AbstractCommandsQuickAccessProvider extends PickerQuickAcc
commandPicks.push(this.toCommandPick(commandPick, runOptions));
}

if (!this.hasAdditionalCommandPicks(filter, token)) {
return commandPicks;
}

return {
picks: commandPicks,
additionalPicks: this.getAdditionalCommandPicks(allCommandPicks, filteredCommandPicks, filter, token)
.then(additionalCommandPicks => additionalCommandPicks.map(commandPick => this.toCommandPick(commandPick, runOptions)))
additionalPicks: (async (): Promise<Picks<ICommandQuickPick>> => {
const additionalCommandPicks = await this.getAdditionalCommandPicks(allCommandPicks, filteredCommandPicks, filter, token);
if (token.isCancellationRequested) {
return [];
}

return additionalCommandPicks.map(commandPick => this.toCommandPick(commandPick, runOptions));
})()
};
}

private toCommandPick(commandPick: ICommandQuickPick | IQuickPickSeparator, runOptions?: IQuickAccessProviderRunOptions): ICommandQuickPick | IQuickPickSeparator {
if (commandPick.type === 'separator') {
return commandPick;
}

const keybinding = this.keybindingService.lookupKeybinding(commandPick.commandId);
const ariaLabel = keybinding ?
localize('commandPickAriaLabelWithKeybinding', "{0}, {1}", commandPick.label, keybinding.getAriaLabel()) :
Expand Down Expand Up @@ -210,27 +221,22 @@ export abstract class AbstractCommandsQuickAccessProvider extends PickerQuickAcc
};
}

/**
* Subclasses to provide the actual command entries.
*/
protected abstract getCommandPicks(token: CancellationToken): Array<ICommandQuickPick>;
protected abstract getCommandPicks(token: CancellationToken): Promise<Array<ICommandQuickPick>>;

/**
* Subclasses to provide the actual command entries.
*/
protected abstract hasAdditionalCommandPicks(filter: string, token: CancellationToken): boolean;
protected abstract getAdditionalCommandPicks(allPicks: ICommandQuickPick[], picksSoFar: ICommandQuickPick[], filter: string, token: CancellationToken): Promise<Array<ICommandQuickPick | IQuickPickSeparator>>;
}

interface ISerializedCommandHistory {
usesLRU?: boolean;
entries: { key: string; value: number }[];
readonly usesLRU?: boolean;
readonly entries: { key: string; value: number }[];
}

interface ICommandsQuickAccessConfiguration {
workbench: {
commandPalette: {
history: number;
preserveInput: boolean;
readonly workbench: {
readonly commandPalette: {
readonly history: number;
readonly preserveInput: boolean;
};
};
}
Expand Down
3 changes: 1 addition & 2 deletions src/vs/platform/quickinput/browser/helpQuickAccess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { Extensions, IQuickAccessProvider, IQuickAccessProviderDescriptor, IQuic
import { IQuickInputService, IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';

interface IHelpQuickAccessPickItem extends IQuickPickItem {
prefix: string;
readonly prefix: string;
}

export class HelpQuickAccessProvider implements IQuickAccessProvider {
Expand Down Expand Up @@ -75,4 +75,3 @@ export class HelpQuickAccessProvider implements IQuickAccessProvider {
});
}
}

84 changes: 57 additions & 27 deletions src/vs/platform/quickinput/browser/pickerQuickAccess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,18 +64,37 @@ export interface IPickerQuickAccessProviderOptions<T extends IPickerQuickAccessI
/**
* Enables support for opening picks in the background via gesture.
*/
canAcceptInBackground?: boolean;
readonly canAcceptInBackground?: boolean;

/**
* Enables to show a pick entry when no results are returned from a search.
*/
noResultsPick?: T | ((filter: string) => T);
readonly noResultsPick?: T | ((filter: string) => T);
}

export type Pick<T> = T | IQuickPickSeparator;
export type PicksWithActive<T> = { items: readonly Pick<T>[]; active?: T };
export type Picks<T> = readonly Pick<T>[] | PicksWithActive<T>;
export type FastAndSlowPicks<T> = { picks: Picks<T>; additionalPicks: Promise<Picks<T>> };
export type FastAndSlowPicks<T> = {

/**
* Picks that will show instantly or after a short delay
* based on the `mergeDelay` property to reduce flicker.
*/
readonly picks: Picks<T>;

/**
* Picks that will show after they have been resolved.
*/
readonly additionalPicks: Promise<Picks<T>>;

/**
* A delay in milliseconds to wait before showing the
* `picks` to give a chance to merge with `additionalPicks`
* for reduced flicker.
*/
readonly mergeDelay?: number;
};

function isPicksWithActive<T>(obj: unknown): obj is PicksWithActive<T> {
const candidate = obj as PicksWithActive<T>;
Expand All @@ -91,8 +110,6 @@ function isFastAndSlowPicks<T>(obj: unknown): obj is FastAndSlowPicks<T> {

export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem> extends Disposable implements IQuickAccessProvider {

private static FAST_PICKS_RACE_DELAY = 200; // timeout before we accept fast results before slow results are present

constructor(private prefix: string, protected options?: IPickerQuickAccessProviderOptions<T>) {
super();
}
Expand Down Expand Up @@ -158,51 +175,50 @@ export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem
return true;
};

// No Picks
if (providedPicks === null) {
// Ignore
}

// Fast and Slow Picks
else if (isFastAndSlowPicks(providedPicks)) {
const applyFastAndSlowPicks = async (fastAndSlowPicks: FastAndSlowPicks<T>): Promise<void> => {
let fastPicksApplied = false;
let slowPicksApplied = false;

await Promise.all([

// Fast Picks: to reduce amount of flicker, we race against
// the slow picks over 500ms and then set the fast picks.
// If the slow picks are faster, we reduce the flicker by
// only setting the items once.
// Fast Picks: if `mergeDelay` is configured, in order to reduce
// amount of flicker, we race against the slow picks over some delay
// and then set the fast picks.
// If the slow picks are faster, we reduce the flicker by only
// setting the items once.

(async () => {
await timeout(PickerQuickAccessProvider.FAST_PICKS_RACE_DELAY);
if (picksToken.isCancellationRequested) {
return;
if (typeof fastAndSlowPicks.mergeDelay === 'number') {
await timeout(fastAndSlowPicks.mergeDelay);
if (picksToken.isCancellationRequested) {
return;
}
}

if (!slowPicksApplied) {
fastPicksApplied = applyPicks(providedPicks.picks, true /* skip over empty to reduce flicker */);
fastPicksApplied = applyPicks(fastAndSlowPicks.picks, true /* skip over empty to reduce flicker */);
}
})(),

// Slow Picks: we await the slow picks and then set them at
// once together with the fast picks, but only if we actually
// have additional results.

(async () => {
picker.busy = true;
try {
const awaitedAdditionalPicks = await providedPicks.additionalPicks;
const awaitedAdditionalPicks = await fastAndSlowPicks.additionalPicks;
if (picksToken.isCancellationRequested) {
return;
}

let picks: readonly Pick<T>[];
let activePick: Pick<T> | undefined = undefined;
if (isPicksWithActive(providedPicks.picks)) {
picks = providedPicks.picks.items;
activePick = providedPicks.picks.active;
if (isPicksWithActive(fastAndSlowPicks.picks)) {
picks = fastAndSlowPicks.picks.items;
activePick = fastAndSlowPicks.picks.active;
} else {
picks = providedPicks.picks;
picks = fastAndSlowPicks.picks;
}

let additionalPicks: readonly Pick<T>[];
Expand Down Expand Up @@ -243,6 +259,16 @@ export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem
}
})()
]);
};

// No Picks
if (providedPicks === null) {
// Ignore
}

// Fast and Slow Picks
else if (isFastAndSlowPicks(providedPicks)) {
await applyFastAndSlowPicks(providedPicks);
}

// Fast Picks
Expand All @@ -259,7 +285,11 @@ export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem
return;
}

applyPicks(awaitedPicks);
if (isFastAndSlowPicks(awaitedPicks)) {
await applyFastAndSlowPicks(awaitedPicks);
} else {
applyPicks(awaitedPicks);
}
} finally {
if (!picksToken.isCancellationRequested) {
picker.busy = false;
Expand Down Expand Up @@ -343,5 +373,5 @@ export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem
* @returns the picks either directly, as promise or combined fast and slow results.
* Pickers can return `null` to signal that no change in picks is needed.
*/
protected abstract _getPicks(filter: string, disposables: DisposableStore, token: CancellationToken, runOptions?: IQuickAccessProviderRunOptions): Picks<T> | Promise<Picks<T>> | FastAndSlowPicks<T> | null;
protected abstract _getPicks(filter: string, disposables: DisposableStore, token: CancellationToken, runOptions?: IQuickAccessProviderRunOptions): Picks<T> | Promise<Picks<T> | FastAndSlowPicks<T>> | FastAndSlowPicks<T> | null;
}
6 changes: 3 additions & 3 deletions src/vs/platform/quickinput/browser/quickAccess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon
private readonly lastAcceptedPickerValues = new Map<IQuickAccessProviderDescriptor, string>();

private visibleQuickAccess: {
picker: IQuickPick<IQuickPickItem>;
descriptor: IQuickAccessProviderDescriptor | undefined;
value: string;
readonly picker: IQuickPick<IQuickPickItem>;
readonly descriptor: IQuickAccessProviderDescriptor | undefined;
readonly value: string;
} | undefined = undefined;

constructor(
Expand Down
17 changes: 9 additions & 8 deletions src/vs/platform/quickinput/common/quickAccess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,40 +14,40 @@ import { Registry } from 'vs/platform/registry/common/platform';
* quick access.
*/
export interface IQuickAccessProviderRunOptions {
from?: string;
readonly from?: string;
}

/**
* The specific options for the AnythingQuickAccessProvider. Put here to share between layers.
*/
export interface AnythingQuickAccessProviderRunOptions extends IQuickAccessProviderRunOptions {
includeHelp?: boolean;
readonly includeHelp?: boolean;
}

export interface IQuickAccessOptions {

/**
* Allows to enable quick navigate support in quick input.
*/
quickNavigateConfiguration?: IQuickNavigateConfiguration;
readonly quickNavigateConfiguration?: IQuickNavigateConfiguration;

/**
* Allows to configure a different item activation strategy.
* By default the first item in the list will get activated.
*/
itemActivation?: ItemActivation;
readonly itemActivation?: ItemActivation;

/**
* Whether to take the input value as is and not restore it
* from any existing value if quick access is visible.
*/
preserveValue?: boolean;
readonly preserveValue?: boolean;

/**
* Provider specific options for this particular showing of the
* quick access.
*/
providerOptions?: IQuickAccessProviderRunOptions;
readonly providerOptions?: IQuickAccessProviderRunOptions;
}

export interface IQuickAccessController {
Expand Down Expand Up @@ -114,12 +114,12 @@ export interface IQuickAccessProviderHelp {
* The prefix to show for the help entry. If not provided,
* the prefix used for registration will be taken.
*/
prefix?: string;
readonly prefix?: string;

/**
* A description text to help understand the intent of the provider.
*/
description: string;
readonly description: string;

/**
* The command to bring up this quick access provider.
Expand Down Expand Up @@ -181,6 +181,7 @@ export interface IQuickAccessRegistry {
}

export class QuickAccessRegistry implements IQuickAccessRegistry {

private providers: IQuickAccessProviderDescriptor[] = [];
private defaultProvider: IQuickAccessProviderDescriptor | undefined = undefined;

Expand Down
Loading