diff --git a/extensions/microsoft-authentication/src/AADHelper.ts b/extensions/microsoft-authentication/src/AADHelper.ts index 7c7724d05b03b..9d77be2a4f8ca 100644 --- a/extensions/microsoft-authentication/src/AADHelper.ts +++ b/extensions/microsoft-authentication/src/AADHelper.ts @@ -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." } } */ diff --git a/src/vs/editor/contrib/quickAccess/browser/editorNavigationQuickAccess.ts b/src/vs/editor/contrib/quickAccess/browser/editorNavigationQuickAccess.ts index f76e4b868b6e3..98d97438d14da 100644 --- a/src/vs/editor/contrib/quickAccess/browser/editorNavigationQuickAccess.ts +++ b/src/vs/editor/contrib/quickAccess/browser/editorNavigationQuickAccess.ts @@ -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 { diff --git a/src/vs/editor/standalone/browser/quickAccess/standaloneCommandsQuickAccess.ts b/src/vs/editor/standalone/browser/quickAccess/standaloneCommandsQuickAccess.ts index cf853302d6fe5..5e87aa8f71050 100644 --- a/src/vs/editor/standalone/browser/quickAccess/standaloneCommandsQuickAccess.ts +++ b/src/vs/editor/standalone/browser/quickAccess/standaloneCommandsQuickAccess.ts @@ -37,10 +37,14 @@ export class StandaloneCommandsQuickAccessProvider extends AbstractEditorCommand super({ showAlias: false }, instantiationService, keybindingService, commandService, telemetryService, dialogService); } - protected getCommandPicks(): Array { + protected async getCommandPicks(): Promise> { return this.getCodeEditorCommandPicks(); } + protected hasAdditionalCommandPicks(): boolean { + return false; + } + protected async getAdditionalCommandPicks(): Promise { return []; } diff --git a/src/vs/platform/quickinput/browser/commandsQuickAccess.ts b/src/vs/platform/quickinput/browser/commandsQuickAccess.ts index 607a0bd91c073..05e07a1543dda 100644 --- a/src/vs/platform/quickinput/browser/commandsQuickAccess.ts +++ b/src/vs/platform/quickinput/browser/commandsQuickAccess.ts @@ -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 { - showAlias: boolean; + readonly showAlias: boolean; suggestedCommandIds?: Set; } @@ -56,10 +56,10 @@ export abstract class AbstractCommandsQuickAccessProvider extends PickerQuickAcc this.options = options; } - protected _getPicks(filter: string, _disposables: DisposableStore, token: CancellationToken, runOptions?: IQuickAccessProviderRunOptions): Picks | FastAndSlowPicks { + protected async _getPicks(filter: string, _disposables: DisposableStore, token: CancellationToken, runOptions?: IQuickAccessProviderRunOptions): Promise | FastAndSlowPicks> { // Ask subclass for all command picks - const allCommandPicks = this.getCommandPicks(token); + const allCommandPicks = await this.getCommandPicks(token); if (token.isCancellationRequested) { return []; @@ -166,10 +166,20 @@ 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> => { + const additionalCommandPicks = await this.getAdditionalCommandPicks(allCommandPicks, filteredCommandPicks, filter, token); + if (token.isCancellationRequested) { + return []; + } + + return additionalCommandPicks.map(commandPick => this.toCommandPick(commandPick, runOptions)); + })() }; } @@ -177,6 +187,7 @@ export abstract class AbstractCommandsQuickAccessProvider extends PickerQuickAcc if (commandPick.type === 'separator') { return commandPick; } + const keybinding = this.keybindingService.lookupKeybinding(commandPick.commandId); const ariaLabel = keybinding ? localize('commandPickAriaLabelWithKeybinding', "{0}, {1}", commandPick.label, keybinding.getAriaLabel()) : @@ -210,27 +221,22 @@ export abstract class AbstractCommandsQuickAccessProvider extends PickerQuickAcc }; } - /** - * Subclasses to provide the actual command entries. - */ - protected abstract getCommandPicks(token: CancellationToken): Array; + protected abstract getCommandPicks(token: CancellationToken): Promise>; - /** - * 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>; } 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; }; }; } diff --git a/src/vs/platform/quickinput/browser/helpQuickAccess.ts b/src/vs/platform/quickinput/browser/helpQuickAccess.ts index bdffa0bd228a7..0346fc0b85692 100644 --- a/src/vs/platform/quickinput/browser/helpQuickAccess.ts +++ b/src/vs/platform/quickinput/browser/helpQuickAccess.ts @@ -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 { @@ -75,4 +75,3 @@ export class HelpQuickAccessProvider implements IQuickAccessProvider { }); } } - diff --git a/src/vs/platform/quickinput/browser/pickerQuickAccess.ts b/src/vs/platform/quickinput/browser/pickerQuickAccess.ts index 7ea3087de9ba8..4eae7f8ba7448 100644 --- a/src/vs/platform/quickinput/browser/pickerQuickAccess.ts +++ b/src/vs/platform/quickinput/browser/pickerQuickAccess.ts @@ -64,18 +64,37 @@ export interface IPickerQuickAccessProviderOptions T); + readonly noResultsPick?: T | ((filter: string) => T); } export type Pick = T | IQuickPickSeparator; export type PicksWithActive = { items: readonly Pick[]; active?: T }; export type Picks = readonly Pick[] | PicksWithActive; -export type FastAndSlowPicks = { picks: Picks; additionalPicks: Promise> }; +export type FastAndSlowPicks = { + + /** + * Picks that will show instantly or after a short delay + * based on the `mergeDelay` property to reduce flicker. + */ + readonly picks: Picks; + + /** + * Picks that will show after they have been resolved. + */ + readonly additionalPicks: Promise>; + + /** + * 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(obj: unknown): obj is PicksWithActive { const candidate = obj as PicksWithActive; @@ -91,8 +110,6 @@ function isFastAndSlowPicks(obj: unknown): obj is FastAndSlowPicks { export abstract class PickerQuickAccessProvider 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) { super(); } @@ -158,51 +175,50 @@ export abstract class PickerQuickAccessProvider): Promise => { 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[]; let activePick: Pick | 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[]; @@ -243,6 +259,16 @@ export abstract class PickerQuickAccessProvider | Promise> | FastAndSlowPicks | null; + protected abstract _getPicks(filter: string, disposables: DisposableStore, token: CancellationToken, runOptions?: IQuickAccessProviderRunOptions): Picks | Promise | FastAndSlowPicks> | FastAndSlowPicks | null; } diff --git a/src/vs/platform/quickinput/browser/quickAccess.ts b/src/vs/platform/quickinput/browser/quickAccess.ts index ea8b0a35ea7e4..0f2fa5c8f3c67 100644 --- a/src/vs/platform/quickinput/browser/quickAccess.ts +++ b/src/vs/platform/quickinput/browser/quickAccess.ts @@ -20,9 +20,9 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon private readonly lastAcceptedPickerValues = new Map(); private visibleQuickAccess: { - picker: IQuickPick; - descriptor: IQuickAccessProviderDescriptor | undefined; - value: string; + readonly picker: IQuickPick; + readonly descriptor: IQuickAccessProviderDescriptor | undefined; + readonly value: string; } | undefined = undefined; constructor( diff --git a/src/vs/platform/quickinput/common/quickAccess.ts b/src/vs/platform/quickinput/common/quickAccess.ts index d0f8c57dbb3b3..e5c5dee917330 100644 --- a/src/vs/platform/quickinput/common/quickAccess.ts +++ b/src/vs/platform/quickinput/common/quickAccess.ts @@ -14,14 +14,14 @@ 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 { @@ -29,25 +29,25 @@ 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 { @@ -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. @@ -181,6 +181,7 @@ export interface IQuickAccessRegistry { } export class QuickAccessRegistry implements IQuickAccessRegistry { + private providers: IQuickAccessProviderDescriptor[] = []; private defaultProvider: IQuickAccessProviderDescriptor | undefined = undefined; diff --git a/src/vs/workbench/browser/quickaccess.ts b/src/vs/workbench/browser/quickaccess.ts index 8f10b6807e449..7aaec862f9930 100644 --- a/src/vs/workbench/browser/quickaccess.ts +++ b/src/vs/workbench/browser/quickaccess.ts @@ -17,18 +17,18 @@ export const defaultQuickAccessContextKeyValue = 'inFilesPicker'; export const defaultQuickAccessContext = ContextKeyExpr.and(inQuickPickContext, ContextKeyExpr.has(defaultQuickAccessContextKeyValue)); export interface IWorkbenchQuickAccessConfiguration { - workbench: { - commandPalette: { - history: number; - preserveInput: boolean; - experimental: { - suggestCommands: boolean; - useSemanticSimilarity: boolean; + readonly workbench: { + readonly commandPalette: { + readonly history: number; + readonly preserveInput: boolean; + readonly experimental: { + readonly suggestCommands: boolean; + readonly useSemanticSimilarity: boolean; }; }; - quickOpen: { - enableExperimentalNewVersion: boolean; - preserveInput: boolean; + readonly quickOpen: { + readonly enableExperimentalNewVersion: boolean; + readonly preserveInput: boolean; }; }; } diff --git a/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts b/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts index c6359ac4cd3e4..edea37c5b774e 100644 --- a/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts +++ b/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts @@ -7,9 +7,9 @@ import { localize } from 'vs/nls'; import { ICommandQuickPick, CommandsHistory } from 'vs/platform/quickinput/browser/commandsQuickAccess'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IMenuService, MenuId, MenuItemAction, SubmenuItemAction, Action2 } from 'vs/platform/actions/common/actions'; -// import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { CancellationToken } from 'vs/base/common/cancellation'; -// import { timeout } from 'vs/base/common/async'; +import { raceTimeout, timeout } from 'vs/base/common/async'; import { AbstractEditorCommandsQuickAccessProvider } from 'vs/editor/contrib/quickAccess/browser/commandsQuickAccess'; import { IEditor } from 'vs/editor/common/editorCommon'; import { Language } from 'vs/base/common/platform'; @@ -34,25 +34,21 @@ import { stripIcons } from 'vs/base/common/iconLabels'; import { isFirefox } from 'vs/base/browser/browser'; import { IProductService } from 'vs/platform/product/common/productService'; import { ISemanticSimilarityService } from 'vs/workbench/services/semanticSimilarity/common/semanticSimilarityService'; -import { timeout } from 'vs/base/common/async'; import { IInteractiveSessionService } from 'vs/workbench/contrib/interactiveSession/common/interactiveSessionService'; import { ILogService } from 'vs/platform/log/common/log'; import { IInteractiveSessionWidgetService } from 'vs/workbench/contrib/interactiveSession/browser/interactiveSessionWidget'; export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAccessProvider { + private static SEMANTIC_SIMILARITY_MAX_PICKS = 3; private static SEMANTIC_SIMILARITY_THRESHOLD = 0.8; private static SEMANTIC_SIMILARITY_DEBOUNCE = 200; - // TODO: bring this back once we have a chosen strategy for FastAndSlowPicks where Fast is also Promise based // If extensions are not yet registered, we wait for a little moment to give them // a chance to register so that the complete set of commands shows up as result // We do not want to delay functionality beyond that time though to keep the commands // functional. - // private readonly extensionRegistrationRace = Promise.race([ - // timeout(800), - // this.extensionService.whenInstalledExtensionsRegistered() - // ]); + private readonly extensionRegistrationRace = raceTimeout(this.extensionService.whenInstalledExtensionsRegistered(), 800); private useSemanticSimilarity = false; @@ -69,7 +65,7 @@ export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAcce constructor( @IEditorService private readonly editorService: IEditorService, @IMenuService private readonly menuService: IMenuService, - // @IExtensionService private readonly extensionService: IExtensionService, + @IExtensionService private readonly extensionService: IExtensionService, @IInstantiationService instantiationService: IInstantiationService, @IKeybindingService keybindingService: IKeybindingService, @ICommandService commandService: ICommandService, @@ -125,11 +121,10 @@ export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAcce this.useSemanticSimilarity = config.experimental.useSemanticSimilarity; } - protected getCommandPicks(token: CancellationToken): Array { + protected async getCommandPicks(token: CancellationToken): Promise> { - // TODO: bring this back once we have a chosen strategy for FastAndSlowPicks where Fast is also Promise based // wait for extensions registration or 800ms once - // await this.extensionRegistrationRace; + await this.extensionRegistrationRace; if (token.isCancellationRequested) { return []; @@ -138,23 +133,32 @@ export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAcce return [ ...this.getCodeEditorCommandPicks(), ...this.getGlobalCommandPicks() - ].map(c => ({ - ...c, + ].map(picks => ({ + ...picks, buttons: [{ iconClass: ThemeIcon.asClassName(Codicon.gear), tooltip: localize('configure keybinding', "Configure Keybinding"), }], trigger: (): TriggerAction => { - this.preferencesService.openGlobalKeybindingSettings(false, { query: `@command:${c.commandId}` }); + this.preferencesService.openGlobalKeybindingSettings(false, { query: `@command:${picks.commandId}` }); return TriggerAction.CLOSE_PICKER; }, })); } - protected async getAdditionalCommandPicks(allPicks: ICommandQuickPick[], picksSoFar: ICommandQuickPick[], filter: string, token: CancellationToken): Promise> { + protected hasAdditionalCommandPicks(filter: string, token: CancellationToken): boolean { if (!this.useSemanticSimilarity || filter === '' || token.isCancellationRequested || !this.semanticSimilarityService.isEnabled()) { + return false; + } + + return true; + } + + protected async getAdditionalCommandPicks(allPicks: ICommandQuickPick[], picksSoFar: ICommandQuickPick[], filter: string, token: CancellationToken): Promise> { + if (!this.hasAdditionalCommandPicks(filter, token)) { return []; } + const format = allPicks.map(p => p.commandId); let scores: number[]; try { @@ -164,6 +168,11 @@ export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAcce } catch (e) { return []; } + + if (token.isCancellationRequested) { + return []; + } + const sortedIndices = scores.map((_, i) => i).sort((a, b) => scores[b] - scores[a]); const setOfPicksSoFar = new Set(picksSoFar.map(p => p.commandId)); const additionalPicks: Array = picksSoFar.length > 0 @@ -179,12 +188,14 @@ export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAcce if (score < CommandsQuickAccessProvider.SEMANTIC_SIMILARITY_THRESHOLD || numOfSmartPicks === CommandsQuickAccessProvider.SEMANTIC_SIMILARITY_MAX_PICKS) { break; } + const pick = allPicks[i]; if (!setOfPicksSoFar.has(pick.commandId)) { additionalPicks.push(pick); numOfSmartPicks++; } } + return additionalPicks; } diff --git a/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts b/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts index d35e8733ef0df..dde4d2279da47 100644 --- a/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts @@ -84,6 +84,8 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider | undefined = undefined; @@ -394,7 +396,10 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider