mirror of
https://github.com/microsoft/vscode.git
synced 2026-02-15 07:28:05 +00:00
Add toggle support for QuickInput/QuickPick, resourceUri support for QuickPickItem (#271598)
* Add quickPickItemResource API proposal * Transfer resourceUri from extension host to main thread. * Make proposed API checks consistent. * Process resourceUri * Fix up resourceUri mapping logic * API proposal * Transfer toggles from extension host to main thread * Support Folder icon, refactor label/description derivation. * Update * Update API proposal per API review * Update transfer logic per API changes * Move toggles to the base input interface * Handle toggle button type * Fix up * Updates * Propagate checked state, dispose removed toggles. * Nit * Expand icons * Feedback/updates * Added comments, PR feedback * Updates * Revert some change, add typings and unit-tests to converters. * Add a quick pick test for resourceUri * Test updates
This commit is contained in:
@@ -32,7 +32,9 @@
|
||||
"notebookMessaging",
|
||||
"notebookMime",
|
||||
"portsAttributes",
|
||||
"quickInputButtonLocation",
|
||||
"quickPickSortByLabel",
|
||||
"quickPickItemResource",
|
||||
"resolvers",
|
||||
"scmActionButton",
|
||||
"scmSelectedProvider",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { commands, Disposable, QuickPick, QuickPickItem, window } from 'vscode';
|
||||
import { commands, Disposable, QuickPick, QuickPickItem, window, workspace } from 'vscode';
|
||||
import { assertNoRpc, closeAllEditors } from '../utils';
|
||||
|
||||
interface QuickPickExpected {
|
||||
@@ -248,6 +248,71 @@ suite('vscode API - quick input', function () {
|
||||
quickPick.hide();
|
||||
await waitForHide(quickPick);
|
||||
});
|
||||
|
||||
test('createQuickPick, match item by label derived from resourceUri', function (_done) {
|
||||
let done = (err?: any) => {
|
||||
done = () => { };
|
||||
_done(err);
|
||||
};
|
||||
|
||||
const quickPick = createQuickPick({
|
||||
events: ['active', 'selection', 'accept', 'hide'],
|
||||
activeItems: [['']],
|
||||
selectionItems: [['']],
|
||||
acceptedItems: {
|
||||
active: [['']],
|
||||
selection: [['']],
|
||||
dispose: [true]
|
||||
},
|
||||
}, (err?: any) => done(err));
|
||||
|
||||
const baseUri = workspace!.workspaceFolders![0].uri;
|
||||
quickPick.items = [
|
||||
{ label: 'a1', resourceUri: baseUri.with({ path: baseUri.path + '/test1.txt' }) },
|
||||
{ label: '', resourceUri: baseUri.with({ path: baseUri.path + '/test2.txt' }) },
|
||||
{ label: 'a3', resourceUri: baseUri.with({ path: baseUri.path + '/test3.txt' }) }
|
||||
];
|
||||
quickPick.value = 'test2.txt';
|
||||
quickPick.show();
|
||||
|
||||
(async () => {
|
||||
await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem');
|
||||
})()
|
||||
.catch(err => done(err));
|
||||
});
|
||||
|
||||
test('createQuickPick, match item by description derived from resourceUri', function (_done) {
|
||||
let done = (err?: any) => {
|
||||
done = () => { };
|
||||
_done(err);
|
||||
};
|
||||
|
||||
const quickPick = createQuickPick({
|
||||
events: ['active', 'selection', 'accept', 'hide'],
|
||||
activeItems: [['a2']],
|
||||
selectionItems: [['a2']],
|
||||
acceptedItems: {
|
||||
active: [['a2']],
|
||||
selection: [['a2']],
|
||||
dispose: [true]
|
||||
},
|
||||
}, (err?: any) => done(err));
|
||||
|
||||
const baseUri = workspace!.workspaceFolders![0].uri;
|
||||
quickPick.items = [
|
||||
{ label: 'a1', resourceUri: baseUri.with({ path: baseUri.path + '/test1.txt' }) },
|
||||
{ label: 'a2', resourceUri: baseUri.with({ path: baseUri.path + '/test2.txt' }) },
|
||||
{ label: 'a3', resourceUri: baseUri.with({ path: baseUri.path + '/test3.txt' }) }
|
||||
];
|
||||
quickPick.matchOnDescription = true;
|
||||
quickPick.value = 'test2.txt';
|
||||
quickPick.show();
|
||||
|
||||
(async () => {
|
||||
await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem');
|
||||
})()
|
||||
.catch(err => done(err));
|
||||
});
|
||||
});
|
||||
|
||||
function createQuickPick(expected: QuickPickExpected, done: (err?: any) => void, record = false) {
|
||||
|
||||
@@ -101,4 +101,17 @@ export namespace ThemeIcon {
|
||||
return ti1.id === ti2.id && ti1.color?.id === ti2.color?.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether specified icon is defined and has 'file' ID.
|
||||
*/
|
||||
export function isFile(icon: ThemeIcon | undefined): boolean {
|
||||
return icon?.id === Codicon.file.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether specified icon is defined and has 'folder' ID.
|
||||
*/
|
||||
export function isFolder(icon: ThemeIcon | undefined): boolean {
|
||||
return icon?.id === Codicon.folder.id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,6 +328,9 @@ const _allApiProposals = {
|
||||
quickInputButtonLocation: {
|
||||
proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.quickInputButtonLocation.d.ts',
|
||||
},
|
||||
quickPickItemResource: {
|
||||
proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.quickPickItemResource.d.ts',
|
||||
},
|
||||
quickPickItemTooltip: {
|
||||
proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.quickPickItemTooltip.d.ts',
|
||||
},
|
||||
|
||||
@@ -359,6 +359,11 @@ export interface IQuickInput extends IDisposable {
|
||||
*/
|
||||
ignoreFocusOut: boolean;
|
||||
|
||||
/**
|
||||
* The toggle buttons to be added to the input box.
|
||||
*/
|
||||
toggles: IQuickInputToggle[] | undefined;
|
||||
|
||||
/**
|
||||
* Shows the quick input.
|
||||
*/
|
||||
@@ -694,11 +699,6 @@ export interface IQuickPick<T extends IQuickPickItem, O extends { useSeparators:
|
||||
*/
|
||||
hideCheckAll: boolean;
|
||||
|
||||
/**
|
||||
* The toggle buttons to be added to the input box.
|
||||
*/
|
||||
toggles: IQuickInputToggle[] | undefined;
|
||||
|
||||
/**
|
||||
* Focus a particular item in the list. Used internally for keyboard navigation.
|
||||
* @param focus The focus behavior.
|
||||
@@ -794,7 +794,13 @@ export enum QuickInputButtonLocation {
|
||||
/**
|
||||
* To the right of the input box.
|
||||
*/
|
||||
Inline = 2
|
||||
Inline = 2,
|
||||
|
||||
/**
|
||||
* At the far end inside the input box.
|
||||
* Used by the public API to create toggles.
|
||||
*/
|
||||
Input = 3,
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,26 +3,30 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IPickOptions, IInputOptions, IQuickInputService, IQuickInput, IQuickPick, IQuickPickItem } from '../../../platform/quickinput/common/quickInput.js';
|
||||
import { ExtHostContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, TransferQuickPickItem, MainContext, TransferQuickInput, TransferQuickInputButton, IInputBoxOptions, TransferQuickPickItemOrSeparator } from '../common/extHost.protocol.js';
|
||||
import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js';
|
||||
import { URI } from '../../../base/common/uri.js';
|
||||
import { Toggle } from '../../../base/browser/ui/toggle/toggle.js';
|
||||
import { CancellationToken } from '../../../base/common/cancellation.js';
|
||||
import { DisposableStore } from '../../../base/common/lifecycle.js';
|
||||
import { Lazy } from '../../../base/common/lazy.js';
|
||||
import { DisposableStore, IDisposable } from '../../../base/common/lifecycle.js';
|
||||
import { basenameOrAuthority, dirname } from '../../../base/common/resources.js';
|
||||
import { ThemeIcon } from '../../../base/common/themables.js';
|
||||
import { isUriComponents, URI } from '../../../base/common/uri.js';
|
||||
import { ILanguageService } from '../../../editor/common/languages/language.js';
|
||||
import { getIconClasses } from '../../../editor/common/services/getIconClasses.js';
|
||||
import { IModelService } from '../../../editor/common/services/model.js';
|
||||
import { ILabelService } from '../../../platform/label/common/label.js';
|
||||
import { IInputOptions, IPickOptions, IQuickInput, IQuickInputService, IQuickPick, IQuickPickItem, QuickInputButtonLocation } from '../../../platform/quickinput/common/quickInput.js';
|
||||
import { asCssVariable, inputActiveOptionBackground, inputActiveOptionBorder, inputActiveOptionForeground } from '../../../platform/theme/common/colorRegistry.js';
|
||||
import { ICustomEditorLabelService } from '../../services/editor/common/customEditorLabelService.js';
|
||||
import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js';
|
||||
import { ExtHostContext, ExtHostQuickOpenShape, IInputBoxOptions, MainContext, MainThreadQuickOpenShape, TransferQuickInput, TransferQuickInputButton, TransferQuickPickItem, TransferQuickPickItemOrSeparator } from '../common/extHost.protocol.js';
|
||||
|
||||
interface QuickInputSession {
|
||||
input: IQuickInput;
|
||||
handlesToItems: Map<number, TransferQuickPickItem>;
|
||||
handlesToToggles: Map<number, { toggle: Toggle; listener: IDisposable }>;
|
||||
store: DisposableStore;
|
||||
}
|
||||
|
||||
function reviveIconPathUris(iconPath: { dark: URI; light?: URI | undefined }) {
|
||||
iconPath.dark = URI.revive(iconPath.dark);
|
||||
if (iconPath.light) {
|
||||
iconPath.light = URI.revive(iconPath.light);
|
||||
}
|
||||
}
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadQuickOpen)
|
||||
export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
|
||||
|
||||
@@ -35,7 +39,11 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@IQuickInputService quickInputService: IQuickInputService
|
||||
@IQuickInputService quickInputService: IQuickInputService,
|
||||
@ILabelService private readonly labelService: ILabelService,
|
||||
@ICustomEditorLabelService private readonly customEditorLabelService: ICustomEditorLabelService,
|
||||
@IModelService private readonly modelService: IModelService,
|
||||
@ILanguageService private readonly languageService: ILanguageService
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostQuickOpen);
|
||||
this._quickInputService = quickInputService;
|
||||
@@ -80,6 +88,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
|
||||
|
||||
$setItems(instance: number, items: TransferQuickPickItemOrSeparator[]): Promise<void> {
|
||||
if (this._items[instance]) {
|
||||
items.forEach(item => this.expandItemProps(item));
|
||||
this._items[instance].resolve(items);
|
||||
delete this._items[instance];
|
||||
}
|
||||
@@ -159,62 +168,79 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
|
||||
session = {
|
||||
input,
|
||||
handlesToItems: new Map(),
|
||||
handlesToToggles: new Map(),
|
||||
store
|
||||
};
|
||||
this.sessions.set(sessionId, session);
|
||||
}
|
||||
|
||||
const { input, handlesToItems } = session;
|
||||
const quickPick = input as IQuickPick<IQuickPickItem>;
|
||||
for (const param in params) {
|
||||
if (param === 'id' || param === 'type') {
|
||||
continue;
|
||||
}
|
||||
if (param === 'visible') {
|
||||
if (params.visible) {
|
||||
input.show();
|
||||
} else {
|
||||
input.hide();
|
||||
switch (param) {
|
||||
case 'id':
|
||||
case 'type':
|
||||
continue;
|
||||
|
||||
case 'visible':
|
||||
if (params.visible) {
|
||||
input.show();
|
||||
} else {
|
||||
input.hide();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'items': {
|
||||
handlesToItems.clear();
|
||||
params.items?.forEach((item: TransferQuickPickItemOrSeparator) => {
|
||||
this.expandItemProps(item);
|
||||
if (item.type !== 'separator') {
|
||||
item.buttons?.forEach(button => this.expandIconPath(button));
|
||||
handlesToItems.set(item.handle, item);
|
||||
}
|
||||
});
|
||||
quickPick.items = params.items;
|
||||
break;
|
||||
}
|
||||
} else if (param === 'items') {
|
||||
handlesToItems.clear();
|
||||
params[param].forEach((item: TransferQuickPickItemOrSeparator) => {
|
||||
if (item.type === 'separator') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.buttons) {
|
||||
item.buttons = item.buttons.map((button: TransferQuickInputButton) => {
|
||||
if (button.iconPath) {
|
||||
reviveIconPathUris(button.iconPath);
|
||||
case 'activeItems':
|
||||
quickPick.activeItems = params.activeItems
|
||||
?.map((handle: number) => handlesToItems.get(handle))
|
||||
.filter(Boolean);
|
||||
break;
|
||||
|
||||
case 'selectedItems':
|
||||
quickPick.selectedItems = params.selectedItems
|
||||
?.map((handle: number) => handlesToItems.get(handle))
|
||||
.filter(Boolean);
|
||||
break;
|
||||
|
||||
case 'buttons': {
|
||||
const buttons = [], toggles = [];
|
||||
for (const button of params.buttons!) {
|
||||
if (button.handle === -1) {
|
||||
buttons.push(this._quickInputService.backButton);
|
||||
} else {
|
||||
this.expandIconPath(button);
|
||||
|
||||
// Currently buttons are only supported outside of the input box
|
||||
// and toggles only inside. When/if that changes, this will need to be updated.
|
||||
if (button.location === QuickInputButtonLocation.Input) {
|
||||
toggles.push(button);
|
||||
} else {
|
||||
buttons.push(button);
|
||||
}
|
||||
|
||||
return button;
|
||||
});
|
||||
}
|
||||
handlesToItems.set(item.handle, item);
|
||||
});
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
(input as any)[param] = params[param];
|
||||
} else if (param === 'activeItems' || param === 'selectedItems') {
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
(input as any)[param] = params[param]
|
||||
.filter((handle: number) => handlesToItems.has(handle))
|
||||
.map((handle: number) => handlesToItems.get(handle));
|
||||
} else if (param === 'buttons') {
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
(input as any)[param] = params.buttons!.map(button => {
|
||||
if (button.handle === -1) {
|
||||
return this._quickInputService.backButton;
|
||||
}
|
||||
}
|
||||
input.buttons = buttons;
|
||||
this.updateToggles(sessionId, session, toggles);
|
||||
break;
|
||||
}
|
||||
|
||||
if (button.iconPath) {
|
||||
reviveIconPathUris(button.iconPath);
|
||||
}
|
||||
|
||||
return button;
|
||||
});
|
||||
} else {
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
(input as any)[param] = params[param];
|
||||
default:
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
(input as any)[param] = params[param];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Promise.resolve(undefined);
|
||||
@@ -228,4 +254,114 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
|
||||
}
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Derives icon, label and description for Quick Pick items that represent a resource URI.
|
||||
*/
|
||||
private expandItemProps(item: TransferQuickPickItemOrSeparator) {
|
||||
if (item.type === 'separator') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!item.resourceUri) {
|
||||
this.expandIconPath(item);
|
||||
return;
|
||||
}
|
||||
|
||||
// Derive missing label and description from resourceUri.
|
||||
const resourceUri = URI.from(item.resourceUri);
|
||||
item.label ??= this.customEditorLabelService.getName(resourceUri) || '';
|
||||
if (item.label) {
|
||||
item.description ??= this.labelService.getUriLabel(resourceUri, { relative: true });
|
||||
} else {
|
||||
item.label = basenameOrAuthority(resourceUri);
|
||||
item.description ??= this.labelService.getUriLabel(dirname(resourceUri), { relative: true });
|
||||
}
|
||||
|
||||
// Derive icon props from resourceUri if icon is set to ThemeIcon.File or ThemeIcon.Folder.
|
||||
const icon = item.iconPathDto;
|
||||
if (ThemeIcon.isThemeIcon(icon) && (ThemeIcon.isFile(icon) || ThemeIcon.isFolder(icon))) {
|
||||
const iconClasses = new Lazy(() => getIconClasses(this.modelService, this.languageService, resourceUri));
|
||||
Object.defineProperty(item, 'iconClasses', { get: () => iconClasses.value });
|
||||
} else {
|
||||
this.expandIconPath(item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts IconPath DTO into iconPath/iconClass properties.
|
||||
*/
|
||||
private expandIconPath(target: Pick<TransferQuickPickItem, 'iconPathDto' | 'iconPath' | 'iconClass'>) {
|
||||
const icon = target.iconPathDto;
|
||||
if (!icon) {
|
||||
return;
|
||||
} else if (ThemeIcon.isThemeIcon(icon)) {
|
||||
// TODO: Since IQuickPickItem and IQuickInputButton do not support ThemeIcon directly, the color ID is lost here.
|
||||
// We should consider changing changing iconPath/iconClass to IconPath in both interfaces.
|
||||
// Request for color support: https://github.com/microsoft/vscode/issues/185356..
|
||||
target.iconClass = ThemeIcon.asClassName(icon);
|
||||
} else if (isUriComponents(icon)) {
|
||||
const uri = URI.from(icon);
|
||||
target.iconPath = { dark: uri, light: uri };
|
||||
} else {
|
||||
const { dark, light } = icon;
|
||||
target.iconPath = { dark: URI.from(dark), light: URI.from(light) };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the toggles for a given quick input session by creating new {@link Toggle}-s
|
||||
* from buttons, updating existing toggles props and removing old ones.
|
||||
*/
|
||||
private updateToggles(sessionId: number, session: QuickInputSession, buttons: TransferQuickInputButton[]) {
|
||||
const { input, handlesToToggles, store } = session;
|
||||
|
||||
// Add new or update existing toggles.
|
||||
const toggles = [];
|
||||
for (const button of buttons) {
|
||||
const title = button.tooltip || '';
|
||||
const isChecked = !!button.checked;
|
||||
|
||||
// TODO: Toggle class only supports ThemeIcon at the moment, but not other formats of IconPath.
|
||||
// We should consider adding support for the full IconPath to Toggle, in this code should be updated.
|
||||
const icon = ThemeIcon.isThemeIcon(button.iconPathDto) ? button.iconPathDto : undefined;
|
||||
|
||||
let { toggle } = handlesToToggles.get(button.handle) || {};
|
||||
if (toggle) {
|
||||
// Toggle already exists, update its props.
|
||||
toggle.setTitle(title);
|
||||
toggle.setIcon(icon);
|
||||
toggle.checked = isChecked;
|
||||
} else {
|
||||
// Create a new toggle from the button.
|
||||
toggle = store.add(new Toggle({
|
||||
title,
|
||||
icon,
|
||||
isChecked,
|
||||
inputActiveOptionBorder: asCssVariable(inputActiveOptionBorder),
|
||||
inputActiveOptionForeground: asCssVariable(inputActiveOptionForeground),
|
||||
inputActiveOptionBackground: asCssVariable(inputActiveOptionBackground)
|
||||
}));
|
||||
|
||||
const listener = store.add(toggle.onChange(() => {
|
||||
this._proxy.$onDidTriggerButton(sessionId, button.handle, toggle!.checked);
|
||||
}));
|
||||
|
||||
handlesToToggles.set(button.handle, { toggle, listener });
|
||||
}
|
||||
toggles.push(toggle);
|
||||
}
|
||||
|
||||
// Remove toggles that are no longer present from the session map.
|
||||
for (const [handle, { toggle, listener }] of handlesToToggles) {
|
||||
if (!buttons.some(button => button.handle === handle)) {
|
||||
handlesToToggles.delete(handle);
|
||||
store.delete(toggle);
|
||||
store.delete(listener);
|
||||
}
|
||||
}
|
||||
|
||||
// Update toggle interfaces on the input widget.
|
||||
input.toggles = toggles;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +98,11 @@ import { IExtHostDocumentSaveDelegate } from './extHostDocumentData.js';
|
||||
import { TerminalShellExecutionCommandLineConfidence } from './extHostTypes.js';
|
||||
import * as tasks from './shared/tasks.js';
|
||||
|
||||
export type IconPathDto =
|
||||
| UriComponents
|
||||
| { light: UriComponents; dark: UriComponents }
|
||||
| ThemeIcon;
|
||||
|
||||
export interface IWorkspaceData extends IStaticWorkspaceData {
|
||||
folders: { uri: UriComponents; name: string; index: number }[];
|
||||
}
|
||||
@@ -614,17 +619,29 @@ export interface TransferQuickPickItem {
|
||||
// shared properties from IQuickPickItem
|
||||
type?: 'item';
|
||||
label: string;
|
||||
iconPath?: { light?: URI; dark: URI };
|
||||
iconClass?: string;
|
||||
iconPathDto?: IconPathDto;
|
||||
description?: string;
|
||||
detail?: string;
|
||||
picked?: boolean;
|
||||
alwaysShow?: boolean;
|
||||
buttons?: TransferQuickInputButton[];
|
||||
resourceUri?: UriComponents;
|
||||
|
||||
// TODO: These properties are not used for transfer (iconPathDto is used instead) but they cannot be removed
|
||||
// because this type is used as IQuickPickItem on the main thread. Ideally IQuickPickItem should also use IconPath.
|
||||
iconPath?: { light?: URI; dark: URI };
|
||||
iconClass?: string;
|
||||
}
|
||||
|
||||
export interface TransferQuickInputButton extends quickInput.IQuickInputButton {
|
||||
handle: number;
|
||||
iconPathDto: IconPathDto;
|
||||
checked?: boolean;
|
||||
|
||||
// TODO: These properties are not used for transfer (iconPathDto is used instead) but they cannot be removed
|
||||
// because this type is used as IQuickInputButton on the main thread. Ideally IQuickInputButton should also use IconPath.
|
||||
iconPath?: { light?: URI; dark: URI };
|
||||
iconClass?: string;
|
||||
}
|
||||
|
||||
export type TransferQuickInput = TransferQuickPick | TransferInputBox;
|
||||
@@ -1635,7 +1652,7 @@ export interface SCMHistoryItemRefDto {
|
||||
readonly revision?: string;
|
||||
readonly category?: string;
|
||||
readonly description?: string;
|
||||
readonly icon?: UriComponents | { light: UriComponents; dark: UriComponents } | ThemeIcon;
|
||||
readonly icon?: IconPathDto;
|
||||
}
|
||||
|
||||
export interface SCMHistoryItemRefsChangeEventDto {
|
||||
@@ -1652,7 +1669,7 @@ export interface SCMHistoryItemDto {
|
||||
readonly message: string;
|
||||
readonly displayId?: string;
|
||||
readonly author?: string;
|
||||
readonly authorIcon?: UriComponents | { light: UriComponents; dark: UriComponents } | ThemeIcon;
|
||||
readonly authorIcon?: IconPathDto;
|
||||
readonly authorEmail?: string;
|
||||
readonly timestamp?: number;
|
||||
readonly statistics?: {
|
||||
@@ -1671,7 +1688,7 @@ export interface SCMHistoryItemChangeDto {
|
||||
}
|
||||
|
||||
export interface MainThreadSCMShape extends IDisposable {
|
||||
$registerSourceControl(handle: number, parentHandle: number | undefined, id: string, label: string, rootUri: UriComponents | undefined, iconPath: UriComponents | { light: UriComponents; dark: UriComponents } | ThemeIcon | undefined, inputBoxDocumentUri: UriComponents): Promise<void>;
|
||||
$registerSourceControl(handle: number, parentHandle: number | undefined, id: string, label: string, rootUri: UriComponents | undefined, iconPath: IconPathDto | undefined, inputBoxDocumentUri: UriComponents): Promise<void>;
|
||||
$updateSourceControl(handle: number, features: SCMProviderFeatures): Promise<void>;
|
||||
$unregisterSourceControl(handle: number): Promise<void>;
|
||||
|
||||
@@ -2198,7 +2215,7 @@ export interface IWorkspaceEditEntryMetadataDto {
|
||||
needsConfirmation: boolean;
|
||||
label: string;
|
||||
description?: string;
|
||||
iconPath?: { id: string } | UriComponents | { light: UriComponents; dark: UriComponents };
|
||||
iconPath?: IconPathDto;
|
||||
}
|
||||
|
||||
export interface IChatNotebookEditDto {
|
||||
@@ -2423,7 +2440,7 @@ export interface ExtHostQuickOpenShape {
|
||||
$onDidChangeSelection(sessionId: number, handles: number[]): void;
|
||||
$onDidAccept(sessionId: number): void;
|
||||
$onDidChangeValue(sessionId: number, value: string): void;
|
||||
$onDidTriggerButton(sessionId: number, handle: number): void;
|
||||
$onDidTriggerButton(sessionId: number, handle: number, checked?: boolean): void;
|
||||
$onDidTriggerItemButton(sessionId: number, itemHandle: number, buttonHandle: number): void;
|
||||
$onDidHide(sessionId: number): void;
|
||||
}
|
||||
|
||||
@@ -10,15 +10,13 @@ import { ExtHostCommands } from './extHostCommands.js';
|
||||
import { IExtHostWorkspaceProvider } from './extHostWorkspace.js';
|
||||
import { InputBox, InputBoxOptions, InputBoxValidationMessage, QuickInput, QuickInputButton, QuickPick, QuickPickItem, QuickPickItemButtonEvent, QuickPickOptions, WorkspaceFolder, WorkspaceFolderPickOptions } from 'vscode';
|
||||
import { ExtHostQuickOpenShape, IMainContext, MainContext, TransferQuickInput, TransferQuickInputButton, TransferQuickPickItemOrSeparator } from './extHost.protocol.js';
|
||||
import { URI } from '../../../base/common/uri.js';
|
||||
import { ThemeIcon, QuickInputButtons, QuickPickItemKind, InputBoxValidationSeverity } from './extHostTypes.js';
|
||||
import { QuickInputButtons, QuickPickItemKind, InputBoxValidationSeverity } from './extHostTypes.js';
|
||||
import { isCancellationError } from '../../../base/common/errors.js';
|
||||
import { IExtensionDescription } from '../../../platform/extensions/common/extensions.js';
|
||||
import { coalesce } from '../../../base/common/arrays.js';
|
||||
import Severity from '../../../base/common/severity.js';
|
||||
import { ThemeIcon as ThemeIconUtils } from '../../../base/common/themables.js';
|
||||
import { checkProposedApiEnabled, isProposedApiEnabled } from '../../services/extensions/common/extensions.js';
|
||||
import { MarkdownString } from './extHostTypeConverters.js';
|
||||
import { checkProposedApiEnabled } from '../../services/extensions/common/extensions.js';
|
||||
import { IconPath, MarkdownString } from './extHostTypeConverters.js';
|
||||
|
||||
export type Item = string | QuickPickItem;
|
||||
|
||||
@@ -90,8 +88,6 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const allowedTooltips = isProposedApiEnabled(extension, 'quickPickItemTooltip');
|
||||
|
||||
return itemsPromise.then(items => {
|
||||
|
||||
const pickItems: TransferQuickPickItemOrSeparator[] = [];
|
||||
@@ -102,20 +98,23 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx
|
||||
} else if (item.kind === QuickPickItemKind.Separator) {
|
||||
pickItems.push({ type: 'separator', label: item.label });
|
||||
} else {
|
||||
if (item.tooltip && !allowedTooltips) {
|
||||
console.warn(`Extension '${extension.identifier.value}' uses a tooltip which is proposed API that is only available when running out of dev or with the following command line switch: --enable-proposed-api ${extension.identifier.value}`);
|
||||
if (item.tooltip) {
|
||||
checkProposedApiEnabled(extension, 'quickPickItemTooltip');
|
||||
}
|
||||
|
||||
if (item.resourceUri) {
|
||||
checkProposedApiEnabled(extension, 'quickPickItemResource');
|
||||
}
|
||||
|
||||
const icon = (item.iconPath) ? getIconPathOrClass(item.iconPath) : undefined;
|
||||
pickItems.push({
|
||||
label: item.label,
|
||||
iconPath: icon?.iconPath,
|
||||
iconClass: icon?.iconClass,
|
||||
iconPathDto: IconPath.from(item.iconPath),
|
||||
description: item.description,
|
||||
detail: item.detail,
|
||||
picked: item.picked,
|
||||
alwaysShow: item.alwaysShow,
|
||||
tooltip: allowedTooltips ? MarkdownString.fromStrict(item.tooltip) : undefined,
|
||||
tooltip: MarkdownString.fromStrict(item.tooltip),
|
||||
resourceUri: item.resourceUri,
|
||||
handle
|
||||
});
|
||||
}
|
||||
@@ -256,9 +255,9 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx
|
||||
}
|
||||
}
|
||||
|
||||
$onDidTriggerButton(sessionId: number, handle: number): void {
|
||||
$onDidTriggerButton(sessionId: number, handle: number, checked?: boolean): void {
|
||||
const session = this._sessions.get(sessionId);
|
||||
session?._fireDidTriggerButton(handle);
|
||||
session?._fireDidTriggerButton(handle, checked);
|
||||
}
|
||||
|
||||
$onDidTriggerItemButton(sessionId: number, itemHandle: number, buttonHandle: number): void {
|
||||
@@ -400,9 +399,8 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx
|
||||
}
|
||||
|
||||
set buttons(buttons: QuickInputButton[]) {
|
||||
const allowedButtonLocation = isProposedApiEnabled(this._extension, 'quickInputButtonLocation');
|
||||
if (!allowedButtonLocation && buttons.some(button => button.location)) {
|
||||
console.warn(`Extension '${this._extension.identifier.value}' uses a button location which is proposed API that is only available when running out of dev or with the following command line switch: --enable-proposed-api ${this._extension.identifier.value}`);
|
||||
if (buttons.some(button => button.location || button.checked !== undefined)) {
|
||||
checkProposedApiEnabled(this._extension, 'quickInputButtonLocation');
|
||||
}
|
||||
this._buttons = buttons.slice();
|
||||
this._handlesToButtons.clear();
|
||||
@@ -413,10 +411,11 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx
|
||||
this.update({
|
||||
buttons: buttons.map<TransferQuickInputButton>((button, i) => {
|
||||
return {
|
||||
...getIconPathOrClass(button.iconPath),
|
||||
iconPathDto: IconPath.from(button.iconPath),
|
||||
tooltip: button.tooltip,
|
||||
handle: button === QuickInputButtons.Back ? -1 : i,
|
||||
location: allowedButtonLocation ? button.location : undefined
|
||||
location: button.location,
|
||||
checked: button.checked
|
||||
};
|
||||
})
|
||||
});
|
||||
@@ -446,9 +445,12 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx
|
||||
this._onDidChangeValueEmitter.fire(value);
|
||||
}
|
||||
|
||||
_fireDidTriggerButton(handle: number) {
|
||||
_fireDidTriggerButton(handle: number, checked?: boolean) {
|
||||
const button = this._handlesToButtons.get(handle);
|
||||
if (button) {
|
||||
if (checked !== undefined) {
|
||||
button.checked = checked;
|
||||
}
|
||||
this._onDidTriggerButtonEmitter.fire(button);
|
||||
}
|
||||
}
|
||||
@@ -499,7 +501,7 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx
|
||||
}
|
||||
this.dispatchUpdate();
|
||||
} else if (this._visible && !this._updateTimeout) {
|
||||
// Defer the update so that multiple changes to setters dont cause a redraw each
|
||||
// Defer the update so that multiple changes to setters don't cause a redraw each
|
||||
this._updateTimeout = setTimeout(() => {
|
||||
this._updateTimeout = undefined;
|
||||
this.dispatchUpdate();
|
||||
@@ -513,43 +515,6 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx
|
||||
}
|
||||
}
|
||||
|
||||
function getIconUris(iconPath: QuickInputButton['iconPath']): { dark: URI; light?: URI } | { id: string } {
|
||||
if (iconPath instanceof ThemeIcon) {
|
||||
return { id: iconPath.id };
|
||||
}
|
||||
const dark = getDarkIconUri(iconPath as URI | { light: URI; dark: URI });
|
||||
const light = getLightIconUri(iconPath as URI | { light: URI; dark: URI });
|
||||
// Tolerate strings: https://github.com/microsoft/vscode/issues/110432#issuecomment-726144556
|
||||
return {
|
||||
dark: typeof dark === 'string' ? URI.file(dark) : dark,
|
||||
light: typeof light === 'string' ? URI.file(light) : light
|
||||
};
|
||||
}
|
||||
|
||||
function getLightIconUri(iconPath: URI | { light: URI; dark: URI }) {
|
||||
return typeof iconPath === 'object' && 'light' in iconPath ? iconPath.light : iconPath;
|
||||
}
|
||||
|
||||
function getDarkIconUri(iconPath: URI | { light: URI; dark: URI }) {
|
||||
return typeof iconPath === 'object' && 'dark' in iconPath ? iconPath.dark : iconPath;
|
||||
}
|
||||
|
||||
function getIconPathOrClass(icon: QuickInputButton['iconPath']) {
|
||||
const iconPathOrIconClass = getIconUris(icon);
|
||||
let iconPath: { dark: URI; light?: URI | undefined } | undefined;
|
||||
let iconClass: string | undefined;
|
||||
if ('id' in iconPathOrIconClass) {
|
||||
iconClass = ThemeIconUtils.asClassName(iconPathOrIconClass);
|
||||
} else {
|
||||
iconPath = iconPathOrIconClass;
|
||||
}
|
||||
|
||||
return {
|
||||
iconPath,
|
||||
iconClass
|
||||
};
|
||||
}
|
||||
|
||||
class ExtHostQuickPick<T extends QuickPickItem> extends ExtHostQuickInput implements QuickPick<T> {
|
||||
|
||||
private _items: T[] = [];
|
||||
@@ -590,32 +555,33 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx
|
||||
this._itemsToHandles.set(item, i);
|
||||
});
|
||||
|
||||
const allowedTooltips = isProposedApiEnabled(this._extension, 'quickPickItemTooltip');
|
||||
|
||||
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 {
|
||||
if (item.tooltip && !allowedTooltips) {
|
||||
console.warn(`Extension '${this._extension.identifier.value}' uses a tooltip which is proposed API that is only available when running out of dev or with the following command line switch: --enable-proposed-api ${this._extension.identifier.value}`);
|
||||
if (item.tooltip) {
|
||||
checkProposedApiEnabled(this._extension, 'quickPickItemTooltip');
|
||||
}
|
||||
|
||||
if (item.resourceUri) {
|
||||
checkProposedApiEnabled(this._extension, 'quickPickItemResource');
|
||||
}
|
||||
|
||||
const icon = (item.iconPath) ? getIconPathOrClass(item.iconPath) : undefined;
|
||||
pickItems.push({
|
||||
handle,
|
||||
label: item.label,
|
||||
iconPath: icon?.iconPath,
|
||||
iconClass: icon?.iconClass,
|
||||
iconPathDto: IconPath.from(item.iconPath),
|
||||
description: item.description,
|
||||
detail: item.detail,
|
||||
picked: item.picked,
|
||||
alwaysShow: item.alwaysShow,
|
||||
tooltip: allowedTooltips ? MarkdownString.fromStrict(item.tooltip) : undefined,
|
||||
tooltip: MarkdownString.fromStrict(item.tooltip),
|
||||
resourceUri: item.resourceUri,
|
||||
buttons: item.buttons?.map<TransferQuickInputButton>((button, i) => {
|
||||
return {
|
||||
...getIconPathOrClass(button.iconPath),
|
||||
iconPathDto: IconPath.from(button.iconPath),
|
||||
tooltip: button.tooltip,
|
||||
handle: i
|
||||
};
|
||||
|
||||
@@ -3732,6 +3732,56 @@ export namespace IconPath {
|
||||
export function fromThemeIcon(iconPath: vscode.ThemeIcon): languages.IconPath {
|
||||
return iconPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a {@link vscode.IconPath} to an {@link extHostProtocol.IconPathDto}.
|
||||
* @note This function will tolerate strings specified instead of URIs in IconPath for historical reasons.
|
||||
* Such strings are treated as file paths and converted using {@link URI.file} function, not {@link URI.from}.
|
||||
* See https://github.com/microsoft/vscode/issues/110432#issuecomment-726144556 for context.
|
||||
*/
|
||||
export function from(value: undefined): undefined;
|
||||
export function from(value: vscode.IconPath): extHostProtocol.IconPathDto;
|
||||
export function from(value: vscode.IconPath | undefined): extHostProtocol.IconPathDto | undefined;
|
||||
export function from(value: vscode.IconPath | undefined): extHostProtocol.IconPathDto | undefined {
|
||||
if (!value) {
|
||||
return undefined;
|
||||
} else if (ThemeIcon.isThemeIcon(value)) {
|
||||
return value;
|
||||
} else if (URI.isUri(value)) {
|
||||
return value;
|
||||
} else if (typeof value === 'string') {
|
||||
return URI.file(value);
|
||||
} else if (typeof value === 'object' && value !== null && 'dark' in value) {
|
||||
const dark = typeof value.dark === 'string' ? URI.file(value.dark) : value.dark;
|
||||
const light = typeof value.light === 'string' ? URI.file(value.light) : value.light;
|
||||
return !dark ? undefined : { dark, light: light ?? dark };
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a {@link extHostProtocol.IconPathDto} to a {@link vscode.IconPath}.
|
||||
* @note This is a strict conversion and we assume types are correct in this case.
|
||||
*/
|
||||
export function to(value: undefined): undefined;
|
||||
export function to(value: extHostProtocol.IconPathDto): vscode.IconPath;
|
||||
export function to(value: extHostProtocol.IconPathDto | undefined): vscode.IconPath | undefined;
|
||||
export function to(value: extHostProtocol.IconPathDto | undefined): vscode.IconPath | undefined {
|
||||
if (!value) {
|
||||
return undefined;
|
||||
} else if (ThemeIcon.isThemeIcon(value)) {
|
||||
return value;
|
||||
} else if (isUriComponents(value)) {
|
||||
return URI.revive(value);
|
||||
} else {
|
||||
const icon = value as { light: UriComponents; dark: UriComponents };
|
||||
return {
|
||||
light: URI.revive(icon.light),
|
||||
dark: URI.revive(icon.dark)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export namespace AiSettingsSearch {
|
||||
|
||||
@@ -2544,7 +2544,8 @@ export class DebugVisualization {
|
||||
|
||||
export enum QuickInputButtonLocation {
|
||||
Title = 1,
|
||||
Inline = 2
|
||||
Inline = 2,
|
||||
Input = 3
|
||||
}
|
||||
|
||||
@es5ClassCompat
|
||||
|
||||
123
src/vs/workbench/api/test/common/extHostTypeConverters.test.ts
Normal file
123
src/vs/workbench/api/test/common/extHostTypeConverters.test.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import assert from 'assert';
|
||||
import { URI, UriComponents } from '../../../../base/common/uri.js';
|
||||
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
|
||||
import { IconPathDto } from '../../common/extHost.protocol.js';
|
||||
import { IconPath } from '../../common/extHostTypeConverters.js';
|
||||
import { ThemeColor, ThemeIcon } from '../../common/extHostTypes.js';
|
||||
|
||||
suite('extHostTypeConverters', function () {
|
||||
ensureNoDisposablesAreLeakedInTestSuite();
|
||||
|
||||
suite('IconPath', function () {
|
||||
suite('from', function () {
|
||||
test('undefined', function () {
|
||||
assert.strictEqual(IconPath.from(undefined), undefined);
|
||||
});
|
||||
|
||||
test('ThemeIcon', function () {
|
||||
const themeIcon = new ThemeIcon('account', new ThemeColor('testing.iconForeground'));
|
||||
assert.strictEqual(IconPath.from(themeIcon), themeIcon);
|
||||
});
|
||||
|
||||
test('URI', function () {
|
||||
const uri = URI.parse('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==');
|
||||
assert.strictEqual(IconPath.from(uri), uri);
|
||||
});
|
||||
|
||||
test('string', function () {
|
||||
const str = '/path/to/icon.png';
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
const r1 = IconPath.from(str as any) as any as URI;
|
||||
assert.ok(URI.isUri(r1));
|
||||
assert.strictEqual(r1.scheme, 'file');
|
||||
assert.strictEqual(r1.path, str);
|
||||
});
|
||||
|
||||
test('dark only', function () {
|
||||
const input = { dark: URI.file('/path/to/dark.png') };
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
const result = IconPath.from(input as any) as unknown as { dark: URI; light: URI };
|
||||
assert.strictEqual(typeof result, 'object');
|
||||
assert.ok('light' in result && 'dark' in result);
|
||||
assert.ok(URI.isUri(result.light));
|
||||
assert.ok(URI.isUri(result.dark));
|
||||
assert.strictEqual(result.dark.toString(), input.dark.toString());
|
||||
assert.strictEqual(result.light.toString(), input.dark.toString());
|
||||
});
|
||||
|
||||
test('dark/light', function () {
|
||||
const input = { light: URI.file('/path/to/light.png'), dark: URI.file('/path/to/dark.png') };
|
||||
const result = IconPath.from(input);
|
||||
assert.strictEqual(typeof result, 'object');
|
||||
assert.ok('light' in result && 'dark' in result);
|
||||
assert.ok(URI.isUri(result.light));
|
||||
assert.ok(URI.isUri(result.dark));
|
||||
assert.strictEqual(result.dark.toString(), input.dark.toString());
|
||||
assert.strictEqual(result.light.toString(), input.light.toString());
|
||||
});
|
||||
|
||||
test('dark/light strings', function () {
|
||||
const input = { light: '/path/to/light.png', dark: '/path/to/dark.png' };
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
const result = IconPath.from(input as any) as unknown as IconPathDto;
|
||||
assert.strictEqual(typeof result, 'object');
|
||||
assert.ok('light' in result && 'dark' in result);
|
||||
assert.ok(URI.isUri(result.light));
|
||||
assert.ok(URI.isUri(result.dark));
|
||||
assert.strictEqual(result.dark.path, input.dark);
|
||||
assert.strictEqual(result.light.path, input.light);
|
||||
});
|
||||
|
||||
test('invalid object', function () {
|
||||
const invalidObject = { foo: 'bar' };
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
const result = IconPath.from(invalidObject as any);
|
||||
assert.strictEqual(result, undefined);
|
||||
});
|
||||
|
||||
test('light only', function () {
|
||||
const input = { light: URI.file('/path/to/light.png') };
|
||||
// eslint-disable-next-line local/code-no-any-casts
|
||||
const result = IconPath.from(input as any);
|
||||
assert.strictEqual(result, undefined);
|
||||
});
|
||||
});
|
||||
|
||||
suite('to', function () {
|
||||
test('undefined', function () {
|
||||
assert.strictEqual(IconPath.to(undefined), undefined);
|
||||
});
|
||||
|
||||
test('ThemeIcon', function () {
|
||||
const themeIcon = new ThemeIcon('account');
|
||||
assert.strictEqual(IconPath.to(themeIcon), themeIcon);
|
||||
});
|
||||
|
||||
test('URI', function () {
|
||||
const uri: UriComponents = { scheme: 'data', path: 'image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==' };
|
||||
const result = IconPath.to(uri);
|
||||
assert.ok(URI.isUri(result));
|
||||
assert.strictEqual(result.toString(), URI.revive(uri).toString());
|
||||
});
|
||||
|
||||
test('dark/light', function () {
|
||||
const input: { light: UriComponents; dark: UriComponents } = {
|
||||
light: { scheme: 'file', path: '/path/to/light.png' },
|
||||
dark: { scheme: 'file', path: '/path/to/dark.png' }
|
||||
};
|
||||
const result = IconPath.to(input);
|
||||
assert.strictEqual(typeof result, 'object');
|
||||
assert.ok('light' in result && 'dark' in result);
|
||||
assert.ok(URI.isUri(result.light));
|
||||
assert.ok(URI.isUri(result.dark));
|
||||
assert.strictEqual(result.dark.toString(), URI.revive(input.dark).toString());
|
||||
assert.strictEqual(result.light.toString(), URI.revive(input.light).toString());
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1475,16 +1475,8 @@ class TreeRenderer extends Disposable implements ITreeRenderer<ITreeItem, FuzzyS
|
||||
return !(hasResource && this.isFileKindThemeIcon(icon));
|
||||
}
|
||||
|
||||
private isFolderThemeIcon(icon: ThemeIcon | undefined): boolean {
|
||||
return icon?.id === FolderThemeIcon.id;
|
||||
}
|
||||
|
||||
private isFileKindThemeIcon(icon: ThemeIcon | undefined): boolean {
|
||||
if (icon) {
|
||||
return icon.id === FileThemeIcon.id || this.isFolderThemeIcon(icon);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return ThemeIcon.isFile(icon) || ThemeIcon.isFolder(icon);
|
||||
}
|
||||
|
||||
private getFileKind(node: ITreeItem): FileKind {
|
||||
|
||||
@@ -16,7 +16,12 @@ declare module 'vscode' {
|
||||
/**
|
||||
* To the right of the input box.
|
||||
*/
|
||||
Inline = 2
|
||||
Inline = 2,
|
||||
|
||||
/**
|
||||
* At the far end inside the input box.
|
||||
*/
|
||||
Input = 3
|
||||
}
|
||||
|
||||
export interface QuickInputButton {
|
||||
@@ -25,5 +30,12 @@ declare module 'vscode' {
|
||||
* @note This property is ignored if the button was added to a QuickPickItem.
|
||||
*/
|
||||
location?: QuickInputButtonLocation;
|
||||
|
||||
/**
|
||||
* Indicates whether the button is checked.
|
||||
* @note This property is currently only applicable to buttons with location {@link QuickInputButtonLocation.Input}.
|
||||
* It must be set for such buttons and will be updated when the button is toggled.
|
||||
*/
|
||||
checked?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
21
src/vscode-dts/vscode.proposed.quickPickItemResource.d.ts
vendored
Normal file
21
src/vscode-dts/vscode.proposed.quickPickItemResource.d.ts
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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/59826
|
||||
|
||||
export interface QuickPickItem {
|
||||
/**
|
||||
* The {@link Uri} of the resource representing this item.
|
||||
*
|
||||
* Will be used to derive the {@link label}, when it is not provided (falsy or empty).
|
||||
* Will be used to derive the {@link description}, when it is not provided (falsy or empty).
|
||||
* Will be used to derive the icon from current file icon theme, when {@link iconPath} has either
|
||||
* {@link ThemeIcon.File} or {@link ThemeIcon.Folder} value.
|
||||
*/
|
||||
resourceUri?: Uri;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user