mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from vscode 099a7622e6e90dbcc226e428d4e35a72cb19ecbc (#9646)
* Merge from vscode 099a7622e6e90dbcc226e428d4e35a72cb19ecbc * fix strict
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { PickerQuickAccessProvider, IPickerQuickAccessItem } from 'vs/platform/quickinput/common/quickAccess';
|
||||
import { PickerQuickAccessProvider, IPickerQuickAccessItem } from 'vs/platform/quickinput/browser/pickerQuickAccess';
|
||||
import { distinct } from 'vs/base/common/arrays';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { DisposableStore, Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
@@ -40,9 +40,7 @@ export abstract class AbstractCommandsQuickAccessProvider extends PickerQuickAcc
|
||||
|
||||
private static WORD_FILTER = or(matchesPrefix, matchesWords, matchesContiguousSubString);
|
||||
|
||||
private readonly disposables = new DisposableStore();
|
||||
|
||||
private readonly commandsHistory = this.disposables.add(this.instantiationService.createInstance(CommandsHistory));
|
||||
private readonly commandsHistory = this._register(this.instantiationService.createInstance(CommandsHistory));
|
||||
|
||||
constructor(
|
||||
private options: ICommandsQuickAccessOptions,
|
||||
@@ -173,11 +171,10 @@ export abstract class AbstractCommandsQuickAccessProvider extends PickerQuickAcc
|
||||
return commandPicks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses to provide the actual command entries.
|
||||
*/
|
||||
protected abstract getCommandPicks(disposables: DisposableStore, token: CancellationToken): Promise<Array<ICommandQuickPick>>;
|
||||
|
||||
dispose(): void {
|
||||
this.disposables.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
interface ISerializedCommandHistory {
|
||||
|
||||
@@ -36,7 +36,7 @@ export class HelpQuickAccessProvider implements IQuickAccessProvider {
|
||||
// name of a provider (e.g. `?term` for terminals)
|
||||
disposables.add(picker.onDidChangeValue(value => {
|
||||
const providerDescriptor = this.registry.getQuickAccessProvider(value.substr(HelpQuickAccessProvider.PREFIX.length));
|
||||
if (providerDescriptor && providerDescriptor.prefix !== HelpQuickAccessProvider.PREFIX) {
|
||||
if (providerDescriptor && providerDescriptor.prefix && providerDescriptor.prefix !== HelpQuickAccessProvider.PREFIX) {
|
||||
this.quickInputService.quickAccess.show(providerDescriptor.prefix);
|
||||
}
|
||||
}));
|
||||
|
||||
167
src/vs/platform/quickinput/browser/pickerQuickAccess.ts
Normal file
167
src/vs/platform/quickinput/browser/pickerQuickAccess.ts
Normal file
@@ -0,0 +1,167 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { IQuickPickSeparator, IKeyMods, IQuickPickAcceptEvent } from 'vs/base/parts/quickinput/common/quickInput';
|
||||
import { IQuickAccessProvider } from 'vs/platform/quickinput/common/quickAccess';
|
||||
import { IDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export enum TriggerAction {
|
||||
|
||||
/**
|
||||
* Do nothing after the button was clicked.
|
||||
*/
|
||||
NO_ACTION,
|
||||
|
||||
/**
|
||||
* Close the picker.
|
||||
*/
|
||||
CLOSE_PICKER,
|
||||
|
||||
/**
|
||||
* Update the results of the picker.
|
||||
*/
|
||||
REFRESH_PICKER
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* @param keyMods the state of modifier keys when the item was accepted.
|
||||
* @param event the underlying event that caused the accept to trigger.
|
||||
*/
|
||||
accept?(keyMods: IKeyMods, event: IQuickPickAcceptEvent): void;
|
||||
|
||||
/**
|
||||
* A method that will be executed when a button of the pick item was
|
||||
* clicked on.
|
||||
*
|
||||
* @param buttonIndex index of the button of the item that
|
||||
* was clicked.
|
||||
*
|
||||
* @param the state of modifier keys when the button was triggered.
|
||||
*
|
||||
* @returns a value that indicates what should happen after the trigger
|
||||
* which can be a `Promise` for long running operations.
|
||||
*/
|
||||
trigger?(buttonIndex: number, keyMods: IKeyMods): TriggerAction | Promise<TriggerAction>;
|
||||
}
|
||||
|
||||
export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem> extends Disposable implements IQuickAccessProvider {
|
||||
|
||||
constructor(private prefix: string) {
|
||||
super();
|
||||
}
|
||||
|
||||
provide(picker: IQuickPick<T>, token: CancellationToken): IDisposable {
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
// Allow subclasses to configure picker
|
||||
this.configure(picker);
|
||||
|
||||
// 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(), disposables.add(new DisposableStore()), picksCts.token);
|
||||
if (Array.isArray(res)) {
|
||||
picker.items = res;
|
||||
} else {
|
||||
picker.busy = true;
|
||||
try {
|
||||
const items = await res;
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
picker.items = items;
|
||||
} finally {
|
||||
if (!token.isCancellationRequested) {
|
||||
picker.busy = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
disposables.add(picker.onDidChangeValue(() => updatePickerItems()));
|
||||
updatePickerItems();
|
||||
|
||||
// Accept the pick on accept and hide picker
|
||||
disposables.add(picker.onDidAccept(event => {
|
||||
const [item] = picker.selectedItems;
|
||||
if (typeof item?.accept === 'function') {
|
||||
if (!event.inBackground) {
|
||||
picker.hide(); // hide picker unless we accept in background
|
||||
}
|
||||
item.accept(picker.keyMods, event);
|
||||
}
|
||||
}));
|
||||
|
||||
// Trigger the pick with button index if button triggered
|
||||
disposables.add(picker.onDidTriggerItemButton(async ({ button, item }) => {
|
||||
if (typeof item.trigger === 'function') {
|
||||
const buttonIndex = item.buttons?.indexOf(button) ?? -1;
|
||||
if (buttonIndex >= 0) {
|
||||
const result = item.trigger(buttonIndex, picker.keyMods);
|
||||
const action = (typeof result === 'number') ? result : await result;
|
||||
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case TriggerAction.NO_ACTION:
|
||||
break;
|
||||
case TriggerAction.CLOSE_PICKER:
|
||||
picker.hide();
|
||||
break;
|
||||
case TriggerAction.REFRESH_PICKER:
|
||||
updatePickerItems();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
return disposables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses can override this method to configure the picker before showing it.
|
||||
*
|
||||
* @param picker the picker instance used for the quick access before it opens.
|
||||
*/
|
||||
protected configure(picker: IQuickPick<T>): void { }
|
||||
|
||||
/**
|
||||
* 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`.
|
||||
*
|
||||
* @param filter a filter to apply to the picks.
|
||||
* @param disposables can be used to register disposables that should be cleaned
|
||||
* up when the picker closes.
|
||||
* @param token for long running tasks, implementors need to check on cancellation
|
||||
* through this token.
|
||||
*/
|
||||
protected abstract getPicks(filter: string, disposables: DisposableStore, token: CancellationToken): Array<T | IQuickPickSeparator> | Promise<Array<T | IQuickPickSeparator>>;
|
||||
}
|
||||
@@ -37,11 +37,11 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon
|
||||
// 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.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);
|
||||
picker.contextKey = descriptor?.contextKey;
|
||||
picker.filterValue = (value: string) => value.substring(descriptor ? descriptor.prefix.length : 0);
|
||||
|
||||
// Remember as last active picker and clean up once picker get's disposed
|
||||
this.lastActivePicker = picker;
|
||||
@@ -72,8 +72,10 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon
|
||||
}
|
||||
}));
|
||||
|
||||
// Ask provider to fill the picker as needed
|
||||
disposables.add(provider.provide(picker, cts.token));
|
||||
// Ask provider to fill the picker as needed if we have one
|
||||
if (provider) {
|
||||
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
|
||||
@@ -81,8 +83,11 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon
|
||||
picker.show();
|
||||
}
|
||||
|
||||
private getOrInstantiateProvider(value: string): [IQuickAccessProvider, IQuickAccessProviderDescriptor] {
|
||||
const providerDescriptor = this.registry.getQuickAccessProvider(value) || this.registry.defaultProvider;
|
||||
private getOrInstantiateProvider(value: string): [IQuickAccessProvider | undefined, IQuickAccessProviderDescriptor | undefined] {
|
||||
const providerDescriptor = this.registry.getQuickAccessProvider(value);
|
||||
if (!providerDescriptor) {
|
||||
return [undefined, undefined];
|
||||
}
|
||||
|
||||
let provider = this.mapProviderToDescriptor.get(providerDescriptor);
|
||||
if (!provider) {
|
||||
|
||||
@@ -4,13 +4,11 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { first } from 'vs/base/common/arrays';
|
||||
import { first, coalesce } 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';
|
||||
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export interface IQuickAccessController {
|
||||
|
||||
@@ -93,11 +91,6 @@ export const Extensions = {
|
||||
|
||||
export interface IQuickAccessRegistry {
|
||||
|
||||
/**
|
||||
* The default provider to use when no other provider matches.
|
||||
*/
|
||||
defaultProvider: IQuickAccessProviderDescriptor;
|
||||
|
||||
/**
|
||||
* Registers a quick access provider to the platform.
|
||||
*/
|
||||
@@ -114,172 +107,54 @@ export interface IQuickAccessRegistry {
|
||||
getQuickAccessProvider(prefix: string): IQuickAccessProviderDescriptor | undefined;
|
||||
}
|
||||
|
||||
class QuickAccessRegistry implements IQuickAccessRegistry {
|
||||
export 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; }
|
||||
private defaultProvider: IQuickAccessProviderDescriptor | undefined = undefined;
|
||||
|
||||
registerQuickAccessProvider(provider: IQuickAccessProviderDescriptor): IDisposable {
|
||||
this.providers.push(provider);
|
||||
|
||||
// Extract the default provider when no prefix is present
|
||||
if (provider.prefix.length === 0) {
|
||||
this.defaultProvider = provider;
|
||||
} else {
|
||||
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));
|
||||
return toDisposable(() => {
|
||||
this.providers.splice(this.providers.indexOf(provider), 1);
|
||||
|
||||
if (this.defaultProvider === provider) {
|
||||
this.defaultProvider = undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getQuickAccessProviders(): IQuickAccessProviderDescriptor[] {
|
||||
return [this.defaultProvider, ...this.providers];
|
||||
return coalesce([this.defaultProvider, ...this.providers]);
|
||||
}
|
||||
|
||||
getQuickAccessProvider(prefix: string): IQuickAccessProviderDescriptor | undefined {
|
||||
return prefix ? (first(this.providers, provider => startsWith(prefix, provider.prefix)) || undefined) : undefined;
|
||||
const result = prefix ? (first(this.providers, provider => startsWith(prefix, provider.prefix)) || undefined) : undefined;
|
||||
|
||||
return result || this.defaultProvider;
|
||||
}
|
||||
|
||||
clear(): Function {
|
||||
const providers = [...this.providers];
|
||||
const defaultProvider = this.defaultProvider;
|
||||
|
||||
this.providers = [];
|
||||
this.defaultProvider = undefined;
|
||||
|
||||
return () => {
|
||||
this.providers = providers;
|
||||
this.defaultProvider = defaultProvider;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Registry.add(Extensions.Quickaccess, new QuickAccessRegistry());
|
||||
|
||||
//#region Helper class for simple picker based providers
|
||||
|
||||
export enum TriggerAction {
|
||||
|
||||
/**
|
||||
* Do nothing after the button was clicked.
|
||||
*/
|
||||
NO_ACTION,
|
||||
|
||||
/**
|
||||
* Close the picker.
|
||||
*/
|
||||
CLOSE_PICKER,
|
||||
|
||||
/**
|
||||
* Update the results of the picker.
|
||||
*/
|
||||
REFRESH_PICKER
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* @param buttonIndex index of the button of the item that
|
||||
* was clicked.
|
||||
*
|
||||
* @returns a value that indicates what should happen after the trigger
|
||||
* which can be a `Promise` for long running operations.
|
||||
*/
|
||||
trigger?(buttonIndex: number): TriggerAction | Promise<TriggerAction>;
|
||||
}
|
||||
|
||||
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(), disposables.add(new DisposableStore()), picksCts.token);
|
||||
if (Array.isArray(res)) {
|
||||
picker.items = res;
|
||||
} else {
|
||||
picker.busy = true;
|
||||
try {
|
||||
const items = await res;
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
picker.items = items;
|
||||
} finally {
|
||||
if (!token.isCancellationRequested) {
|
||||
picker.busy = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
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(async ({ button, item }) => {
|
||||
if (typeof item.trigger === 'function') {
|
||||
const buttonIndex = item.buttons?.indexOf(button) ?? -1;
|
||||
if (buttonIndex >= 0) {
|
||||
const result = item.trigger(buttonIndex);
|
||||
const action = (typeof result === 'number') ? result : await result;
|
||||
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case TriggerAction.NO_ACTION:
|
||||
break;
|
||||
case TriggerAction.CLOSE_PICKER:
|
||||
picker.hide();
|
||||
break;
|
||||
case TriggerAction.REFRESH_PICKER:
|
||||
updatePickerItems();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
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`.
|
||||
*
|
||||
* @param filter a filter to apply to the picks.
|
||||
* @param disposables can be used to register disposables that should be cleaned
|
||||
* up when the picker closes.
|
||||
* @param token for long running tasks, implementors need to check on cancellation
|
||||
* through this token.
|
||||
*/
|
||||
protected abstract getPicks(filter: string, disposables: DisposableStore, token: CancellationToken): Array<T | IQuickPickSeparator> | Promise<Array<T | IQuickPickSeparator>>;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
Reference in New Issue
Block a user