mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-11 02:32:35 -05:00
Merge from vscode cfc1ab4c5f816765b91fb7ead3c3427a7c8581a3
This commit is contained in:
77
src/vs/platform/quickinput/browser/helpQuickAccess.ts
Normal file
77
src/vs/platform/quickinput/browser/helpQuickAccess.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IQuickPick, IQuickPickItem, IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IQuickAccessProvider, IQuickAccessRegistry, Extensions } from 'vs/platform/quickinput/common/quickAccess';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { localize } from 'vs/nls';
|
||||
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
interface IHelpQuickAccessPickItem extends IQuickPickItem {
|
||||
prefix: string;
|
||||
}
|
||||
|
||||
export class HelpQuickAccessProvider implements IQuickAccessProvider {
|
||||
|
||||
static PREFIX = '?';
|
||||
|
||||
private readonly registry = Registry.as<IQuickAccessRegistry>(Extensions.Quickaccess);
|
||||
|
||||
constructor(@IQuickInputService private readonly quickInputService: IQuickInputService) { }
|
||||
|
||||
provide(picker: IQuickPick<IHelpQuickAccessPickItem>, token: CancellationToken): IDisposable {
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
// Open a picker with the selected value if picked
|
||||
disposables.add(picker.onDidAccept(() => {
|
||||
const [item] = picker.selectedItems;
|
||||
if (item) {
|
||||
this.quickInputService.quickAccess.show(item.prefix);
|
||||
}
|
||||
}));
|
||||
|
||||
// Fill in all providers separated by editor/global scope
|
||||
const { editorProviders, globalProviders } = this.getQuickAccessProviders();
|
||||
picker.items = editorProviders.length === 0 || globalProviders.length === 0 ?
|
||||
|
||||
// Without groups
|
||||
[
|
||||
...(editorProviders.length === 0 ? globalProviders : editorProviders)
|
||||
] :
|
||||
|
||||
// With groups
|
||||
[
|
||||
{ label: localize('globalCommands', "global commands"), type: 'separator' },
|
||||
...globalProviders,
|
||||
{ label: localize('editorCommands', "editor commands"), type: 'separator' },
|
||||
...editorProviders
|
||||
];
|
||||
|
||||
return disposables;
|
||||
}
|
||||
|
||||
private getQuickAccessProviders(): { editorProviders: IHelpQuickAccessPickItem[], globalProviders: IHelpQuickAccessPickItem[] } {
|
||||
const globalProviders: IHelpQuickAccessPickItem[] = [];
|
||||
const editorProviders: IHelpQuickAccessPickItem[] = [];
|
||||
|
||||
for (const provider of this.registry.getQuickAccessProviders().sort((p1, p2) => p1.prefix.localeCompare(p2.prefix))) {
|
||||
for (const helpEntry of provider.helpEntries) {
|
||||
const prefix = helpEntry.prefix || provider.prefix;
|
||||
const label = prefix || '\u2026' /* ... */;
|
||||
|
||||
(helpEntry.needsEditor ? editorProviders : globalProviders).push({
|
||||
prefix,
|
||||
label,
|
||||
description: helpEntry.description,
|
||||
ariaLabel: localize('entryAriaLabel', "{0}, picker help", label)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return { editorProviders, globalProviders };
|
||||
}
|
||||
}
|
||||
|
||||
95
src/vs/platform/quickinput/browser/quickAccess.ts
Normal file
95
src/vs/platform/quickinput/browser/quickAccess.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IQuickInputService, IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IQuickAccessController, IQuickAccessProvider, IQuickAccessRegistry, Extensions, IQuickAccessProviderDescriptor } from 'vs/platform/quickinput/common/quickAccess';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { once } from 'vs/base/common/functional';
|
||||
|
||||
export class QuickAccessController extends Disposable implements IQuickAccessController {
|
||||
|
||||
private readonly registry = Registry.as<IQuickAccessRegistry>(Extensions.Quickaccess);
|
||||
private readonly mapProviderToDescriptor = new Map<IQuickAccessProviderDescriptor, IQuickAccessProvider>();
|
||||
|
||||
private lastActivePicker: IQuickPick<IQuickPickItem> | undefined = undefined;
|
||||
|
||||
constructor(
|
||||
@IQuickInputService private readonly quickInputService: IQuickInputService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
show(value = ''): void {
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
// Hide any previous picker if any
|
||||
this.lastActivePicker?.hide();
|
||||
|
||||
// Find provider for the value to show
|
||||
const [provider, descriptor] = this.getOrInstantiateProvider(value);
|
||||
|
||||
// Create a picker for the provider to use with the initial value
|
||||
// and adjust the filtering to exclude the prefix from filtering
|
||||
const picker = disposables.add(this.quickInputService.createQuickPick());
|
||||
picker.placeholder = descriptor.placeholder;
|
||||
picker.value = value;
|
||||
picker.valueSelection = [value.length, value.length];
|
||||
picker.contextKey = descriptor.contextKey;
|
||||
picker.filterValue = (value: string) => value.substring(descriptor.prefix.length);
|
||||
|
||||
// Remember as last active picker and clean up once picker get's disposed
|
||||
this.lastActivePicker = picker;
|
||||
disposables.add(toDisposable(() => {
|
||||
if (picker === this.lastActivePicker) {
|
||||
this.lastActivePicker = undefined;
|
||||
}
|
||||
}));
|
||||
|
||||
// Create a cancellation token source that is valid as long as the
|
||||
// picker has not been closed without picking an item
|
||||
const cts = disposables.add(new CancellationTokenSource());
|
||||
once(picker.onDidHide)(() => {
|
||||
if (picker.selectedItems.length === 0) {
|
||||
cts.cancel();
|
||||
}
|
||||
|
||||
// Start to dispose once picker hides
|
||||
disposables.dispose();
|
||||
});
|
||||
|
||||
// Whenever the value changes, check if the provider has
|
||||
// changed and if so - re-create the picker from the beginning
|
||||
disposables.add(picker.onDidChangeValue(value => {
|
||||
const [providerForValue] = this.getOrInstantiateProvider(value);
|
||||
if (providerForValue !== provider) {
|
||||
this.show(value);
|
||||
}
|
||||
}));
|
||||
|
||||
// Ask provider to fill the picker as needed
|
||||
disposables.add(provider.provide(picker, cts.token));
|
||||
|
||||
// Finally, show the picker. This is important because a provider
|
||||
// may not call this and then our disposables would leak that rely
|
||||
// on the onDidHide event.
|
||||
picker.show();
|
||||
}
|
||||
|
||||
private getOrInstantiateProvider(value: string): [IQuickAccessProvider, IQuickAccessProviderDescriptor] {
|
||||
const providerDescriptor = this.registry.getQuickAccessProvider(value) || this.registry.defaultProvider;
|
||||
|
||||
let provider = this.mapProviderToDescriptor.get(providerDescriptor);
|
||||
if (!provider) {
|
||||
provider = this.instantiationService.createInstance(providerDescriptor.ctor);
|
||||
this.mapProviderToDescriptor.set(providerDescriptor, provider);
|
||||
}
|
||||
|
||||
return [provider, providerDescriptor];
|
||||
}
|
||||
}
|
||||
228
src/vs/platform/quickinput/browser/quickInput.ts
Normal file
228
src/vs/platform/quickinput/browser/quickInput.ts
Normal file
@@ -0,0 +1,228 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IQuickInputService, IQuickPickItem, IPickOptions, IInputOptions, IQuickNavigateConfiguration, IQuickPick, IQuickInputButton, IInputBox, QuickPickInput } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IThemeService, Themable } from 'vs/platform/theme/common/themeService';
|
||||
import { inputBackground, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoForeground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningForeground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorForeground, inputValidationErrorBorder, badgeBackground, badgeForeground, contrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, progressBarBackground, widgetShadow, listFocusForeground, listFocusBackground, activeContrastBorder, pickerGroupBorder, pickerGroupForeground, quickInputForeground, quickInputBackground, quickInputTitleBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { computeStyles } from 'vs/platform/theme/common/styler';
|
||||
import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { QuickInputController, IQuickInputStyles, IQuickInputOptions } from 'vs/base/parts/quickinput/browser/quickInput';
|
||||
import { WorkbenchList } from 'vs/platform/list/browser/listService';
|
||||
import { List, IListOptions } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list';
|
||||
import { IQuickAccessController } from 'vs/platform/quickinput/common/quickAccess';
|
||||
import { QuickAccessController } from 'vs/platform/quickinput/browser/quickAccess';
|
||||
|
||||
export interface IQuickInputControllerHost extends ILayoutService { }
|
||||
|
||||
export class QuickInputService extends Themable implements IQuickInputService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
get backButton(): IQuickInputButton { return this.controller.backButton; }
|
||||
|
||||
get onShow() { return this.controller.onShow; }
|
||||
get onHide() { return this.controller.onHide; }
|
||||
|
||||
private _controller: QuickInputController | undefined;
|
||||
private get controller(): QuickInputController {
|
||||
if (!this._controller) {
|
||||
this._controller = this._register(this.createController());
|
||||
}
|
||||
|
||||
return this._controller;
|
||||
}
|
||||
|
||||
private _quickAccess: IQuickAccessController | undefined;
|
||||
get quickAccess(): IQuickAccessController {
|
||||
if (!this._quickAccess) {
|
||||
this._quickAccess = this._register(this.instantiationService.createInstance(QuickAccessController));
|
||||
}
|
||||
|
||||
return this._quickAccess;
|
||||
}
|
||||
|
||||
private readonly contexts = new Map<string, IContextKey<boolean>>();
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IAccessibilityService private readonly accessibilityService: IAccessibilityService,
|
||||
@ILayoutService protected readonly layoutService: ILayoutService
|
||||
) {
|
||||
super(themeService);
|
||||
}
|
||||
|
||||
protected createController(host: IQuickInputControllerHost = this.layoutService, options?: Partial<IQuickInputOptions>): QuickInputController {
|
||||
const defaultOptions: IQuickInputOptions = {
|
||||
idPrefix: 'quickInput_', // Constant since there is still only one.
|
||||
container: host.container,
|
||||
ignoreFocusOut: () => false,
|
||||
isScreenReaderOptimized: () => this.accessibilityService.isScreenReaderOptimized(),
|
||||
backKeybindingLabel: () => undefined,
|
||||
setContextKey: (id?: string) => this.setContextKey(id),
|
||||
returnFocus: () => host.focus(),
|
||||
createList: <T>(
|
||||
user: string,
|
||||
container: HTMLElement,
|
||||
delegate: IListVirtualDelegate<T>,
|
||||
renderers: IListRenderer<T, any>[],
|
||||
options: IListOptions<T>,
|
||||
) => this.instantiationService.createInstance(WorkbenchList, user, container, delegate, renderers, options) as List<T>,
|
||||
styles: this.computeStyles()
|
||||
};
|
||||
|
||||
const controller = this._register(new QuickInputController({
|
||||
...defaultOptions,
|
||||
...options
|
||||
}));
|
||||
|
||||
controller.layout(host.dimension, host.offset?.top ?? 0);
|
||||
|
||||
// Layout changes
|
||||
this._register(host.onLayout(dimension => controller.layout(dimension, host.offset?.top ?? 0)));
|
||||
|
||||
// Context keys
|
||||
this._register(controller.onShow(() => this.resetContextKeys()));
|
||||
this._register(controller.onHide(() => this.resetContextKeys()));
|
||||
|
||||
return controller;
|
||||
}
|
||||
|
||||
private setContextKey(id?: string) {
|
||||
let key: IContextKey<boolean> | undefined;
|
||||
if (id) {
|
||||
key = this.contexts.get(id);
|
||||
if (!key) {
|
||||
key = new RawContextKey<boolean>(id, false)
|
||||
.bindTo(this.contextKeyService);
|
||||
this.contexts.set(id, key);
|
||||
}
|
||||
}
|
||||
|
||||
if (key && key.get()) {
|
||||
return; // already active context
|
||||
}
|
||||
|
||||
this.resetContextKeys();
|
||||
|
||||
if (key) {
|
||||
key.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
private resetContextKeys() {
|
||||
this.contexts.forEach(context => {
|
||||
if (context.get()) {
|
||||
context.reset();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pick<T extends IQuickPickItem, O extends IPickOptions<T>>(picks: Promise<QuickPickInput<T>[]> | QuickPickInput<T>[], options: O = <O>{}, token: CancellationToken = CancellationToken.None): Promise<O extends { canPickMany: true } ? T[] : T> {
|
||||
return this.controller.pick(picks, options, token);
|
||||
}
|
||||
|
||||
input(options: IInputOptions = {}, token: CancellationToken = CancellationToken.None): Promise<string> {
|
||||
return this.controller.input(options, token);
|
||||
}
|
||||
|
||||
createQuickPick<T extends IQuickPickItem>(): IQuickPick<T> {
|
||||
return this.controller.createQuickPick();
|
||||
}
|
||||
|
||||
createInputBox(): IInputBox {
|
||||
return this.controller.createInputBox();
|
||||
}
|
||||
|
||||
focus() {
|
||||
this.controller.focus();
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.controller.toggle();
|
||||
}
|
||||
|
||||
navigate(next: boolean, quickNavigate?: IQuickNavigateConfiguration) {
|
||||
this.controller.navigate(next, quickNavigate);
|
||||
}
|
||||
|
||||
accept() {
|
||||
return this.controller.accept();
|
||||
}
|
||||
|
||||
back() {
|
||||
return this.controller.back();
|
||||
}
|
||||
|
||||
cancel() {
|
||||
return this.controller.cancel();
|
||||
}
|
||||
|
||||
hide(focusLost?: boolean): void {
|
||||
return this.controller.hide(focusLost);
|
||||
}
|
||||
|
||||
protected updateStyles() {
|
||||
this.controller.applyStyles(this.computeStyles());
|
||||
}
|
||||
|
||||
private computeStyles(): IQuickInputStyles {
|
||||
return {
|
||||
widget: {
|
||||
...computeStyles(this.theme, {
|
||||
quickInputBackground,
|
||||
quickInputForeground,
|
||||
quickInputTitleBackground,
|
||||
contrastBorder,
|
||||
widgetShadow
|
||||
}),
|
||||
},
|
||||
inputBox: computeStyles(this.theme, {
|
||||
inputForeground,
|
||||
inputBackground,
|
||||
inputBorder,
|
||||
inputValidationInfoBackground,
|
||||
inputValidationInfoForeground,
|
||||
inputValidationInfoBorder,
|
||||
inputValidationWarningBackground,
|
||||
inputValidationWarningForeground,
|
||||
inputValidationWarningBorder,
|
||||
inputValidationErrorBackground,
|
||||
inputValidationErrorForeground,
|
||||
inputValidationErrorBorder
|
||||
}),
|
||||
countBadge: computeStyles(this.theme, {
|
||||
badgeBackground,
|
||||
badgeForeground,
|
||||
badgeBorder: contrastBorder
|
||||
}),
|
||||
button: computeStyles(this.theme, {
|
||||
buttonForeground,
|
||||
buttonBackground,
|
||||
buttonHoverBackground,
|
||||
buttonBorder: contrastBorder
|
||||
}),
|
||||
progressBar: computeStyles(this.theme, {
|
||||
progressBarBackground
|
||||
}),
|
||||
list: computeStyles(this.theme, {
|
||||
listBackground: quickInputBackground,
|
||||
// Look like focused when inactive.
|
||||
listInactiveFocusForeground: listFocusForeground,
|
||||
listInactiveFocusBackground: listFocusBackground,
|
||||
listFocusOutline: activeContrastBorder,
|
||||
listInactiveFocusOutline: activeContrastBorder,
|
||||
pickerGroupBorder,
|
||||
pickerGroupForeground
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
242
src/vs/platform/quickinput/common/quickAccess.ts
Normal file
242
src/vs/platform/quickinput/common/quickAccess.ts
Normal file
@@ -0,0 +1,242 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { first } from 'vs/base/common/arrays';
|
||||
import { startsWith } from 'vs/base/common/strings';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
import { IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IQuickPickSeparator } from 'vs/base/parts/quickinput/common/quickInput';
|
||||
|
||||
export interface IQuickAccessController {
|
||||
|
||||
/**
|
||||
* Open the quick access picker with the optional value prefilled.
|
||||
*/
|
||||
show(value?: string): void;
|
||||
}
|
||||
|
||||
export interface IQuickAccessProvider {
|
||||
|
||||
/**
|
||||
* Called whenever a prefix was typed into quick pick that matches the provider.
|
||||
*
|
||||
* @param picker the picker to use for showing provider results. The picker is
|
||||
* automatically shown after the method returns, no need to call `show()`.
|
||||
* @param token providers have to check the cancellation token everytime after
|
||||
* 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).
|
||||
* @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;
|
||||
}
|
||||
|
||||
export interface IQuickAccessProviderHelp {
|
||||
|
||||
/**
|
||||
* The prefix to show for the help entry. If not provided,
|
||||
* the prefix used for registration will be taken.
|
||||
*/
|
||||
prefix?: string;
|
||||
|
||||
/**
|
||||
* A description text to help understand the intent of the provider.
|
||||
*/
|
||||
description: string;
|
||||
|
||||
/**
|
||||
* Separation between provider for editors and global ones.
|
||||
*/
|
||||
needsEditor: boolean;
|
||||
}
|
||||
|
||||
export interface IQuickAccessProviderDescriptor {
|
||||
|
||||
/**
|
||||
* The actual provider that will be instantiated as needed.
|
||||
*/
|
||||
readonly ctor: { new(...services: any /* TS BrandedService but no clue how to type this properly */[]): IQuickAccessProvider };
|
||||
|
||||
/**
|
||||
* The prefix for quick access picker to use the provider for.
|
||||
*/
|
||||
readonly prefix: string;
|
||||
|
||||
/**
|
||||
* A placeholder to use for the input field when the provider is active.
|
||||
* This will also be read out by screen readers and thus helps for
|
||||
* accessibility.
|
||||
*/
|
||||
readonly placeholder?: string;
|
||||
|
||||
/**
|
||||
* Documentation for the provider in the quick access help.
|
||||
*/
|
||||
readonly helpEntries: IQuickAccessProviderHelp[];
|
||||
|
||||
/**
|
||||
* A context key that will be set automatically when the
|
||||
* picker for the provider is showing.
|
||||
*/
|
||||
readonly contextKey?: string;
|
||||
}
|
||||
|
||||
export const Extensions = {
|
||||
Quickaccess: 'workbench.contributions.quickaccess'
|
||||
};
|
||||
|
||||
export interface IQuickAccessRegistry {
|
||||
|
||||
/**
|
||||
* The default provider to use when no other provider matches.
|
||||
*/
|
||||
defaultProvider: IQuickAccessProviderDescriptor;
|
||||
|
||||
/**
|
||||
* Registers a quick access provider to the platform.
|
||||
*/
|
||||
registerQuickAccessProvider(provider: IQuickAccessProviderDescriptor): IDisposable;
|
||||
|
||||
/**
|
||||
* Get all registered quick access providers.
|
||||
*/
|
||||
getQuickAccessProviders(): IQuickAccessProviderDescriptor[];
|
||||
|
||||
/**
|
||||
* Get a specific quick access provider for a given prefix.
|
||||
*/
|
||||
getQuickAccessProvider(prefix: string): IQuickAccessProviderDescriptor | undefined;
|
||||
}
|
||||
|
||||
class QuickAccessRegistry implements IQuickAccessRegistry {
|
||||
private providers: IQuickAccessProviderDescriptor[] = [];
|
||||
|
||||
private _defaultProvider: IQuickAccessProviderDescriptor | undefined = undefined;
|
||||
get defaultProvider(): IQuickAccessProviderDescriptor { return assertIsDefined(this._defaultProvider); }
|
||||
set defaultProvider(provider: IQuickAccessProviderDescriptor) { this._defaultProvider = provider; }
|
||||
|
||||
registerQuickAccessProvider(provider: IQuickAccessProviderDescriptor): IDisposable {
|
||||
this.providers.push(provider);
|
||||
|
||||
// sort the providers by decreasing prefix length, such that longer
|
||||
// prefixes take priority: 'ext' vs 'ext install' - the latter should win
|
||||
this.providers.sort((providerA, providerB) => providerB.prefix.length - providerA.prefix.length);
|
||||
|
||||
return toDisposable(() => this.providers.splice(this.providers.indexOf(provider), 1));
|
||||
}
|
||||
|
||||
getQuickAccessProviders(): IQuickAccessProviderDescriptor[] {
|
||||
return [this.defaultProvider, ...this.providers];
|
||||
}
|
||||
|
||||
getQuickAccessProvider(prefix: string): IQuickAccessProviderDescriptor | undefined {
|
||||
return prefix ? (first(this.providers, provider => startsWith(prefix, provider.prefix)) || undefined) : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
Registry.add(Extensions.Quickaccess, new QuickAccessRegistry());
|
||||
|
||||
//#region Helper class for simple picker based providers
|
||||
|
||||
export interface IPickerQuickAccessItem extends IQuickPickItem {
|
||||
|
||||
/**
|
||||
* A method that will be executed when the pick item is accepted from
|
||||
* the picker. The picker will close automatically before running this.
|
||||
*/
|
||||
accept?(): void;
|
||||
|
||||
/**
|
||||
* A method that will be executed when a button of the pick item was
|
||||
* clicked on. The picker will only close if `true` is returned.
|
||||
*
|
||||
* @param buttonIndex index of the button of the item that
|
||||
* was clicked.
|
||||
*
|
||||
* @returns a valud indicating if the picker should close or not.
|
||||
*/
|
||||
trigger?(buttonIndex: number): boolean;
|
||||
}
|
||||
|
||||
export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem> implements IQuickAccessProvider {
|
||||
|
||||
constructor(private prefix: string) { }
|
||||
|
||||
provide(picker: IQuickPick<T>, token: CancellationToken): IDisposable {
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
// Disable filtering & sorting, we control the results
|
||||
picker.matchOnLabel = picker.matchOnDescription = picker.matchOnDetail = picker.sortByLabel = false;
|
||||
|
||||
// Set initial picks and update on type
|
||||
let picksCts: CancellationTokenSource | undefined = undefined;
|
||||
const updatePickerItems = async () => {
|
||||
|
||||
// Cancel any previous ask for picks and busy
|
||||
picksCts?.dispose(true);
|
||||
picker.busy = false;
|
||||
|
||||
// Create new cancellation source for this run
|
||||
picksCts = new CancellationTokenSource(token);
|
||||
|
||||
// Collect picks and support both long running and short
|
||||
const res = this.getPicks(picker.value.substr(this.prefix.length).trim(), picksCts.token);
|
||||
if (Array.isArray(res)) {
|
||||
picker.items = res;
|
||||
} else {
|
||||
picker.busy = true;
|
||||
try {
|
||||
picker.items = await res;
|
||||
} finally {
|
||||
picker.busy = false;
|
||||
}
|
||||
}
|
||||
|
||||
this.getPicks(picker.value.substr(this.prefix.length).trim(), picksCts.token);
|
||||
};
|
||||
disposables.add(picker.onDidChangeValue(() => updatePickerItems()));
|
||||
updatePickerItems();
|
||||
|
||||
// Accept the pick on accept and hide picker
|
||||
disposables.add(picker.onDidAccept(() => {
|
||||
const [item] = picker.selectedItems;
|
||||
if (typeof item?.accept === 'function') {
|
||||
picker.hide();
|
||||
item.accept();
|
||||
}
|
||||
}));
|
||||
|
||||
// Trigger the pick with button index if button triggered
|
||||
disposables.add(picker.onDidTriggerItemButton(({ button, item }) => {
|
||||
if (typeof item.trigger === 'function') {
|
||||
const buttonIndex = item.buttons?.indexOf(button) ?? -1;
|
||||
if (buttonIndex >= 0) {
|
||||
const hide = item.trigger(buttonIndex);
|
||||
if (hide !== false) {
|
||||
picker.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
return disposables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of picks and separators as needed. If the picks are resolved
|
||||
* long running, the provided cancellation token should be used to cancel the
|
||||
* operation when the token signals this.
|
||||
*
|
||||
* The implementor is responsible for filtering and sorting the picks given the
|
||||
* provided `filter`.
|
||||
*/
|
||||
protected abstract getPicks(filter: string, token: CancellationToken): Array<T | IQuickPickSeparator> | Promise<Array<T | IQuickPickSeparator>>;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
@@ -3,11 +3,13 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IQuickPickItem, IPickOptions, IInputOptions, IQuickNavigateConfiguration, IQuickPick, IQuickInputButton, IInputBox, QuickPickInput } from 'vs/base/parts/quickinput/common/quickInput';
|
||||
import { IQuickAccessController } from 'vs/platform/quickinput/common/quickAccess';
|
||||
|
||||
export { IQuickPickItem, IPickOptions, IInputOptions, IQuickNavigateConfiguration, IQuickPick, IQuickInput, IQuickInputButton, IInputBox, IQuickPickItemButtonEvent, QuickPickInput, IQuickPickSeparator, IKeyMods } from 'vs/base/parts/quickinput/common/quickInput';
|
||||
export * from 'vs/base/parts/quickinput/common/quickInput';
|
||||
|
||||
export const IQuickInputService = createDecorator<IQuickInputService>('quickInputService');
|
||||
|
||||
@@ -18,7 +20,28 @@ export interface IQuickInputService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
/**
|
||||
* Opens the quick input box for selecting items and returns a promise with the user selected item(s) if any.
|
||||
* Provides access to the back button in quick input.
|
||||
*/
|
||||
readonly backButton: IQuickInputButton;
|
||||
|
||||
/**
|
||||
* Provides access to the quick access providers.
|
||||
*/
|
||||
readonly quickAccess: IQuickAccessController;
|
||||
|
||||
/**
|
||||
* Allows to register on the event that quick input is showing.
|
||||
*/
|
||||
readonly onShow: Event<void>;
|
||||
|
||||
/**
|
||||
* Allows to register on the event that quick input is hiding.
|
||||
*/
|
||||
readonly onHide: Event<void>;
|
||||
|
||||
/**
|
||||
* Opens the quick input box for selecting items and returns a promise
|
||||
* with the user selected item(s) if any.
|
||||
*/
|
||||
pick<T extends IQuickPickItem>(picks: Promise<QuickPickInput<T>[]> | QuickPickInput<T>[], options?: IPickOptions<T> & { canPickMany: true }, token?: CancellationToken): Promise<T[]>;
|
||||
pick<T extends IQuickPickItem>(picks: Promise<QuickPickInput<T>[]> | QuickPickInput<T>[], options?: IPickOptions<T> & { canPickMany: false }, token?: CancellationToken): Promise<T>;
|
||||
@@ -29,20 +52,46 @@ export interface IQuickInputService {
|
||||
*/
|
||||
input(options?: IInputOptions, token?: CancellationToken): Promise<string>;
|
||||
|
||||
backButton: IQuickInputButton;
|
||||
|
||||
/**
|
||||
* Provides raw access to the quick pick controller.
|
||||
*/
|
||||
createQuickPick<T extends IQuickPickItem>(): IQuickPick<T>;
|
||||
|
||||
/**
|
||||
* Provides raw access to the quick input controller.
|
||||
*/
|
||||
createInputBox(): IInputBox;
|
||||
|
||||
/**
|
||||
* Moves focus into quick input.
|
||||
*/
|
||||
focus(): void;
|
||||
|
||||
/**
|
||||
* Toggle the checked state of the selected item.
|
||||
*/
|
||||
toggle(): void;
|
||||
|
||||
/**
|
||||
* Navigate inside the opened quick input list.
|
||||
*/
|
||||
navigate(next: boolean, quickNavigate?: IQuickNavigateConfiguration): void;
|
||||
|
||||
accept(): Promise<void>;
|
||||
|
||||
/**
|
||||
* Navigate back in a multi-step quick input.
|
||||
*/
|
||||
back(): Promise<void>;
|
||||
|
||||
/**
|
||||
* Accept the selected item.
|
||||
*/
|
||||
accept(): Promise<void>;
|
||||
|
||||
/**
|
||||
* Cancels quick input and closes it.
|
||||
*/
|
||||
cancel(): Promise<void>;
|
||||
|
||||
// TODO@Ben remove once quick open is gone
|
||||
hide(focusLost?: boolean): void;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user