Skip to content

Commit

Permalink
First cut of quick pick separators as kind. ref #74967
Browse files Browse the repository at this point in the history
  • Loading branch information
TylerLeonhardt committed Nov 10, 2021
1 parent 958a5ad commit a41001c
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 74 deletions.
7 changes: 7 additions & 0 deletions src/vs/vscode.proposed.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2762,4 +2762,11 @@ declare module 'vscode' {

//#endregion

//#region quickPickItemKind: https://github.com/microsoft/vscode/issues/74967

export interface QuickPickItem {
kind?: string | { label: string; };
}

//#endregion
}
26 changes: 15 additions & 11 deletions src/vs/workbench/api/browser/mainThreadQuickOpen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
*--------------------------------------------------------------------------------------------*/

import { IPickOptions, IInputOptions, IQuickInputService, IQuickInput, IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { ExtHostContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, TransferQuickPickItems, 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';

interface QuickInputSession {
input: IQuickInput;
handlesToItems: Map<number, TransferQuickPickItems>;
handlesToItems: Map<number, TransferQuickPickItem>;
}

function reviveIconPathUris(iconPath: { dark: URI; light?: URI | undefined; }) {
Expand All @@ -27,7 +27,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
private readonly _proxy: ExtHostQuickOpenShape;
private readonly _quickInputService: IQuickInputService;
private readonly _items: Record<number, {
resolve(items: TransferQuickPickItems[]): void;
resolve(items: TransferQuickPickItemOrSeparator[]): void;
reject(error: Error): void;
}> = {};

Expand All @@ -42,16 +42,16 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
public dispose(): void {
}

$show(instance: number, options: IPickOptions<TransferQuickPickItems>, token: CancellationToken): Promise<number | number[] | undefined> {
const contents = new Promise<TransferQuickPickItems[]>((resolve, reject) => {
$show(instance: number, options: IPickOptions<TransferQuickPickItem>, token: CancellationToken): Promise<number | number[] | undefined> {
const contents = new Promise<TransferQuickPickItemOrSeparator[]>((resolve, reject) => {
this._items[instance] = { resolve, reject };
});

options = {
...options,
onDidFocus: el => {
if (el) {
this._proxy.$onItemSelected((<TransferQuickPickItems>el).handle);
this._proxy.$onItemSelected((<TransferQuickPickItem>el).handle);
}
}
};
Expand All @@ -73,7 +73,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
}
}

$setItems(instance: number, items: TransferQuickPickItems[]): Promise<void> {
$setItems(instance: number, items: TransferQuickPickItemOrSeparator[]): Promise<void> {
if (this._items[instance]) {
this._items[instance].resolve(items);
delete this._items[instance];
Expand Down Expand Up @@ -140,13 +140,13 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
// Add extra events specific for quickpick
const quickpick = input as IQuickPick<IQuickPickItem>;
quickpick.onDidChangeActive(items => {
this._proxy.$onDidChangeActive(sessionId, items.map(item => (item as TransferQuickPickItems).handle));
this._proxy.$onDidChangeActive(sessionId, items.map(item => (item as TransferQuickPickItem).handle));
});
quickpick.onDidChangeSelection(items => {
this._proxy.$onDidChangeSelection(sessionId, items.map(item => (item as TransferQuickPickItems).handle));
this._proxy.$onDidChangeSelection(sessionId, items.map(item => (item as TransferQuickPickItem).handle));
});
quickpick.onDidTriggerItemButton((e) => {
this._proxy.$onDidTriggerItemButton(sessionId, (e.item as TransferQuickPickItems).handle, (e.button as TransferQuickInputButton).handle);
this._proxy.$onDidTriggerItemButton(sessionId, (e.item as TransferQuickPickItem).handle, (e.button as TransferQuickInputButton).handle);
});
}

Expand All @@ -169,7 +169,11 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
}
} else if (param === 'items') {
handlesToItems.clear();
params[param].forEach((item: TransferQuickPickItems) => {
params[param].forEach((item: TransferQuickPickItemOrSeparator) => {
if (item.type === 'separator') {
return;
}

if (item.buttons) {
item.buttons = item.buttons.map((button: TransferQuickInputButton) => {
if (button.iconPath) {
Expand Down
9 changes: 5 additions & 4 deletions src/vs/workbench/api/common/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,8 @@ export interface MainThreadTerminalServiceShape extends IDisposable {
$sendProcessExit(terminalId: number, exitCode: number | undefined): void;
}

export interface TransferQuickPickItems extends quickInput.IQuickPickItem {
export type TransferQuickPickItemOrSeparator = TransferQuickPickItem | quickInput.IQuickPickSeparator;
export interface TransferQuickPickItem extends quickInput.IQuickPickItem {
handle: number;
buttons?: TransferQuickInputButton[];
}
Expand Down Expand Up @@ -548,7 +549,7 @@ export interface TransferQuickPick extends BaseTransferQuickInput {

buttons?: TransferQuickInputButton[];

items?: TransferQuickPickItems[];
items?: TransferQuickPickItemOrSeparator[];

activeItems?: number[];

Expand Down Expand Up @@ -593,8 +594,8 @@ export interface IInputBoxOptions {
}

export interface MainThreadQuickOpenShape extends IDisposable {
$show(instance: number, options: quickInput.IPickOptions<TransferQuickPickItems>, token: CancellationToken): Promise<number | number[] | undefined>;
$setItems(instance: number, items: TransferQuickPickItems[]): Promise<void>;
$show(instance: number, options: quickInput.IPickOptions<TransferQuickPickItem>, token: CancellationToken): Promise<number | number[] | undefined>;
$setItems(instance: number, items: TransferQuickPickItemOrSeparator[]): Promise<void>;
$setError(instance: number, error: Error): Promise<void>;
$input(options: IInputBoxOptions | undefined, validateInput: boolean, token: CancellationToken): Promise<string | undefined>;
$createOrUpdate(params: TransferQuickInput): Promise<void>;
Expand Down
141 changes: 82 additions & 59 deletions src/vs/workbench/api/common/extHostQuickOpen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ 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, TransferQuickPickItems, TransferQuickInput, TransferQuickInputButton } from './extHost.protocol';
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 { isPromiseCanceledError } from 'vs/base/common/errors';
Expand Down Expand Up @@ -59,7 +59,7 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx
showQuickPick(itemsOrItemsPromise: QuickPickItem[] | Promise<QuickPickItem[]>, enableProposedApi: boolean, options: QuickPickOptions & { canPickMany: true; }, token?: CancellationToken): Promise<QuickPickItem[] | undefined>;
showQuickPick(itemsOrItemsPromise: string[] | Promise<string[]>, enableProposedApi: boolean, options?: QuickPickOptions, token?: CancellationToken): Promise<string | undefined>;
showQuickPick(itemsOrItemsPromise: QuickPickItem[] | Promise<QuickPickItem[]>, enableProposedApi: boolean, options?: QuickPickOptions, token?: CancellationToken): Promise<QuickPickItem | undefined>;
showQuickPick(itemsOrItemsPromise: Item[] | Promise<Item[]>, enableProposedApi: boolean, options?: QuickPickOptions, token: CancellationToken = CancellationToken.None): Promise<Item | Item[] | undefined> {
async showQuickPick(itemsOrItemsPromise: Item[] | Promise<Item[]>, enableProposedApi: boolean, options?: QuickPickOptions, token: CancellationToken = CancellationToken.None): Promise<Item | Item[] | undefined> {

// clear state from last invocation
this._onDidSelectItem = undefined;
Expand All @@ -80,70 +80,81 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx
const widgetClosedMarker = {};
const widgetClosedPromise = quickPickWidget.then(() => widgetClosedMarker);

return Promise.race([widgetClosedPromise, itemsPromise]).then(result => {
if (result === widgetClosedMarker) {
return undefined;
}
const result = await Promise.race([widgetClosedPromise, itemsPromise]);
if (result === widgetClosedMarker) {
return undefined;
}

return itemsPromise.then(items => {

const pickItems: TransferQuickPickItems[] = [];
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;
} else {
label = item.label;
description = item.description;
detail = item.detail;
picked = item.picked;
alwaysShow = item.alwaysShow;
}
pickItems.push({
label,
description,
handle,
detail,
picked,
alwaysShow
});
const items = await itemsPromise;

const pickItems: TransferQuickPickItemOrSeparator[] = [];
let lastKind: string | { label: string; } | undefined = undefined;
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') {
// 'string' items have a kind of undefined so
// if the previous item had a kind, that is considered a
// change and would cause the addition of a separator.
if (lastKind) {
pickItems.push({ type: 'separator' });
lastKind = undefined;
}

// handle selection changes
if (options && typeof options.onDidSelectItem === 'function') {
this._onDidSelectItem = (handle) => {
options.onDidSelectItem!(items[handle]);
};
label = item;
} else {
if (lastKind !== item.kind) {
const label: string | undefined = typeof item.kind === 'string' ? item.kind : item.kind?.label;
pickItems.push({ type: 'separator', label });
lastKind = item.kind;
}
label = item.label;
description = item.description;
detail = item.detail;
picked = item.picked;
alwaysShow = item.alwaysShow;
}
pickItems.push({
label,
description,
handle,
detail,
picked,
alwaysShow
});
}

// handle selection changes
if (options && typeof options.onDidSelectItem === 'function') {
this._onDidSelectItem = (handle) => {
options.onDidSelectItem!(items[handle]);
};
}

// show items
proxy.$setItems(instance, pickItems);
// show items
proxy.$setItems(instance, pickItems);

return quickPickWidget.then(handle => {
if (typeof handle === 'number') {
return items[handle];
} else if (Array.isArray(handle)) {
return handle.map(h => items[h]);
}
return undefined;
});
try {
return quickPickWidget.then(handle => {
if (typeof handle === 'number') {
return items[handle];
} else if (Array.isArray(handle)) {
return handle.map(h => items[h]);
}
return undefined;
});
}).then(undefined, err => {
} catch (err) {
if (isPromiseCanceledError(err)) {
return undefined;
}

proxy.$setError(instance, err);

return Promise.reject(err);
});
throw err;
}
}

$onItemSelected(handle: number): void {
Expand Down Expand Up @@ -553,8 +564,16 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx
this._handlesToItems.set(i, item);
this._itemsToHandles.set(item, i);
});
this.update({
items: items.map((item, i) => ({
const itemsItems: Array<TransferQuickPickItemOrSeparator> = [];
let lastKind: string | { label: string; } | undefined = undefined;
items.forEach((item, i) => {

if (lastKind !== item.kind) {
const label: string | undefined = typeof item.kind === 'string' ? item.kind : item.kind?.label;
itemsItems.push({ type: 'separator', label });
lastKind = item.kind;
}
itemsItems.push({
label: item.label,
description: item.description,
handle: i,
Expand All @@ -567,8 +586,12 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx
tooltip: button.tooltip,
handle: i
};
}),
}))
})
});
});

this.update({
items: itemsItems
});
}

Expand Down

0 comments on commit a41001c

Please sign in to comment.