From 68e01d4a8dd9d6784132c21128a69c2096edb2fe Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Thu, 13 Oct 2022 20:35:55 -0700 Subject: [PATCH 1/2] Command Center With Modes --- .../quickinput/browser/helpQuickAccess.ts | 45 ++++--- .../quickinput/browser/pickerQuickAccess.ts | 8 +- .../quickinput/browser/quickAccess.ts | 21 +++- .../platform/quickinput/common/quickAccess.ts | 23 +++- .../browser/actions/quickAccessActions.ts | 27 ++++- .../parts/titlebar/commandCenterControl.ts | 2 +- .../search/browser/anythingQuickAccess.ts | 111 +++++++++++++++--- 7 files changed, 185 insertions(+), 52 deletions(-) diff --git a/src/vs/platform/quickinput/browser/helpQuickAccess.ts b/src/vs/platform/quickinput/browser/helpQuickAccess.ts index 90d85542cca87..e77794d189b2e 100644 --- a/src/vs/platform/quickinput/browser/helpQuickAccess.ts +++ b/src/vs/platform/quickinput/browser/helpQuickAccess.ts @@ -7,7 +7,7 @@ import { localize } from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { Extensions, IQuickAccessProvider, IQuickAccessRegistry } from 'vs/platform/quickinput/common/quickAccess'; +import { Extensions, IQuickAccessProvider, IQuickAccessProviderDescriptor, IQuickAccessRegistry } from 'vs/platform/quickinput/common/quickAccess'; import { IQuickInputService, IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; interface IHelpQuickAccessPickItem extends IQuickPickItem { @@ -46,34 +46,33 @@ export class HelpQuickAccessProvider implements IQuickAccessProvider { })); // Fill in all providers - picker.items = this.getQuickAccessProviders(); + picker.items = this.getQuickAccessProviders().filter(p => p.prefix !== HelpQuickAccessProvider.PREFIX); return disposables; } - private getQuickAccessProviders(): IHelpQuickAccessPickItem[] { - const providers: IHelpQuickAccessPickItem[] = []; - - for (const provider of this.registry.getQuickAccessProviders().sort((providerA, providerB) => providerA.prefix.localeCompare(providerB.prefix))) { - if (provider.prefix === HelpQuickAccessProvider.PREFIX) { - continue; // exclude help which is already active - } - - for (const helpEntry of provider.helpEntries) { - const prefix = helpEntry.prefix || provider.prefix; - const label = prefix || '\u2026' /* ... */; - - providers.push({ - prefix, - label, - keybinding: helpEntry.commandId ? this.keybindingService.lookupKeybinding(helpEntry.commandId) : undefined, - ariaLabel: localize('helpPickAriaLabel', "{0}, {1}", label, helpEntry.description), - description: helpEntry.description - }); - } - } + public getQuickAccessProviders(): IHelpQuickAccessPickItem[] { + const providers: IHelpQuickAccessPickItem[] = this.registry + .getQuickAccessProviders() + .sort((providerA, providerB) => providerA.prefix.localeCompare(providerB.prefix)) + .flatMap(provider => this.createPicks(provider)); return providers; } + + private createPicks(provider: IQuickAccessProviderDescriptor): IHelpQuickAccessPickItem[] { + return provider.helpEntries.map(helpEntry => { + const prefix = helpEntry.prefix || provider.prefix; + const label = prefix || '\u2026' /* ... */; + + return { + prefix, + label, + keybinding: helpEntry.commandId ? this.keybindingService.lookupKeybinding(helpEntry.commandId) : undefined, + ariaLabel: localize('helpPickAriaLabel', "{0}, {1}", label, helpEntry.description), + description: helpEntry.description + }; + }); + } } diff --git a/src/vs/platform/quickinput/browser/pickerQuickAccess.ts b/src/vs/platform/quickinput/browser/pickerQuickAccess.ts index bba15d63cfc06..eb07b7d3dce91 100644 --- a/src/vs/platform/quickinput/browser/pickerQuickAccess.ts +++ b/src/vs/platform/quickinput/browser/pickerQuickAccess.ts @@ -7,7 +7,7 @@ import { timeout } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { Disposable, DisposableStore, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { IKeyMods, IQuickPickDidAcceptEvent, IQuickPickSeparator } from 'vs/base/parts/quickinput/common/quickInput'; -import { IQuickAccessProvider } from 'vs/platform/quickinput/common/quickAccess'; +import { IQuickAccessProvider, IQuickAccessProviderRunOptions } from 'vs/platform/quickinput/common/quickAccess'; import { IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; export enum TriggerAction { @@ -97,7 +97,7 @@ export abstract class PickerQuickAccessProvider, token: CancellationToken): IDisposable { + provide(picker: IQuickPick, token: CancellationToken, runOptions?: IQuickAccessProviderRunOptions): IDisposable { const disposables = new DisposableStore(); // Apply options if any @@ -122,7 +122,7 @@ export abstract class PickerQuickAccessProvider, skipEmpty?: boolean): boolean => { let items: readonly Pick[]; @@ -338,5 +338,5 @@ export abstract class PickerQuickAccessProvider | Promise> | FastAndSlowPicks | null; + protected abstract _getPicks(filter: string, disposables: DisposableStore, token: CancellationToken, runOptions?: IQuickAccessProviderRunOptions): Picks | Promise> | FastAndSlowPicks | null; } diff --git a/src/vs/platform/quickinput/browser/quickAccess.ts b/src/vs/platform/quickinput/browser/quickAccess.ts index b2ed573cc7216..8b131cde8f917 100644 --- a/src/vs/platform/quickinput/browser/quickAccess.ts +++ b/src/vs/platform/quickinput/browser/quickAccess.ts @@ -8,7 +8,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { once } from 'vs/base/common/functional'; import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { DefaultQuickAccessFilterValue, Extensions, IQuickAccessController, IQuickAccessOptions, IQuickAccessProvider, IQuickAccessProviderDescriptor, IQuickAccessRegistry } from 'vs/platform/quickinput/common/quickAccess'; +import { DefaultQuickAccessFilterValue, Extensions, IQuickAccessController, IQuickAccessOptions, IQuickAccessProvider, IQuickAccessProviderDescriptor, IQuickAccessProviderRunOptions, IQuickAccessRegistry } from 'vs/platform/quickinput/common/quickAccess'; import { IQuickInputService, IQuickPick, IQuickPickItem, ItemActivation } from 'vs/platform/quickinput/common/quickInput'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -122,14 +122,14 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon } // Register listeners - disposables.add(this.registerPickerListeners(picker, provider, descriptor, value)); + disposables.add(this.registerPickerListeners(picker, provider, descriptor, value, options?.providerOptions)); // Ask provider to fill the picker as needed if we have one // and pass over a cancellation token that will indicate when // the picker is hiding without a pick being made. const cts = disposables.add(new CancellationTokenSource()); if (provider) { - disposables.add(provider.provide(picker, cts.token)); + disposables.add(provider.provide(picker, cts.token, options?.providerOptions)); } // Finally, trigger disposal and cancellation when the picker @@ -173,7 +173,13 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon picker.valueSelection = valueSelection; } - private registerPickerListeners(picker: IQuickPick, provider: IQuickAccessProvider | undefined, descriptor: IQuickAccessProviderDescriptor | undefined, value: string): IDisposable { + private registerPickerListeners( + picker: IQuickPick, + provider: IQuickAccessProvider | undefined, + descriptor: IQuickAccessProviderDescriptor | undefined, + value: string, + providerOptions?: IQuickAccessProviderRunOptions + ): IDisposable { const disposables = new DisposableStore(); // Remember as last visible picker and clean up once picker get's disposed @@ -189,7 +195,12 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon disposables.add(picker.onDidChangeValue(value => { const [providerForValue] = this.getOrInstantiateProvider(value); if (providerForValue !== provider) { - this.show(value, { preserveValue: true } /* do not rewrite value from user typing! */); + this.show(value, { + // do not rewrite value from user typing! + preserveValue: true, + // persist the value of the providerOptions from the original showing + providerOptions: providerOptions + }); } else { visibleQuickAccess.value = value; // remember the value in our visible one } diff --git a/src/vs/platform/quickinput/common/quickAccess.ts b/src/vs/platform/quickinput/common/quickAccess.ts index 18976f270ba77..a15069be8aa4e 100644 --- a/src/vs/platform/quickinput/common/quickAccess.ts +++ b/src/vs/platform/quickinput/common/quickAccess.ts @@ -10,6 +10,19 @@ import { ItemActivation } from 'vs/base/parts/quickinput/common/quickInput'; import { IQuickNavigateConfiguration, IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { Registry } from 'vs/platform/registry/common/platform'; +/** + * Provider specific options for this particular showing of the + * quick access. + */ +export interface IQuickAccessProviderRunOptions { } + +/** + * The specific options for the AnythingQuickAccessProvider. Put here to share between layers. + */ +export interface AnythingQuickAccessProviderRunOptions extends IQuickAccessProviderRunOptions { + includeHelp?: boolean; +} + export interface IQuickAccessOptions { /** @@ -28,6 +41,12 @@ export interface IQuickAccessOptions { * from any existing value if quick access is visible. */ preserveValue?: boolean; + + /** + * Provider specific options for this particular showing of the + * quick access. + */ + providerOptions?: IQuickAccessProviderRunOptions; } export interface IQuickAccessController { @@ -80,10 +99,12 @@ export interface IQuickAccessProvider { * a long running operation or from event handlers because it could be that the * picker has been closed or changed meanwhile. The token can be used to find out * that the picker was closed without picking an entry (e.g. was canceled by the user). + * @param options additional configuration specific for this provider that will + * influence what picks will be shown. * @return a disposable that will automatically be disposed when the picker * closes or is replaced by another picker. */ - provide(picker: IQuickPick, token: CancellationToken): IDisposable; + provide(picker: IQuickPick, token: CancellationToken, options?: IQuickAccessProviderRunOptions): IDisposable; } export interface IQuickAccessProviderHelp { diff --git a/src/vs/workbench/browser/actions/quickAccessActions.ts b/src/vs/workbench/browser/actions/quickAccessActions.ts index 6cda33c142ce0..ba895c00fd64c 100644 --- a/src/vs/workbench/browser/actions/quickAccessActions.ts +++ b/src/vs/workbench/browser/actions/quickAccessActions.ts @@ -13,6 +13,7 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { inQuickPickContext, defaultQuickAccessContext, getQuickNavigateHandler } from 'vs/workbench/browser/quickaccess'; import { ILocalizedString } from 'vs/platform/action/common/action'; +import { AnythingQuickAccessProviderRunOptions } from 'vs/platform/quickinput/common/quickAccess'; //#region Quick access management commands and keys @@ -139,6 +140,24 @@ registerAction2(class QuickAccessAction extends Action2 { secondary: globalQuickAccessKeybinding.secondary, mac: globalQuickAccessKeybinding.mac }, + f1: true + }); + } + + run(accessor: ServicesAccessor, prefix: undefined): void { + const quickInputService = accessor.get(IQuickInputService); + quickInputService.quickAccess.show(typeof prefix === 'string' ? prefix : undefined, { preserveValue: typeof prefix === 'string' /* preserve as is if provided */ }); + } +}); + +registerAction2(class QuickAccessAction extends Action2 { + constructor() { + super({ + id: 'workbench.action.quickOpenWithModes', + title: { + value: localize('quickOpenWithModes', "Launch Command Center"), + original: 'Launch Command Center' + }, f1: true, menu: { id: MenuId.CommandCenter, @@ -147,9 +166,13 @@ registerAction2(class QuickAccessAction extends Action2 { }); } - run(accessor: ServicesAccessor, prefix: undefined): void { + run(accessor: ServicesAccessor): void { const quickInputService = accessor.get(IQuickInputService); - quickInputService.quickAccess.show(typeof prefix === 'string' ? prefix : undefined, { preserveValue: typeof prefix === 'string' /* preserve as is if provided */ }); + quickInputService.quickAccess.show(undefined, { + providerOptions: { + includeHelp: true, + } as AnythingQuickAccessProviderRunOptions + }); } }); diff --git a/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts b/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts index b281f030a55e1..e59c60f87f621 100644 --- a/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts @@ -50,7 +50,7 @@ export class CommandCenterControl { telemetrySource: 'commandCenter', actionViewItemProvider: (action) => { - if (action instanceof MenuItemAction && action.id === 'workbench.action.quickOpen') { + if (action instanceof MenuItemAction && action.id === 'workbench.action.quickOpenWithModes') { class CommandCenterViewItem extends BaseActionViewItem { diff --git a/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts b/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts index 3a37e9524d22d..1e75062f9052e 100644 --- a/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/anythingQuickAccess'; -import { IQuickInputButton, IKeyMods, quickPickItemScorerAccessor, QuickPickItemScorerAccessor, IQuickPick, IQuickPickItemWithResource, QuickInputHideReason } from 'vs/platform/quickinput/common/quickInput'; +import { IQuickInputButton, IKeyMods, quickPickItemScorerAccessor, QuickPickItemScorerAccessor, IQuickPick, IQuickPickItemWithResource, QuickInputHideReason, IQuickInputService, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { IPickerQuickAccessItem, PickerQuickAccessProvider, TriggerAction, FastAndSlowPicks, Picks, PicksWithActive } from 'vs/platform/quickinput/browser/pickerQuickAccess'; import { prepareQuery, IPreparedQuery, compareItemsByFuzzyScore, scoreItemFuzzy, FuzzyScorerCache } from 'vs/base/common/fuzzyScorer'; import { IFileQueryBuilderOptions, QueryBuilder } from 'vs/workbench/services/search/common/queryBuilder'; @@ -40,7 +40,7 @@ import { Schemas } from 'vs/base/common/network'; import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { ResourceMap } from 'vs/base/common/map'; import { SymbolsQuickAccessProvider } from 'vs/workbench/contrib/search/browser/symbolsQuickAccess'; -import { DefaultQuickAccessFilterValue } from 'vs/platform/quickinput/common/quickAccess'; +import { AnythingQuickAccessProviderRunOptions, DefaultQuickAccessFilterValue } from 'vs/platform/quickinput/common/quickAccess'; import { IWorkbenchQuickAccessConfiguration } from 'vs/workbench/browser/quickaccess'; import { GotoSymbolQuickAccessProvider } from 'vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; @@ -52,6 +52,10 @@ import { withNullAsUndefined } from 'vs/base/common/types'; import { Codicon } from 'vs/base/common/codicons'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { stripIcons } from 'vs/base/common/iconLabels'; +import { HelpQuickAccessProvider } from 'vs/platform/quickinput/browser/helpQuickAccess'; +import { CommandsQuickAccessProvider } from 'vs/workbench/contrib/quickaccess/browser/commandsQuickAccess'; +import { DEBUG_QUICK_ACCESS_PREFIX } from 'vs/workbench/contrib/debug/browser/debugCommands'; +import { TasksQuickAccessProvider } from 'vs/workbench/contrib/tasks/browser/tasksQuickAccess'; interface IAnythingQuickPickItem extends IPickerQuickAccessItem, IQuickPickItemWithResource { } @@ -178,7 +182,8 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider, token: CancellationToken): IDisposable { + override provide(picker: IQuickPick, token: CancellationToken, runOptions?: AnythingQuickAccessProviderRunOptions): IDisposable { const disposables = new DisposableStore(); // Update the pick state for this run @@ -233,7 +238,7 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider this.clearDecorations(activeEditorControl)); } - protected _getPicks(originalFilter: string, disposables: DisposableStore, token: CancellationToken): Picks | Promise> | FastAndSlowPicks | null { + protected _getPicks(originalFilter: string, disposables: DisposableStore, token: CancellationToken, runOptions?: AnythingQuickAccessProviderRunOptions): Picks | Promise> | FastAndSlowPicks | null { // Find a suitable range from the pattern looking for ":", "#" or "," // unless we have the `@` editor symbol character inside the filter @@ -316,10 +321,15 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider | Promise> | FastAndSlowPicks { + private doGetPicks( + filter: string, + options: AnythingQuickAccessProviderRunOptions & { enableEditorSymbolSearch: boolean }, + disposables: DisposableStore, + token: CancellationToken + ): Picks | Promise> | FastAndSlowPicks { const query = prepareQuery(filter); // Return early if we have editor symbol picks. We support this by: @@ -343,16 +353,24 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider; + if (this.pickState.isQuickNavigating) { + picks = historyEditorPicks; + } else { + picks = []; + if (options.includeHelp) { + picks.concat(this.getHelpPicks(query, token)); + } + if (historyEditorPicks.length !== 0) { + picks.push({ type: 'separator', label: localize('recentlyOpenedSeparator', "recently opened") } as IQuickPickSeparator); + picks.concat(historyEditorPicks); + } + } + return { - // Fast picks: editor history - picks: - (this.pickState.isQuickNavigating || historyEditorPicks.length === 0) ? - historyEditorPicks : - [ - { type: 'separator', label: localize('recentlyOpenedSeparator', "recently opened") }, - ...historyEditorPicks - ], + // Fast picks: help (if included) & editor history + picks, // Slow picks: files and symbols additionalPicks: (async (): Promise> => { @@ -732,6 +750,67 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider = this.helpQuickAccess.getQuickAccessProviders(); + const mapOfProviders = new Map(); + for (const provider of providers) { + mapOfProviders.set(provider.prefix, provider); + } + + const importantProviders: Array = []; + const AddProvider = (prefix: string, modifications: Partial = {}) => { + if (mapOfProviders.has(prefix)) { + const provider = mapOfProviders.get(prefix)!; + + // We swap the label and description in this to emphasize the ability + // not the prefix. + provider.label = provider.description!; + provider.description = provider.prefix; + + // If the user chooses 'Go to File' the help should go away as if they were + // entering a new mode + const providerSpecificOptions = provider.prefix === AnythingQuickAccessProvider.PREFIX + ? undefined + : { includeModes: true }; + + importantProviders.push({ + ...mapOfProviders.get(prefix)!, + ...modifications, + accept: () => { + this.quickInputService.quickAccess.show(provider.prefix, { + preserveValue: true, + providerOptions: providerSpecificOptions + }); + } + }); + } + }; + + // Acts as the ordering too + AddProvider(AnythingQuickAccessProvider.PREFIX); + AddProvider(CommandsQuickAccessProvider.PREFIX); + AddProvider(GotoSymbolQuickAccessProvider.PREFIX); + AddProvider(DEBUG_QUICK_ACCESS_PREFIX); + AddProvider(TasksQuickAccessProvider.PREFIX); + AddProvider(HelpQuickAccessProvider.PREFIX, { + // More concise + label: localize('more', 'More') + }); + + return importantProviders; + } + + //#endregion //#region Workspace Symbols (if enabled) From 85f2dff1890afd566f9d7bce448911eebfffc0ba Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Fri, 14 Oct 2022 12:29:07 +0200 Subject: [PATCH 2/2] fix bug --- .../contrib/search/browser/anythingQuickAccess.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts b/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts index 1e75062f9052e..8708e7c1378c3 100644 --- a/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts @@ -359,11 +359,11 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider