diff --git a/src/vs/workbench/api/browser/mainThreadQuickOpen.ts b/src/vs/workbench/api/browser/mainThreadQuickOpen.ts index c5aec867363..9a47764e962 100644 --- a/src/vs/workbench/api/browser/mainThreadQuickOpen.ts +++ b/src/vs/workbench/api/browser/mainThreadQuickOpen.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IPickOptions, IInputOptions, IQuickInputService, IQuickInput, IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; -import { ExtHostContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, TransferQuickPickItem, MainContext, IExtHostContext, TransferQuickInput, TransferQuickInputButton, IInputBoxOptions } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, TransferQuickPickItem, MainContext, IExtHostContext, TransferQuickInput, TransferQuickInputButton, IInputBoxOptions, TransferQuickPickItemOrSeparator } from 'vs/workbench/api/common/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { URI } from 'vs/base/common/uri'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -27,7 +27,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { private readonly _proxy: ExtHostQuickOpenShape; private readonly _quickInputService: IQuickInputService; private readonly _items: Record = {}; @@ -43,7 +43,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { } $show(instance: number, options: IPickOptions, token: CancellationToken): Promise { - const contents = new Promise((resolve, reject) => { + const contents = new Promise((resolve, reject) => { this._items[instance] = { resolve, reject }; }); @@ -73,7 +73,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { } } - $setItems(instance: number, items: TransferQuickPickItem[]): Promise { + $setItems(instance: number, items: TransferQuickPickItemOrSeparator[]): Promise { if (this._items[instance]) { this._items[instance].resolve(items); delete this._items[instance]; @@ -169,7 +169,11 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { } } else if (param === 'items') { handlesToItems.clear(); - params[param].forEach((item: TransferQuickPickItem) => { + params[param].forEach((item: TransferQuickPickItemOrSeparator) => { + if (item.type === 'separator') { + return; + } + if (item.buttons) { item.buttons = item.buttons.map((button: TransferQuickInputButton) => { if (button.iconPath) { diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 00e0d4f9e3a..1d7ab23720b 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -600,6 +600,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return >extHostMessageService.showMessage(extension, Severity.Error, message, rest[0], >rest.slice(1)); }, showQuickPick(items: any, options?: vscode.QuickPickOptions, token?: vscode.CancellationToken): any { + // TODO: remove this once quickPickSeparators has been finalized. + if (Array.isArray(items) && items.some((item) => item.kind !== undefined)) { + checkProposedApiEnabled(extension, 'quickPickSeparators'); + } return extHostQuickOpen.showQuickPick(items, options, token); }, showWorkspaceFolderPick(options?: vscode.WorkspaceFolderPickOptions) { @@ -689,7 +693,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostUrls.registerUriHandler(extension.identifier, handler); }, createQuickPick(): vscode.QuickPick { - return extHostQuickOpen.createQuickPick(extension.identifier); + return extHostQuickOpen.createQuickPick(extension); }, createInputBox(): vscode.InputBox { return extHostQuickOpen.createInputBox(extension.identifier); @@ -1307,6 +1311,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I FunctionCoverage: extHostTypes.FunctionCoverage, WorkspaceTrustState: extHostTypes.WorkspaceTrustState, LanguageStatusSeverity: extHostTypes.LanguageStatusSeverity, + QuickPickItemKind: extHostTypes.QuickPickItemKind, }; }; } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index d935e9ab65a..2144f6df3e6 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -510,6 +510,7 @@ export interface MainThreadTerminalServiceShape extends IDisposable { $sendProcessExit(terminalId: number, exitCode: number | undefined): void; } +export type TransferQuickPickItemOrSeparator = TransferQuickPickItem | quickInput.IQuickPickSeparator; export interface TransferQuickPickItem extends quickInput.IQuickPickItem { handle: number; buttons?: TransferQuickInputButton[]; @@ -548,7 +549,7 @@ export interface TransferQuickPick extends BaseTransferQuickInput { buttons?: TransferQuickInputButton[]; - items?: TransferQuickPickItem[]; + items?: TransferQuickPickItemOrSeparator[]; activeItems?: number[]; @@ -594,7 +595,7 @@ export interface IInputBoxOptions { export interface MainThreadQuickOpenShape extends IDisposable { $show(instance: number, options: quickInput.IPickOptions, token: CancellationToken): Promise; - $setItems(instance: number, items: TransferQuickPickItem[]): Promise; + $setItems(instance: number, items: TransferQuickPickItemOrSeparator[]): Promise; $setError(instance: number, error: Error): Promise; $input(options: IInputBoxOptions | undefined, validateInput: boolean, token: CancellationToken): Promise; $createOrUpdate(params: TransferQuickInput): Promise; diff --git a/src/vs/workbench/api/common/extHostQuickOpen.ts b/src/vs/workbench/api/common/extHostQuickOpen.ts index eb1c05c36a6..177d0649181 100644 --- a/src/vs/workbench/api/common/extHostQuickOpen.ts +++ b/src/vs/workbench/api/common/extHostQuickOpen.ts @@ -9,15 +9,16 @@ import { Emitter } from 'vs/base/common/event'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { IExtHostWorkspaceProvider } from 'vs/workbench/api/common/extHostWorkspace'; -import { InputBox, InputBoxOptions, QuickInput, QuickInputButton, QuickPick, QuickPickItem, QuickPickItemButtonEvent, QuickPickOptions, WorkspaceFolder, WorkspaceFolderPickOptions } from 'vscode'; -import { ExtHostQuickOpenShape, IMainContext, MainContext, TransferQuickPickItem, TransferQuickInput, TransferQuickInputButton } from './extHost.protocol'; +import type { InputBox, InputBoxOptions, QuickInput, QuickInputButton, QuickPick, QuickPickItem, QuickPickItemButtonEvent, QuickPickOptions, WorkspaceFolder, WorkspaceFolderPickOptions } from 'vscode'; +import { ExtHostQuickOpenShape, IMainContext, MainContext, TransferQuickInput, TransferQuickInputButton, TransferQuickPickItemOrSeparator } from './extHost.protocol'; import { URI } from 'vs/base/common/uri'; -import { ThemeIcon, QuickInputButtons } from 'vs/workbench/api/common/extHostTypes'; +import { ThemeIcon, QuickInputButtons, QuickPickItemKind } from 'vs/workbench/api/common/extHostTypes'; import { isPromiseCanceledError } from 'vs/base/common/errors'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { coalesce } from 'vs/base/common/arrays'; import Severity from 'vs/base/common/severity'; import { ThemeIcon as ThemeIconUtils } from 'vs/platform/theme/common/themeService'; +import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; export type Item = string | QuickPickItem; @@ -31,7 +32,7 @@ export interface ExtHostQuickOpen { showWorkspaceFolderPick(options?: WorkspaceFolderPickOptions, token?: CancellationToken): Promise - createQuickPick(extensionId: ExtensionIdentifier): QuickPick; + createQuickPick(extensionId: IExtensionDescription): QuickPick; createInputBox(extensionId: ExtensionIdentifier): InputBox; } @@ -87,33 +88,23 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx return itemsPromise.then(items => { - const pickItems: TransferQuickPickItem[] = []; + const pickItems: TransferQuickPickItemOrSeparator[] = []; for (let handle = 0; handle < items.length; handle++) { - const item = items[handle]; - let label: string; - let description: string | undefined; - let detail: string | undefined; - let picked: boolean | undefined; - let alwaysShow: boolean | undefined; - if (typeof item === 'string') { - label = item; + pickItems.push({ label: item, handle }); + } else if (item.kind === QuickPickItemKind.Separator) { + pickItems.push({ type: 'separator', label: item.label }); } else { - label = item.label; - description = item.description; - detail = item.detail; - picked = item.picked; - alwaysShow = item.alwaysShow; + pickItems.push({ + label: item.label, + description: item.description, + detail: item.detail, + picked: item.picked, + alwaysShow: item.alwaysShow, + handle + }); } - pickItems.push({ - label, - description, - handle, - detail, - picked, - alwaysShow - }); } // handle selection changes @@ -192,8 +183,8 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx // ---- QuickInput - createQuickPick(extensionId: ExtensionIdentifier): QuickPick { - const session: ExtHostQuickPick = new ExtHostQuickPick(extensionId, () => this._sessions.delete(session._id)); + createQuickPick(extension: IExtensionDescription): QuickPick { + const session: ExtHostQuickPick = new ExtHostQuickPick(extension, () => this._sessions.delete(session._id)); this._sessions.set(session._id, session); return session; } @@ -531,8 +522,9 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx private readonly _onDidChangeSelectionEmitter = new Emitter(); private readonly _onDidTriggerItemButtonEmitter = new Emitter>(); - constructor(extensionId: ExtensionIdentifier, onDispose: () => void) { - super(extensionId, onDispose); + // TODO: revert this change once quickPickSeparators has been finalized. + constructor(private readonly extension: IExtensionDescription, onDispose: () => void) { + super(extension.identifier, onDispose); this._disposables.push( this._onDidChangeActiveEmitter, this._onDidChangeSelectionEmitter, @@ -546,6 +538,10 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx } set items(items: T[]) { + if (items.some((item) => item.kind !== undefined)) { + checkProposedApiEnabled(this.extension, 'quickPickSeparators'); + } + this._items = items.slice(); this._handlesToItems.clear(); this._itemsToHandles.clear(); @@ -553,22 +549,33 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx this._handlesToItems.set(i, item); this._itemsToHandles.set(item, i); }); + + const pickItems: TransferQuickPickItemOrSeparator[] = []; + for (let handle = 0; handle < items.length; handle++) { + const item = items[handle]; + if (item.kind === QuickPickItemKind.Separator) { + pickItems.push({ type: 'separator', label: item.label }); + } else { + pickItems.push({ + handle, + label: item.label, + description: item.description, + detail: item.detail, + picked: item.picked, + alwaysShow: item.alwaysShow, + buttons: item.buttons?.map((button, i) => { + return { + ...getIconPathOrClass(button), + tooltip: button.tooltip, + handle: i + }; + }), + }); + } + } + this.update({ - items: items.map((item, i) => ({ - label: item.label, - description: item.description, - handle: i, - detail: item.detail, - picked: item.picked, - alwaysShow: item.alwaysShow, - buttons: item.buttons?.map((button, i) => { - return { - ...getIconPathOrClass(button), - tooltip: button.tooltip, - handle: i - }; - }), - })) + items: pickItems, }); } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 4f193198645..4623fe4de8d 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -2904,6 +2904,11 @@ export class QuickInputButtons { private constructor() { } } +export enum QuickPickItemKind { + Default = 1, + Separator = 2, +} + export enum ExtensionKind { UI = 1, Workspace = 2 diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 1ccca4a1ffc..d413ce4beab 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -39,6 +39,7 @@ export const allApiProposals = Object.freeze({ notebookMessaging: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookMessaging.d.ts', notebookMime: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookMime.d.ts', portsAttributes: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.portsAttributes.d.ts', + quickPickSeparators: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.quickPickSeparators.d.ts', quickPickSortByLabel: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.quickPickSortByLabel.d.ts', resolvers: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.resolvers.d.ts', scmActionButton: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.scmActionButton.d.ts', diff --git a/src/vscode-dts/vscode.proposed.quickPickSeparators.d.ts b/src/vscode-dts/vscode.proposed.quickPickSeparators.d.ts new file mode 100644 index 00000000000..c73c37b2095 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.quickPickSeparators.d.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/74967 + + export enum QuickPickItemKind { + Default = 1, + Separator = 2, + } + + export interface QuickPickItem { + kind?: QuickPickItemKind + } +}