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

Command Center With Help #163635

Merged
merged 2 commits into from Oct 14, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
45 changes: 22 additions & 23 deletions src/vs/platform/quickinput/browser/helpQuickAccess.ts
Expand Up @@ -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 {
Expand Down Expand Up @@ -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[] {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TylerLeonhardt personal taste is to not use public but have it removed

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
};
});
}
}

8 changes: 4 additions & 4 deletions src/vs/platform/quickinput/browser/pickerQuickAccess.ts
Expand Up @@ -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 {
Expand Down Expand Up @@ -97,7 +97,7 @@ export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem
super();
}

provide(picker: IQuickPick<T>, token: CancellationToken): IDisposable {
provide(picker: IQuickPick<T>, token: CancellationToken, runOptions?: IQuickAccessProviderRunOptions): IDisposable {
const disposables = new DisposableStore();

// Apply options if any
Expand All @@ -122,7 +122,7 @@ export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem
// Collect picks and support both long running and short or combined
const picksToken = picksCts.token;
const picksFilter = picker.value.substr(this.prefix.length).trim();
const providedPicks = this._getPicks(picksFilter, picksDisposables, picksToken);
const providedPicks = this._getPicks(picksFilter, picksDisposables, picksToken, runOptions);

const applyPicks = (picks: Picks<T>, skipEmpty?: boolean): boolean => {
let items: readonly Pick<T>[];
Expand Down Expand Up @@ -338,5 +338,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): 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> | null;
}
21 changes: 16 additions & 5 deletions src/vs/platform/quickinput/browser/quickAccess.ts
Expand Up @@ -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';

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -173,7 +173,13 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon
picker.valueSelection = valueSelection;
}

private registerPickerListeners(picker: IQuickPick<IQuickPickItem>, provider: IQuickAccessProvider | undefined, descriptor: IQuickAccessProviderDescriptor | undefined, value: string): IDisposable {
private registerPickerListeners(
picker: IQuickPick<IQuickPickItem>,
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
Expand All @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TylerLeonhardt easier as just providerOptions

});
} else {
visibleQuickAccess.value = value; // remember the value in our visible one
}
Expand Down
23 changes: 22 additions & 1 deletion src/vs/platform/quickinput/common/quickAccess.ts
Expand Up @@ -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 {

/**
Expand All @@ -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 {
Expand Down Expand Up @@ -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<IQuickPickItem>, token: CancellationToken): IDisposable;
provide(picker: IQuickPick<IQuickPickItem>, token: CancellationToken, options?: IQuickAccessProviderRunOptions): IDisposable;
}

export interface IQuickAccessProviderHelp {
Expand Down
27 changes: 25 additions & 2 deletions src/vs/workbench/browser/actions/quickAccessActions.ts
Expand Up @@ -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

Expand Down Expand Up @@ -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,
Expand All @@ -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
});
}
});

Expand Down
Expand Up @@ -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 {

Expand Down