chore(vscode): update to 1.56.0

This commit is contained in:
Akash Satheesan
2021-04-30 20:25:17 +05:30
1749 changed files with 88014 additions and 43316 deletions

View File

@@ -170,7 +170,7 @@ configurationExtPoint.setHandler((extensions, { added, removed }) => {
validateProperties(configuration, extension);
configuration.id = node.id || extension.description.identifier.value;
configuration.extensionInfo = { id: extension.description.identifier.value };
configuration.extensionInfo = { id: extension.description.identifier.value, restrictedConfigurations: extension.description.capabilities?.untrustedWorkspaces?.supported === 'limited' ? extension.description.capabilities?.untrustedWorkspaces.restrictedConfigurations : undefined };
configuration.title = configuration.title || extension.description.displayName || extension.description.identifier.value;
configurations.push(configuration);
return configurations;
@@ -181,10 +181,10 @@ configurationExtPoint.setHandler((extensions, { added, removed }) => {
for (let extension of added) {
const configurations: IConfigurationNode[] = [];
const value = <IConfigurationNode | IConfigurationNode[]>extension.value;
if (!Array.isArray(value)) {
configurations.push(...handleConfiguration(value, extension));
} else {
if (Array.isArray(value)) {
value.forEach(v => configurations.push(...handleConfiguration(v, extension)));
} else {
configurations.push(...handleConfiguration(value, extension));
}
extensionConfigurations.set(ExtensionIdentifier.toKey(extension.description.identifier), configurations);
addedConfigurations.push(...configurations);
@@ -213,7 +213,7 @@ function validateProperties(configuration: IConfigurationNode, extension: IExten
const propertyConfiguration = properties[key];
if (!isObject(propertyConfiguration)) {
delete properties[key];
extension.collector.error(nls.localize('invalid.property', "'configuration.property' must be an object"));
extension.collector.error(nls.localize('invalid.property', "configuration.properties property '{0}' must be an object", key));
continue;
}
if (propertyConfiguration.scope) {
@@ -320,7 +320,7 @@ jsonRegistry.registerSchema('vscode://schemas/workspaceConfig', {
'remoteAuthority': {
type: 'string',
doNotSuggest: true,
description: nls.localize('workspaceConfig.remoteAuthority', "The remote server where the workspace is located. Only used by unsaved remote workspaces."),
description: nls.localize('workspaceConfig.remoteAuthority', "The remote server where the workspace is located."),
}
},
errorMessage: nls.localize('unknownWorkspaceProperty', "Unknown workspace configuration property")

View File

@@ -47,7 +47,7 @@ import { ExtHostUrls } from 'vs/workbench/api/common/extHostUrls';
import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview';
import { IExtHostWindow } from 'vs/workbench/api/common/extHostWindow';
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { throwProposedApiError, checkProposedApiEnabled, checkRequiresWorkspaceTrust } from 'vs/workbench/services/extensions/common/extensions';
import { throwProposedApiError, checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
import type * as vscode from 'vscode';
@@ -84,6 +84,9 @@ import { ExtHostUriOpeners } from 'vs/workbench/api/common/extHostUriOpener';
import { IExtHostSecretState } from 'vs/workbench/api/common/exHostSecretState';
import { ExtHostEditorTabs } from 'vs/workbench/api/common/extHostEditorTabs';
import { IExtHostTelemetry } from 'vs/workbench/api/common/extHostTelemetry';
import { ExtHostNotebookKernels } from 'vs/workbench/api/common/extHostNotebookKernels';
import { RemoteTrustOption } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { TextSearchCompleteMessageType } from 'vs/workbench/services/search/common/searchExtTypes';
export interface IExtensionApiFactory {
(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode;
@@ -140,7 +143,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostDocuments = rpcProtocol.set(ExtHostContext.ExtHostDocuments, new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors));
const extHostDocumentContentProviders = rpcProtocol.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(rpcProtocol, extHostDocumentsAndEditors, extHostLogService));
const extHostDocumentSaveParticipant = rpcProtocol.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(extHostLogService, extHostDocuments, rpcProtocol.getProxy(MainContext.MainThreadBulkEdits)));
const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, extHostDocuments, initData.environment, extHostLogService, extensionStoragePaths));
const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, extHostDocuments, extHostLogService, extensionStoragePaths));
const extHostNotebookKernels = rpcProtocol.set(ExtHostContext.ExtHostNotebookKernels, new ExtHostNotebookKernels(rpcProtocol, initData, extHostNotebook));
const extHostEditors = rpcProtocol.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(rpcProtocol, extHostDocumentsAndEditors));
const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands, extHostLogService));
const extHostEditorInsets = rpcProtocol.set(ExtHostContext.ExtHostEditorInsets, new ExtHostEditorInsets(rpcProtocol.getProxy(MainContext.MainThreadEditorInsets), extHostEditors, initData.environment));
@@ -333,9 +337,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
: extHostTypes.ExtensionKind.UI;
const test: typeof vscode.test = {
registerTestProvider(provider) {
registerTestController(provider) {
checkProposedApiEnabled(extension);
return extHostTesting.registerTestProvider(provider);
return extHostTesting.registerTestController(extension.identifier.value, provider);
},
createDocumentTestObserver(document) {
checkProposedApiEnabled(extension);
@@ -349,9 +353,12 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
checkProposedApiEnabled(extension);
return extHostTesting.runTests(provider);
},
publishTestResult(results, persist = true) {
createTestItem<T>(options: vscode.TestItemOptions, data?: T) {
return new extHostTypes.TestItemImpl(options.id, options.label, options.uri, data);
},
createTestRun(request, name, persist) {
checkProposedApiEnabled(extension);
return extHostTesting.publishExtensionProvidedResults(results, persist);
return extHostTesting.createTestRun(extension.identifier.value, request, name, persist);
},
get onDidChangeTestResults() {
checkProposedApiEnabled(extension);
@@ -363,6 +370,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
},
};
// todo@connor4312: backwards compatibility for a short period
(test as any).createTestRunTask = test.createTestRun;
// namespace: extensions
const extensions: typeof vscode.extensions = {
getExtension(extensionId: string): vscode.Extension<any> | undefined {
@@ -633,6 +643,12 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
if ('pty' in nameOrOptions) {
return extHostTerminalService.createExtensionTerminal(nameOrOptions);
}
if (nameOrOptions.message) {
checkProposedApiEnabled(extension);
}
if (nameOrOptions.icon) {
checkProposedApiEnabled(extension);
}
return extHostTerminalService.createTerminalFromOptions(nameOrOptions);
}
return extHostTerminalService.createTerminal(nameOrOptions, shellPath, shellArgs);
@@ -900,7 +916,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
checkProposedApiEnabled(extension);
return extHostTunnelService.onDidChangeTunnels(listener, thisArg, disposables);
},
registerPortAttributesProvider: (portSelector: { pid?: number, portRange?: [number, number] }, provider: vscode.PortAttributesProvider) => {
registerPortAttributesProvider: (portSelector: { pid?: number, portRange?: [number, number], commandMatcher?: RegExp }, provider: vscode.PortAttributesProvider) => {
checkProposedApiEnabled(extension);
return extHostTunnelService.registerPortsAttributesProvider(portSelector, provider);
},
@@ -908,18 +924,15 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
checkProposedApiEnabled(extension);
return extHostTimeline.registerTimelineProvider(scheme, provider, extension.identifier, extHostCommands.converter);
},
get trustState() {
checkProposedApiEnabled(extension);
checkRequiresWorkspaceTrust(extension);
return extHostWorkspace.trustState;
get isTrusted() {
return extHostWorkspace.trusted;
},
requireWorkspaceTrust: (options?: vscode.WorkspaceTrustRequestOptions) => {
requestWorkspaceTrust: (options?: vscode.WorkspaceTrustRequestOptions) => {
checkProposedApiEnabled(extension);
checkRequiresWorkspaceTrust(extension);
return extHostWorkspace.requireWorkspaceTrust(options);
return extHostWorkspace.requestWorkspaceTrust(options);
},
onDidChangeWorkspaceTrustState: (listener, thisArgs?, disposables?) => {
return extHostWorkspace.onDidChangeWorkspaceTrustState(listener, thisArgs, disposables);
onDidGrantWorkspaceTrust: (listener, thisArgs?, disposables?) => {
return extHostWorkspace.onDidGrantWorkspaceTrust(listener, thisArgs, disposables);
}
};
@@ -1045,26 +1058,19 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
},
get notebookDocuments(): vscode.NotebookDocument[] {
checkProposedApiEnabled(extension);
return extHostNotebook.notebookDocuments.map(d => d.notebookDocument);
},
get onDidChangeActiveNotebookKernel() {
checkProposedApiEnabled(extension);
return extHostNotebook.onDidChangeActiveNotebookKernel;
return extHostNotebook.notebookDocuments.map(d => d.apiNotebook);
},
registerNotebookSerializer(viewType, serializer, options) {
checkProposedApiEnabled(extension);
return extHostNotebook.registerNotebookSerializer(extension, viewType, serializer, options);
},
registerNotebookContentProvider: (viewType: string, provider: vscode.NotebookContentProvider, options?: {
transientOutputs: boolean;
transientMetadata: { [K in keyof vscode.NotebookCellMetadata]?: boolean }
}) => {
registerNotebookContentProvider: (viewType, provider, options) => {
checkProposedApiEnabled(extension);
return extHostNotebook.registerNotebookContentProvider(extension, viewType, provider, options);
},
registerNotebookKernelProvider: (selector: vscode.NotebookDocumentFilter, provider: vscode.NotebookKernelProvider) => {
registerNotebookCellStatusBarItemProvider: (selector: vscode.NotebookSelector, provider: vscode.NotebookCellStatusBarItemProvider) => {
checkProposedApiEnabled(extension);
return extHostNotebook.registerNotebookKernelProvider(extension, selector, provider);
return extHostNotebook.registerNotebookCellStatusBarItemProvider(extension, selector, provider);
},
createNotebookEditorDecorationType(options: vscode.NotebookDecorationRenderOptions): vscode.NotebookEditorDecorationType {
checkProposedApiEnabled(extension);
@@ -1094,13 +1100,13 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
checkProposedApiEnabled(extension);
return new ExtHostNotebookConcatDocument(extHostNotebook, extHostDocuments, notebook, selector);
},
createCellStatusBarItem(cell: vscode.NotebookCell, alignment?: vscode.NotebookCellStatusBarAlignment, priority?: number): vscode.NotebookCellStatusBarItem {
checkProposedApiEnabled(extension);
return extHostNotebook.createNotebookCellStatusBarItemInternal(cell, alignment, priority);
},
createNotebookCellExecutionTask(uri: vscode.Uri, index: number, kernelId: string): vscode.NotebookCellExecutionTask | undefined {
checkProposedApiEnabled(extension);
return extHostNotebook.createNotebookCellExecution(uri, index, kernelId);
},
createNotebookController(id, viewType, label, executeHandler, preloads) {
checkProposedApiEnabled(extension);
return extHostNotebookKernels.createNotebookController(extension, id, viewType, label, executeHandler, preloads);
}
};
@@ -1233,11 +1239,12 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
InlineHint: extHostTypes.InlineHint,
InlineHintKind: extHostTypes.InlineHintKind,
RemoteAuthorityResolverError: extHostTypes.RemoteAuthorityResolverError,
RemoteTrustOption: RemoteTrustOption,
ResolvedAuthority: extHostTypes.ResolvedAuthority,
SourceControlInputBoxValidationType: extHostTypes.SourceControlInputBoxValidationType,
ExtensionRuntime: extHostTypes.ExtensionRuntime,
TimelineItem: extHostTypes.TimelineItem,
NotebookCellRange: extHostTypes.NotebookCellRange,
NotebookRange: extHostTypes.NotebookRange,
NotebookCellKind: extHostTypes.NotebookCellKind,
NotebookCellExecutionState: extHostTypes.NotebookCellExecutionState,
NotebookDocumentMetadata: extHostTypes.NotebookDocumentMetadata,
@@ -1248,11 +1255,13 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
NotebookEditorRevealType: extHostTypes.NotebookEditorRevealType,
NotebookCellOutput: extHostTypes.NotebookCellOutput,
NotebookCellOutputItem: extHostTypes.NotebookCellOutputItem,
NotebookCellStatusBarItem: extHostTypes.NotebookCellStatusBarItem,
NotebookControllerAffinity: extHostTypes.NotebookControllerAffinity,
LinkedEditingRanges: extHostTypes.LinkedEditingRanges,
TestItem: extHostTypes.TestItem,
TestState: extHostTypes.TestState,
TestResult: extHostTypes.TestResult,
TestItemStatus: extHostTypes.TestItemStatus,
TestResultState: extHostTypes.TestResultState,
TestMessage: extHostTypes.TestMessage,
TextSearchCompleteMessageType: TextSearchCompleteMessageType,
TestMessageSeverity: extHostTypes.TestMessageSeverity,
WorkspaceTrustState: extHostTypes.WorkspaceTrustState
};

View File

@@ -41,7 +41,7 @@ import * as tasks from 'vs/workbench/api/common/shared/tasks';
import { IRevealOptions, ITreeItem } from 'vs/workbench/common/views';
import { IAdapterDescriptor, IConfig, IDebugSessionReplMode } from 'vs/workbench/contrib/debug/common/debug';
import { ITextQueryBuilderOptions } from 'vs/workbench/contrib/search/common/queryBuilder';
import { ActivationKind, ExtensionActivationError, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions';
import { ActivationKind, MissingExtensionDependency, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions';
import { createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId, IRPCProtocol } from 'vs/workbench/services/extensions/common/proxyIdentifier';
import * as search from 'vs/workbench/services/search/common/search';
import { EditorGroupColumn, SaveReason } from 'vs/workbench/common/editor';
@@ -50,18 +50,21 @@ import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
import { TunnelCreationOptions, TunnelProviderFeatures, TunnelOptions, ProvidedPortAttributes } from 'vs/platform/remote/common/tunnel';
import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, InternalTimelineOptions } from 'vs/workbench/contrib/timeline/common/timeline';
import { revive } from 'vs/base/common/marshalling';
import { NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEventDto, NotebookDataDto, IMainCellDto, INotebookDocumentFilter, TransientMetadata, INotebookCellStatusBarEntry, ICellRange, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter, IOutputDto, TransientOptions, IImmediateCellEditOperation } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEventDto, NotebookDataDto, IMainCellDto, TransientCellMetadata, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter, IOutputDto, TransientOptions, IImmediateCellEditOperation, INotebookCellStatusBarItem, TransientDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange';
import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
import { Dto } from 'vs/base/common/types';
import { DebugConfigurationProviderTriggerKind, WorkspaceTrustState } from 'vs/workbench/api/common/extHostTypes';
import { DebugConfigurationProviderTriggerKind, TestResultState } from 'vs/workbench/api/common/extHostTypes';
import { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility';
import { IExtensionIdWithVersion } from 'vs/platform/userDataSync/common/extensionsStorageSync';
import { InternalTestItem, ITestState, RunTestForProviderRequest, RunTestsRequest, TestIdWithSrc, TestsDiff, ISerializedTestResults } from 'vs/workbench/contrib/testing/common/testCollection';
import { InternalTestItem, RunTestForProviderRequest, RunTestsRequest, TestIdWithSrc, TestsDiff, ISerializedTestResults, ITestMessage, ITestItem, ITestRunTask, ExtensionRunTestsRequest } from 'vs/workbench/contrib/testing/common/testCollection';
import { CandidatePort } from 'vs/workbench/services/remote/common/remoteExplorerService';
import { WorkspaceTrustRequestOptions, WorkspaceTrustStateChangeEvent } from 'vs/platform/workspace/common/workspaceTrust';
import { WorkspaceTrustRequestOptions } from 'vs/platform/workspace/common/workspaceTrust';
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
import { IShellLaunchConfig, ITerminalDimensions, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal';
import { IShellLaunchConfig, IShellLaunchConfigDto, ITerminalDimensions, ITerminalEnvironment, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal';
import { ITerminalProfile } from 'vs/workbench/contrib/terminal/common/terminal';
import { NotebookSelector } from 'vs/workbench/contrib/notebook/common/notebookSelector';
import { InputValidationType } from 'vs/workbench/contrib/scm/common/scm';
export interface IEnvironment {
isExtensionDevelopmentDebug: boolean;
@@ -454,7 +457,9 @@ export interface TerminalLaunchConfig {
shellPath?: string;
shellArgs?: string[] | string;
cwd?: string | UriComponents;
env?: { [key: string]: string | null; };
env?: ITerminalEnvironment;
icon?: string;
initialText?: string;
waitOnExit?: boolean;
strictEnv?: boolean;
hideFromUser?: boolean;
@@ -671,10 +676,39 @@ export interface CustomTextEditorCapabilities {
readonly supportsMove?: boolean;
}
export const enum WebviewMessageArrayBufferViewType {
Int8Array = 1,
Uint8Array = 2,
Uint8ClampedArray = 3,
Int16Array = 4,
Uint16Array = 5,
Int32Array = 6,
Uint32Array = 7,
Float32Array = 8,
Float64Array = 9,
BigInt64Array = 10,
BigUint64Array = 11,
}
export interface WebviewMessageArrayBufferReference {
readonly $$vscode_array_buffer_reference$$: true,
readonly index: number;
/**
* Tracks if the reference is to a view instead of directly to an ArrayBuffer.
*/
readonly view?: {
readonly type: WebviewMessageArrayBufferViewType;
readonly byteLength: number;
readonly byteOffset: number;
};
}
export interface MainThreadWebviewsShape extends IDisposable {
$setHtml(handle: WebviewHandle, value: string): void;
$setOptions(handle: WebviewHandle, options: IWebviewOptions): void;
$postMessage(handle: WebviewHandle, value: any): Promise<boolean>
$postMessage(handle: WebviewHandle, value: any, ...buffers: VSBuffer[]): Promise<boolean>
}
export interface MainThreadWebviewPanelsShape extends IDisposable {
@@ -686,6 +720,7 @@ export interface MainThreadWebviewPanelsShape extends IDisposable {
title: string;
webviewOptions: IWebviewOptions;
panelOptions: IWebviewPanelOptions;
serializeBuffersForPostMessage: boolean;
},
showOptions: WebviewPanelShowOptions,
): void;
@@ -694,13 +729,13 @@ export interface MainThreadWebviewPanelsShape extends IDisposable {
$setTitle(handle: WebviewHandle, value: string): void;
$setIconPath(handle: WebviewHandle, value: { light: UriComponents, dark: UriComponents; } | undefined): void;
$registerSerializer(viewType: string): void;
$registerSerializer(viewType: string, options: { serializeBuffersForPostMessage: boolean }): void;
$unregisterSerializer(viewType: string): void;
}
export interface MainThreadCustomEditorsShape extends IDisposable {
$registerTextEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: IWebviewPanelOptions, capabilities: CustomTextEditorCapabilities): void;
$registerCustomEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: IWebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean): void;
$registerTextEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: IWebviewPanelOptions, capabilities: CustomTextEditorCapabilities, serializeBuffersForPostMessage: boolean): void;
$registerCustomEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: IWebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean, serializeBuffersForPostMessage: boolean): void;
$unregisterEditorProvider(viewType: string): void;
$onDidEdit(resource: UriComponents, viewType: string, editId: number, label: string | undefined): void;
@@ -708,7 +743,7 @@ export interface MainThreadCustomEditorsShape extends IDisposable {
}
export interface MainThreadWebviewViewsShape extends IDisposable {
$registerWebviewViewProvider(extension: WebviewExtensionDescription, viewType: string, options?: { retainContextWhenHidden?: boolean }): void;
$registerWebviewViewProvider(extension: WebviewExtensionDescription, viewType: string, options: { retainContextWhenHidden?: boolean, serializeBuffersForPostMessage: boolean }): void;
$unregisterWebviewViewProvider(viewType: string): void;
$setWebviewViewTitle(handle: WebviewHandle, value: string | undefined): void;
@@ -726,7 +761,7 @@ export interface WebviewPanelViewStateData {
}
export interface ExtHostWebviewsShape {
$onMessage(handle: WebviewHandle, message: any): void;
$onMessage(handle: WebviewHandle, jsonSerializedMessage: string, ...buffers: VSBuffer[]): void;
$onMissingCsp(handle: WebviewHandle, extensionId: string): void;
}
@@ -827,37 +862,69 @@ export interface INotebookDocumentShowOptions {
position?: EditorGroupColumn;
preserveFocus?: boolean;
pinned?: boolean;
selection?: ICellRange;
selections?: ICellRange[];
}
export type INotebookCellStatusBarEntryDto = Dto<INotebookCellStatusBarEntry>;
export type INotebookCellStatusBarEntryDto = Dto<INotebookCellStatusBarItem>;
export interface INotebookCellStatusBarListDto {
items: INotebookCellStatusBarEntryDto[];
cacheId: number;
}
export interface MainThreadNotebookShape extends IDisposable {
$registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, options: {
transientOutputs: boolean;
transientMetadata: TransientMetadata;
transientCellMetadata: TransientCellMetadata;
transientDocumentMetadata: TransientDocumentMetadata;
viewOptions?: { displayName: string; filenamePattern: (string | IRelativePattern | INotebookExclusiveDocumentFilter)[]; exclusive: boolean; };
}): Promise<void>;
$updateNotebookProviderOptions(viewType: string, options?: { transientOutputs: boolean; transientMetadata: TransientMetadata; }): Promise<void>;
$updateNotebookProviderOptions(viewType: string, options?: { transientOutputs: boolean; transientCellMetadata: TransientCellMetadata; transientDocumentMetadata: TransientDocumentMetadata; }): Promise<void>;
$unregisterNotebookProvider(viewType: string): Promise<void>;
$registerNotebookSerializer(handle: number, extension: NotebookExtensionDescription, viewType: string, options: TransientOptions): void;
$unregisterNotebookSerializer(handle: number): void;
$registerNotebookKernelProvider(extension: NotebookExtensionDescription, handle: number, documentFilter: INotebookDocumentFilter): Promise<void>;
$unregisterNotebookKernelProvider(handle: number): Promise<void>;
$onNotebookKernelChange(handle: number, uri: UriComponents | undefined): void;
$trySaveDocument(uri: UriComponents): Promise<boolean>;
$tryApplyEdits(viewType: string, resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[]): Promise<boolean>;
$applyEdits(resource: UriComponents, edits: IImmediateCellEditOperation[], computeUndoRedo?: boolean): Promise<void>;
$postMessage(editorId: string, forRendererId: string | undefined, value: any): Promise<boolean>;
$setStatusBarEntry(id: number, statusBarEntry: INotebookCellStatusBarEntryDto): Promise<void>;
$tryOpenDocument(uriComponents: UriComponents): Promise<UriComponents>;
$registerNotebookCellStatusBarItemProvider(handle: number, eventHandle: number | undefined, selector: NotebookSelector): Promise<void>;
$unregisterNotebookCellStatusBarItemProvider(handle: number, eventHandle: number | undefined): Promise<void>;
$emitCellStatusBarEvent(eventHandle: number): void;
}
export interface MainThreadNotebookEditorsShape extends IDisposable {
$tryShowNotebookDocument(uriComponents: UriComponents, viewType: string, options: INotebookDocumentShowOptions): Promise<string>;
$tryRevealRange(id: string, range: ICellRange, revealType: NotebookEditorRevealType): Promise<void>;
$registerNotebookEditorDecorationType(key: string, options: INotebookDecorationRenderOptions): void;
$removeNotebookEditorDecorationType(key: string): void;
$trySetDecorations(id: string, range: ICellRange, decorationKey: string): void;
$tryApplyEdits(editorId: string, modelVersionId: number, cellEdits: ICellEditOperation[]): Promise<boolean>
}
export interface MainThreadNotebookDocumentsShape extends IDisposable {
$tryOpenDocument(uriComponents: UriComponents): Promise<UriComponents>;
$trySaveDocument(uri: UriComponents): Promise<boolean>;
$applyEdits(resource: UriComponents, edits: IImmediateCellEditOperation[], computeUndoRedo?: boolean): Promise<void>;
}
export interface INotebookKernelDto2 {
id: string;
viewType: string;
extensionId: ExtensionIdentifier;
extensionLocation: UriComponents;
label: string;
detail?: string;
description?: string;
supportedLanguages?: string[];
supportsInterrupt?: boolean;
hasExecutionOrder?: boolean;
preloads?: { uri: UriComponents; provides: string[] }[];
}
export interface MainThreadNotebookKernelsShape extends IDisposable {
$postMessage(handle: number, editorId: string | undefined, message: any): Promise<boolean>;
$addKernel(handle: number, data: INotebookKernelDto2): Promise<void>;
$updateKernel(handle: number, data: Partial<INotebookKernelDto2>): void;
$removeKernel(handle: number): void;
$updateNotebookPriority(handle: number, uri: UriComponents, value: number | undefined): void;
}
export interface MainThreadUrlsShape extends IDisposable {
@@ -891,7 +958,7 @@ export interface MainThreadWorkspaceShape extends IDisposable {
$saveAll(includeUntitled?: boolean): Promise<boolean>;
$updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, workspaceFoldersToAdd: { uri: UriComponents, name?: string; }[]): Promise<void>;
$resolveProxy(url: string): Promise<string | undefined>;
$requireWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Promise<WorkspaceTrustState>;
$requestWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Promise<boolean | undefined>;
}
export interface IFileChangeDto {
@@ -945,7 +1012,7 @@ export interface MainThreadExtensionServiceShape extends IDisposable {
$activateExtension(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void>;
$onWillActivateExtension(extensionId: ExtensionIdentifier): Promise<void>;
$onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void;
$onExtensionActivationError(extensionId: ExtensionIdentifier, error: ExtensionActivationError): Promise<void>;
$onExtensionActivationError(extensionId: ExtensionIdentifier, error: SerializedError, missingExtensionDependency: MissingExtensionDependency | null): Promise<void>;
$onExtensionRuntimeError(extensionId: ExtensionIdentifier, error: SerializedError): void;
$setPerformanceMarks(marks: performance.PerformanceMark[]): Promise<void>;
}
@@ -999,6 +1066,8 @@ export interface MainThreadSCMShape extends IDisposable {
$setInputBoxValue(sourceControlHandle: number, value: string): void;
$setInputBoxPlaceholder(sourceControlHandle: number, placeholder: string): void;
$setInputBoxVisibility(sourceControlHandle: number, visible: boolean): void;
$setInputBoxFocus(sourceControlHandle: number): void;
$showValidationMessage(sourceControlHandle: number, message: string, type: InputValidationType): void;
$setValidationProviderIsEnabled(sourceControlHandle: number, enabled: boolean): void;
}
@@ -1059,6 +1128,7 @@ export enum CandidatePortSource {
export interface PortAttributesProviderSelector {
pid?: number;
portRange?: [number, number];
commandMatcher?: RegExp;
}
export interface MainThreadTunnelServiceShape extends IDisposable {
@@ -1167,10 +1237,10 @@ export interface ExtHostTreeViewsShape {
}
export interface ExtHostWorkspaceShape {
$initializeWorkspace(workspace: IWorkspaceData | null, trustState: WorkspaceTrustState): void;
$initializeWorkspace(workspace: IWorkspaceData | null, trusted: boolean): void;
$acceptWorkspaceData(workspace: IWorkspaceData | null): void;
$handleTextSearchResult(result: search.IRawFileMatch2, requestId: number): void;
$onDidChangeWorkspaceTrustState(state: WorkspaceTrustStateChangeEvent): void;
$onDidGrantWorkspaceTrust(): void;
}
export interface ExtHostFileSystemInfoShape {
@@ -1268,8 +1338,8 @@ export interface IWillRunFileOperationParticipation {
export interface ExtHostFileSystemEventServiceShape {
$onFileEvent(events: FileSystemEvents): void;
$onWillRunFileOperation(operation: files.FileOperation, files: SourceTargetPair[], timeout: number, token: CancellationToken): Promise<IWillRunFileOperationParticipation | undefined>;
$onDidRunFileOperation(operation: files.FileOperation, files: SourceTargetPair[]): void;
$onWillRunFileOperation(operation: files.FileOperation, files: readonly SourceTargetPair[], timeout: number, token: CancellationToken): Promise<IWillRunFileOperationParticipation | undefined>;
$onDidRunFileOperation(operation: files.FileOperation, files: readonly SourceTargetPair[]): void;
}
export interface ObjectIdentifier {
@@ -1609,15 +1679,6 @@ export interface ExtHostTelemetryShape {
$onDidChangeTelemetryEnabled(enabled: boolean): void;
}
export interface IShellLaunchConfigDto {
name?: string;
executable?: string;
args?: string[] | string;
cwd?: string | UriComponents;
env?: { [key: string]: string | null; };
hideFromUser?: boolean;
}
export interface IShellAndArgsDto {
shell: string;
args: string[] | string | undefined;
@@ -1656,9 +1717,7 @@ export interface ExtHostTerminalServiceShape {
$acceptProcessRequestInitialCwd(id: number): void;
$acceptProcessRequestCwd(id: number): void;
$acceptProcessRequestLatency(id: number): number;
$acceptWorkspacePermissionsChanged(isAllowed: boolean): void;
// TODO: Change quickLaunchOnly to "includeAutoDetected" or something similar
$getAvailableProfiles(quickLaunchOnly: boolean): Promise<ITerminalProfile[]>;
$getAvailableProfiles(configuredProfilesOnly: boolean): Promise<ITerminalProfile[]>;
$getDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto>;
$provideLinks(id: number, line: string): Promise<ITerminalLinkDto[]>;
$activateLink(id: number, linkId: number): void;
@@ -1857,34 +1916,47 @@ export interface INotebookKernelInfoDto2 {
description?: string;
detail?: string;
isPreferred?: boolean;
preloads?: UriComponents[];
preloads?: { uri: UriComponents; provides: string[] }[];
supportedLanguages?: string[]
implementsInterrupt?: boolean;
}
export interface ExtHostNotebookShape {
$resolveNotebookEditor(viewType: string, uri: UriComponents, editorId: string): Promise<void>;
$acceptNotebookActiveKernelChange(event: { uri: UriComponents, providerHandle: number | undefined, kernelFriendlyId: string | undefined }): void;
$provideNotebookKernels(handle: number, uri: UriComponents, token: CancellationToken): Promise<INotebookKernelInfoDto2[]>;
$resolveNotebookKernel(handle: number, editorId: string, uri: UriComponents, kernelId: string, token: CancellationToken): Promise<void>;
$executeNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellRanges: ICellRange[]): Promise<void>;
$cancelNotebookCellExecution(handle: number, uri: UriComponents, kernelId: string, cellRange: ICellRange[]): Promise<void>;
$onDidReceiveMessage(editorId: string, rendererId: string | undefined, message: unknown): void;
export interface ExtHostNotebookShape extends ExtHostNotebookDocumentsAndEditorsShape, ExtHostNotebookDocumentsShape, ExtHostNotebookEditorsShape {
$provideNotebookCellStatusBarItems(handle: number, uri: UriComponents, index: number, token: CancellationToken): Promise<INotebookCellStatusBarListDto | undefined>;
$releaseNotebookCellStatusBarItems(id: number): void;
$openNotebook(viewType: string, uri: UriComponents, backupId: string | undefined, untitledDocumentData: VSBuffer | undefined, token: CancellationToken): Promise<NotebookDataDto>;
$saveNotebook(viewType: string, uri: UriComponents, token: CancellationToken): Promise<boolean>;
$saveNotebookAs(viewType: string, uri: UriComponents, target: UriComponents, token: CancellationToken): Promise<boolean>;
$backupNotebook(viewType: string, uri: UriComponents, cancellation: CancellationToken): Promise<string>;
$dataToNotebook(handle: number, data: VSBuffer): Promise<NotebookDataDto>;
$notebookToData(handle: number, data: NotebookDataDto): Promise<VSBuffer>;
$dataToNotebook(handle: number, data: VSBuffer, token: CancellationToken): Promise<NotebookDataDto>;
$notebookToData(handle: number, data: NotebookDataDto, token: CancellationToken): Promise<VSBuffer>;
}
export interface ExtHostNotebookDocumentsAndEditorsShape {
$acceptDocumentAndEditorsDelta(delta: INotebookDocumentsAndEditorsDelta): void;
}
export interface ExtHostNotebookDocumentsShape {
$acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEventDto, isDirty: boolean): void;
$acceptDirtyStateChanged(uriComponents: UriComponents, isDirty: boolean): void;
$acceptModelSaved(uriComponents: UriComponents): void;
$acceptDocumentPropertiesChanged(uriComponents: UriComponents, data: INotebookDocumentPropertiesChangeData): void;
$acceptDocumentAndEditorsDelta(delta: INotebookDocumentsAndEditorsDelta): void;
}
export type INotebookEditorViewColumnInfo = Record<string, number>;
export interface ExtHostNotebookEditorsShape {
$acceptEditorPropertiesChanged(id: string, data: INotebookEditorPropertiesChangeData): void;
$acceptEditorViewColumns(data: INotebookEditorViewColumnInfo): void;
}
export interface ExtHostNotebookKernelsShape {
$acceptSelection(handle: number, uri: UriComponents, value: boolean): void;
$executeCells(handle: number, uri: UriComponents, handles: number[]): Promise<void>;
$cancelCells(handle: number, uri: UriComponents, handles: number[]): Promise<void>;
$acceptRendererMessage(handle: number, editorId: string, message: any): void;
}
export interface ExtHostStorageShape {
@@ -1927,14 +1999,39 @@ export interface ExtHostTestingShape {
}
export interface MainThreadTestingShape {
$registerTestProvider(id: string): void;
$unregisterTestProvider(id: string): void;
/** Registeres that there's a test controller with the given ID */
$registerTestController(id: string): void;
/** Diposes of the test controller with the given ID */
$unregisterTestController(id: string): void;
/** Requests tests from the given resource/uri, from the observer API. */
$subscribeToDiffs(resource: ExtHostTestingResource, uri: UriComponents): void;
/** Stops requesting tests from the given resource/uri, from the observer API. */
$unsubscribeFromDiffs(resource: ExtHostTestingResource, uri: UriComponents): void;
/** Publishes that new tests were available on the given source. */
$publishDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void;
$updateTestStateInRun(runId: string, testId: string, state: ITestState): void;
/** Request by an extension to run tests. */
$runTests(req: RunTestsRequest, token: CancellationToken): Promise<string>;
$publishExtensionProvidedResults(results: ISerializedTestResults, persist: boolean): void;
// --- test run handling:
/**
* Adds tests to the run. The tests are given in descending depth. The first
* item will be a previously-known test, or a test root.
*/
$addTestsToRun(runId: string, tests: ITestItem[]): void;
/** Updates the state of a test run in the given run. */
$updateTestStateInRun(runId: string, taskId: string, testId: string, state: TestResultState, duration?: number): void;
/** Appends a message to a test in the run. */
$appendTestMessageInRun(runId: string, taskId: string, testId: string, message: ITestMessage): void;
/** Appends raw output to the test run.. */
$appendOutputToRun(runId: string, taskId: string, output: VSBuffer): void;
/** Signals a task in a test run started. */
$startedTestRunTask(runId: string, task: ITestRunTask): void;
/** Signals a task in a test run ended. */
$finishedTestRunTask(runId: string, taskId: string): void;
/** Start a new extension-provided test run. */
$startedExtensionTestRun(req: ExtensionRunTestsRequest): void;
/** Signals that an extension-provided test run finished. */
$finishedExtensionTestRun(runId: string): void;
}
// --- proxy identifiers
@@ -1987,6 +2084,9 @@ export const MainContext = {
MainThreadWindow: createMainId<MainThreadWindowShape>('MainThreadWindow'),
MainThreadLabelService: createMainId<MainThreadLabelServiceShape>('MainThreadLabelService'),
MainThreadNotebook: createMainId<MainThreadNotebookShape>('MainThreadNotebook'),
MainThreadNotebookDocuments: createMainId<MainThreadNotebookDocumentsShape>('MainThreadNotebookDocumentsShape'),
MainThreadNotebookEditors: createMainId<MainThreadNotebookEditorsShape>('MainThreadNotebookEditorsShape'),
MainThreadNotebookKernels: createMainId<MainThreadNotebookKernelsShape>('MainThreadNotebookKernels'),
MainThreadTheming: createMainId<MainThreadThemingShape>('MainThreadTheming'),
MainThreadTunnelService: createMainId<MainThreadTunnelServiceShape>('MainThreadTunnelService'),
MainThreadTimeline: createMainId<MainThreadTimelineShape>('MainThreadTimeline'),
@@ -2033,6 +2133,7 @@ export const ExtHostContext = {
ExtHostOutputService: createMainId<ExtHostOutputServiceShape>('ExtHostOutputService'),
ExtHosLabelService: createMainId<ExtHostLabelServiceShape>('ExtHostLabelService'),
ExtHostNotebook: createMainId<ExtHostNotebookShape>('ExtHostNotebook'),
ExtHostNotebookKernels: createMainId<ExtHostNotebookKernelsShape>('ExtHostNotebookKernels'),
ExtHostTheming: createMainId<ExtHostThemingShape>('ExtHostTheming'),
ExtHostTunnelService: createMainId<ExtHostTunnelServiceShape>('ExtHostTunnelService'),
ExtHostAuthentication: createMainId<ExtHostAuthenticationShape>('ExtHostAuthentication'),

View File

@@ -18,7 +18,7 @@ import { ICommandsExecutor, RemoveFromRecentlyOpenedAPICommand, OpenIssueReporte
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { IRange } from 'vs/editor/common/core/range';
import { IPosition } from 'vs/editor/common/core/position';
import { TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { TransientCellMetadata, TransientDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { VSBuffer } from 'vs/base/common/buffer';
import { decodeSemanticTokensDto } from 'vs/editor/common/services/semanticTokensDto';
@@ -30,13 +30,13 @@ const newCommands: ApiCommand[] = [
new ApiCommand(
'vscode.executeDocumentHighlights', '_executeDocumentHighlights', 'Execute document highlight provider.',
[ApiCommandArgument.Uri, ApiCommandArgument.Position],
new ApiCommandResult<modes.DocumentHighlight[], types.DocumentHighlight[] | undefined>('A promise that resolves to an array of SymbolInformation and DocumentSymbol instances.', tryMapWith(typeConverters.DocumentHighlight.to))
new ApiCommandResult<modes.DocumentHighlight[], types.DocumentHighlight[] | undefined>('A promise that resolves to an array of DocumentHighlight-instances.', tryMapWith(typeConverters.DocumentHighlight.to))
),
// -- document symbols
new ApiCommand(
'vscode.executeDocumentSymbolProvider', '_executeDocumentSymbolProvider', 'Execute document symbol provider.',
[ApiCommandArgument.Uri],
new ApiCommandResult<modes.DocumentSymbol[], vscode.SymbolInformation[] | undefined>('A promise that resolves to an array of DocumentHighlight-instances.', (value, apiArgs) => {
new ApiCommandResult<modes.DocumentSymbol[], vscode.SymbolInformation[] | undefined>('A promise that resolves to an array of SymbolInformation and DocumentSymbol instances.', (value, apiArgs) => {
if (isFalsyOrEmpty(value)) {
return undefined;
@@ -60,7 +60,7 @@ const newCommands: ApiCommand[] = [
range!: vscode.Range;
selectionRange!: vscode.Range;
children!: vscode.DocumentSymbol[];
containerName!: string;
override containerName!: string;
}
return value.map(MergedInfo.to);
@@ -343,18 +343,22 @@ const newCommands: ApiCommand[] = [
new ApiCommandResult<{
viewType: string;
displayName: string;
options: { transientOutputs: boolean; transientMetadata: TransientMetadata };
options: { transientOutputs: boolean; transientCellMetadata: TransientCellMetadata; transientDocumentMetadata: TransientDocumentMetadata; };
filenamePattern: (string | types.RelativePattern | { include: string | types.RelativePattern, exclude: string | types.RelativePattern })[]
}[], {
viewType: string;
displayName: string;
filenamePattern: vscode.NotebookFilenamePattern[];
filenamePattern: (vscode.GlobPattern | { include: vscode.GlobPattern; exclude: vscode.GlobPattern; })[];
options: vscode.NotebookDocumentContentOptions;
}[] | undefined>('A promise that resolves to an array of NotebookContentProvider static info objects.', tryMapWith(item => {
return {
viewType: item.viewType,
displayName: item.displayName,
options: { transientOutputs: item.options.transientOutputs, transientMetadata: item.options.transientMetadata },
options: {
transientOutputs: item.options.transientOutputs,
transientCellMetadata: item.options.transientCellMetadata,
transientDocumentMetadata: item.options.transientDocumentMetadata
},
filenamePattern: item.filenamePattern.map(pattern => typeConverters.NotebookExclusiveDocumentPattern.to(pattern))
};
}))

View File

@@ -14,7 +14,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import { ExtHostWebviews, toExtensionData } from 'vs/workbench/api/common/extHostWebview';
import { ExtHostWebviews, shouldSerializeBuffersForPostMessage, toExtensionData } from 'vs/workbench/api/common/extHostWebview';
import { ExtHostWebviewPanels } from 'vs/workbench/api/common/extHostWebviewPanels';
import { EditorGroupColumn } from 'vs/workbench/common/editor';
import type * as vscode from 'vscode';
@@ -183,7 +183,7 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
disposables.add(this._editorProviders.addTextProvider(viewType, extension, provider));
this._proxy.$registerTextEditorProvider(toExtensionData(extension), viewType, options.webviewOptions || {}, {
supportsMove: !!provider.moveCustomTextEditor,
});
}, shouldSerializeBuffersForPostMessage(extension));
} else {
disposables.add(this._editorProviders.addCustomProvider(viewType, extension, provider));
@@ -199,7 +199,7 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
}));
}
this._proxy.$registerCustomEditorProvider(toExtensionData(extension), viewType, options.webviewOptions || {}, !!options.supportsMultipleEditorsPerDocument);
this._proxy.$registerCustomEditorProvider(toExtensionData(extension), viewType, options.webviewOptions || {}, !!options.supportsMultipleEditorsPerDocument, shouldSerializeBuffersForPostMessage(extension));
}
return extHostTypes.Disposable.from(

View File

@@ -384,7 +384,7 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
}
};
}
return this._variableResolver.resolveAny(ws, config);
return this._variableResolver.resolveAnyAsync(ws, config);
}
protected createDebugAdapter(adapter: IAdapterDescriptor, session: ExtHostDebugSession): AbstractDebugAdapter | undefined {
@@ -990,7 +990,7 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ
}
return undefined;
}
}, undefined, process.env);
}, undefined, Promise.resolve(process.env));
}
}

View File

@@ -288,7 +288,7 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
super(name!, owner, ExtHostDiagnostics._maxDiagnosticsPerFile, loggingProxy, _onDidChangeDiagnostics);
_collections.set(owner, this);
}
dispose() {
override dispose() {
super.dispose();
_collections.delete(owner);
}

View File

@@ -42,7 +42,7 @@ export class ExtHostDocumentData extends MirrorTextModel {
super(uri, lines, eol, versionId);
}
dispose(): void {
override dispose(): void {
// we don't really dispose documents but let
// extensions still read from them. some
// operations, live saving, will now error tho

View File

@@ -3,12 +3,11 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import type * as vscode from 'vscode';
import { IDisposable } from 'vs/base/common/lifecycle';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { ExtensionActivationError, MissingDependencyError } from 'vs/workbench/services/extensions/common/extensions';
import { MissingExtensionDependency } from 'vs/workbench/services/extensions/common/extensions';
import { ILogService } from 'vs/platform/log/common/log';
const NO_OP_VOID_PROMISE = Promise.resolve<void>(undefined);
@@ -158,7 +157,7 @@ export class FailedExtension extends ActivatedExtension {
}
export interface IExtensionsActivatorHost {
onExtensionActivationError(extensionId: ExtensionIdentifier, error: ExtensionActivationError): void;
onExtensionActivationError(extensionId: ExtensionIdentifier, error: Error | null, missingExtensionDependency: MissingExtensionDependency | null): void;
actualActivateExtension(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<ActivatedExtension>;
}
@@ -255,8 +254,12 @@ export class ExtensionsActivator {
const currentExtension = this._registry.getExtensionDescription(currentActivation.id);
if (!currentExtension) {
// Error condition 0: unknown extension
this._host.onExtensionActivationError(currentActivation.id, new MissingDependencyError(currentActivation.id.value));
const error = new Error(`Unknown dependency '${currentActivation.id.value}'`);
const error = new Error(`Cannot activate unknown extension '${currentActivation.id.value}'`);
this._host.onExtensionActivationError(
currentActivation.id,
error,
new MissingExtensionDependency(currentActivation.id.value)
);
this._activatedExtensions.set(ExtensionIdentifier.toKey(currentActivation.id), new FailedExtension(error));
return;
}
@@ -280,9 +283,16 @@ export class ExtensionsActivator {
if (dep && dep.activationFailed) {
// Error condition 2: a dependency has already failed activation
this._host.onExtensionActivationError(currentExtension.identifier, nls.localize('failedDep1', "Cannot activate extension '{0}' because it depends on extension '{1}', which failed to activate.", currentExtension.displayName || currentExtension.identifier.value, depId));
const error = new Error(`Dependency ${depId} failed to activate`);
const currentExtensionFriendlyName = currentExtension.displayName || currentExtension.identifier.value;
const depDesc = this._registry.getExtensionDescription(depId);
const depFriendlyName = (depDesc ? depDesc.displayName || depId : depId);
const error = new Error(`Cannot activate the '${currentExtensionFriendlyName}' extension because its dependency '${depFriendlyName}' failed to activate`);
(<any>error).detail = dep.activationFailedError;
this._host.onExtensionActivationError(
currentExtension.identifier,
error,
null
);
this._activatedExtensions.set(ExtensionIdentifier.toKey(currentExtension.identifier), new FailedExtension(error));
return;
}
@@ -309,8 +319,13 @@ export class ExtensionsActivator {
}
// Error condition 1: unknown dependency
this._host.onExtensionActivationError(currentExtension.identifier, new MissingDependencyError(depId));
const error = new Error(`Unknown dependency '${depId}'`);
const currentExtensionFriendlyName = currentExtension.displayName || currentExtension.identifier.value;
const error = new Error(`Cannot activate the '${currentExtensionFriendlyName}' extension because it depends on unknown extension '${depId}'`);
this._host.onExtensionActivationError(
currentExtension.identifier,
error,
new MissingExtensionDependency(depId)
);
this._activatedExtensions.set(ExtensionIdentifier.toKey(currentExtension.identifier), new FailedExtension(error));
return;
}
@@ -372,7 +387,25 @@ export class ExtensionsActivator {
}
const newlyActivatingExtension = this._host.actualActivateExtension(extensionId, reason).then(undefined, (err) => {
this._host.onExtensionActivationError(extensionId, nls.localize('activationError', "Activating extension '{0}' failed: {1}.", extensionId.value, err.message));
const error = new Error();
if (err && err.name) {
error.name = err.name;
}
if (err && err.message) {
error.message = `Activating extension '${extensionId.value}' failed: ${err.message}.`;
} else {
error.message = `Activating extension '${extensionId.value}' failed: ${err}.`;
}
if (err && err.stack) {
error.stack = err.stack;
}
this._host.onExtensionActivationError(
extensionId,
error,
null
);
this._logService.error(`Activating extension ${extensionId.value} failed due to an error:`);
this._logService.error(err);
// Treat the extension as being empty

View File

@@ -17,12 +17,11 @@ import { ExtHostConfiguration, IExtHostConfiguration } from 'vs/workbench/api/co
import { ActivatedExtension, EmptyExtension, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionModule, HostExtension, ExtensionActivationTimesFragment } from 'vs/workbench/api/common/extHostExtensionActivator';
import { ExtHostStorage, IExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { ExtensionActivationError, checkProposedApiEnabled, ActivationKind } from 'vs/workbench/services/extensions/common/extensions';
import { MissingExtensionDependency, checkProposedApiEnabled, ActivationKind } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
import * as errors from 'vs/base/common/errors';
import type * as vscode from 'vscode';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { Schemas } from 'vs/base/common/network';
import { VSBuffer } from 'vs/base/common/buffer';
import { ExtensionGlobalMemento, ExtensionMemento } from 'vs/workbench/api/common/extHostMemento';
import { RemoteAuthorityResolverError, ExtensionKind, ExtensionMode, ExtensionRuntime } from 'vs/workbench/api/common/extHostTypes';
@@ -161,8 +160,8 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
this._initData.resolvedExtensions,
this._initData.hostExtensions,
{
onExtensionActivationError: (extensionId: ExtensionIdentifier, error: ExtensionActivationError): void => {
this._mainThreadExtensionsProxy.$onExtensionActivationError(extensionId, error);
onExtensionActivationError: (extensionId: ExtensionIdentifier, error: Error, missingExtensionDependency: MissingExtensionDependency | null): void => {
this._mainThreadExtensionsProxy.$onExtensionActivationError(extensionId, errors.transformErrorForSerialization(error), missingExtensionDependency);
},
actualActivateExtension: async (extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<ActivatedExtension> => {
@@ -561,17 +560,15 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
private async _doHandleExtensionTests(): Promise<number> {
const { extensionDevelopmentLocationURI, extensionTestsLocationURI } = this._initData.environment;
if (!extensionDevelopmentLocationURI || !extensionTestsLocationURI || extensionTestsLocationURI.scheme !== Schemas.file) {
if (!extensionDevelopmentLocationURI || !extensionTestsLocationURI) {
throw new Error(nls.localize('extensionTestError1', "Cannot load test runner."));
}
const extensionTestsPath = originalFSPath(extensionTestsLocationURI);
// Require the test runner via node require from the provided path
const testRunner: ITestRunner | INewTestRunner | undefined = await this._loadCommonJSModule(null, extensionTestsLocationURI, new ExtensionActivationTimesBuilder(false));
if (!testRunner || typeof testRunner.run !== 'function') {
throw new Error(nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", extensionTestsPath));
throw new Error(nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", extensionTestsLocationURI.toString()));
}
// Execute the runner if it follows the old `run` spec
@@ -584,6 +581,8 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
}
};
const extensionTestsPath = originalFSPath(extensionTestsLocationURI); // for the old test runner API
const runResult = testRunner.run(extensionTestsPath, oldTestRunnerCallback);
// Using the new API `run(): Promise<void>`
@@ -655,10 +654,10 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
}
try {
this._disposables.add(await this._extHostTunnelService.setTunnelExtensionFunctions(resolver));
performance.mark(`code/extHost/willResolveAuthority/${authorityPrefix}`);
const result = await resolver.resolve(remoteAuthority, { resolveAttempt });
performance.mark(`code/extHost/didResolveAuthorityOK/${authorityPrefix}`);
this._disposables.add(await this._extHostTunnelService.setTunnelExtensionFunctions(resolver));
// Split merged API result into separate authority/options
const authority: ResolvedAuthority = {
@@ -668,7 +667,8 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
connectionToken: result.connectionToken
};
const options: ResolvedOptions = {
extensionHostEnv: result.extensionHostEnv
extensionHostEnv: result.extensionHostEnv,
trust: result.trust
};
return {

View File

@@ -7,6 +7,7 @@ import type * as vscode from 'vscode';
import { IDisposable } from 'vs/base/common/lifecycle';
import { ExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { DeferredPromise, RunOnceScheduler } from 'vs/base/common/async';
export class ExtensionMemento implements vscode.Memento {
@@ -18,6 +19,9 @@ export class ExtensionMemento implements vscode.Memento {
private _value?: { [n: string]: any; };
private readonly _storageListener: IDisposable;
private _deferredPromises: Map<string, DeferredPromise<void>> = new Map();
private _scheduler: RunOnceScheduler;
constructor(id: string, global: boolean, storage: ExtHostStorage) {
this._id = id;
this._shared = global;
@@ -33,6 +37,23 @@ export class ExtensionMemento implements vscode.Memento {
this._value = e.value;
}
});
this._scheduler = new RunOnceScheduler(() => {
const records = this._deferredPromises;
this._deferredPromises = new Map();
(async () => {
try {
await this._storage.setValue(this._shared, this._id, this._value!);
for (const value of records.values()) {
value.complete();
}
} catch (e) {
for (const value of records.values()) {
value.error(e);
}
}
})();
}, 0);
}
get whenReady(): Promise<ExtensionMemento> {
@@ -51,7 +72,20 @@ export class ExtensionMemento implements vscode.Memento {
update(key: string, value: any): Promise<void> {
this._value![key] = value;
return this._storage.setValue(this._shared, this._id, this._value!);
let record = this._deferredPromises.get(key);
if (record !== undefined) {
return record.p;
}
const promise = new DeferredPromise<void>();
this._deferredPromises.set(key, promise);
if (!this._scheduler.isScheduled()) {
this._scheduler.schedule();
}
return promise.p;
}
dispose(): void {

File diff suppressed because it is too large Load Diff

View File

@@ -73,7 +73,7 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD
this._cellUris = new ResourceMap();
const cellLengths: number[] = [];
const cellLineCounts: number[] = [];
for (const cell of this._notebook.cells) {
for (const cell of this._notebook.getCells()) {
if (cell.kind === types.NotebookCellKind.Code && (!this._selector || score(this._selector, cell.document.uri, cell.document.languageId, true))) {
this._cellUris.set(cell.document.uri, this._cells.length);
this._cells.push(cell);

View File

@@ -3,12 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, DisposableStore, dispose } from 'vs/base/common/lifecycle';
import { Schemas } from 'vs/base/common/network';
import { deepFreeze, equals } from 'vs/base/common/objects';
import { URI } from 'vs/base/common/uri';
import { CellKind, INotebookDocumentPropertiesChangeData, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol';
import { CellKind, INotebookDocumentPropertiesChangeData, MainThreadNotebookDocumentsShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
import { ExtHostDocumentsAndEditors, IExtHostModelAddedData } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import * as extHostTypeConverters from 'vs/workbench/api/common/extHostTypeConverters';
@@ -26,7 +24,7 @@ class RawContentChangeEvent {
start: event.start,
deletedCount: event.deletedCount,
deletedItems: event.deletedItems,
items: event.items.map(data => data.cell)
items: event.items.map(data => data.apiCell)
};
});
}
@@ -46,9 +44,6 @@ export class ExtHostCell {
};
}
private _onDidDispose = new Emitter<void>();
readonly onDidDispose: Event<void> = this._onDidDispose.event;
private _outputs: extHostTypes.NotebookCellOutput[];
private _metadata: extHostTypes.NotebookCellMetadata;
private _previousResult: vscode.NotebookCellExecutionSummary | undefined;
@@ -74,16 +69,11 @@ export class ExtHostCell {
this._previousResult = extHostTypeConverters.NotebookCellPreviousExecutionResult.to(this._internalMetadata);
}
dispose() {
this._onDidDispose.fire();
this._onDidDispose.dispose();
}
get internalMetadata(): NotebookCellMetadata {
return this._internalMetadata;
}
get cell(): vscode.NotebookCell {
get apiCell(): vscode.NotebookCell {
if (!this._cell) {
const that = this;
const data = this._extHostDocument.getDocument(this.uri);
@@ -92,7 +82,7 @@ export class ExtHostCell {
}
this._cell = Object.freeze<vscode.NotebookCell>({
get index() { return that._notebook.getCellIndex(that); },
notebook: that._notebook.notebookDocument,
notebook: that._notebook.apiNotebook,
kind: extHostTypeConverters.NotebookCellKind.to(this._cellData.cellKind),
document: data.document,
get outputs() { return that._outputs.slice(0); },
@@ -133,15 +123,13 @@ export interface INotebookEventEmitter {
}
export class ExtHostNotebookDocument extends Disposable {
export class ExtHostNotebookDocument {
private static _handlePool: number = 0;
readonly handle = ExtHostNotebookDocument._handlePool++;
private _cells: ExtHostCell[] = [];
private _cellDisposableMapping = new Map<number, DisposableStore>();
private _notebook: vscode.NotebookDocument | undefined;
private _versionId: number = 0;
private _isDirty: boolean = false;
@@ -149,39 +137,43 @@ export class ExtHostNotebookDocument extends Disposable {
private _disposed: boolean = false;
constructor(
private readonly _proxy: MainThreadNotebookShape,
private readonly _proxy: MainThreadNotebookDocumentsShape,
private readonly _textDocumentsAndEditors: ExtHostDocumentsAndEditors,
private readonly _textDocuments: ExtHostDocuments,
private readonly _emitter: INotebookEventEmitter,
private readonly _viewType: string,
private _metadata: extHostTypes.NotebookDocumentMetadata,
readonly uri: URI,
) {
super();
}
) { }
dispose() {
this._disposed = true;
super.dispose();
dispose(this._cellDisposableMapping.values());
}
get notebookDocument(): vscode.NotebookDocument {
get apiNotebook(): vscode.NotebookDocument {
if (!this._notebook) {
const that = this;
this._notebook = Object.freeze({
this._notebook = {
get uri() { return that.uri; },
get version() { return that._versionId; },
get fileName() { return that.uri.fsPath; },
get viewType() { return that._viewType; },
get isDirty() { return that._isDirty; },
get isUntitled() { return that.uri.scheme === Schemas.untitled; },
get cells(): ReadonlyArray<vscode.NotebookCell> { return that._cells.map(cell => cell.cell); },
get isClosed() { return that._disposed; },
get metadata() { return that._metadata; },
set metadata(_value: Required<vscode.NotebookDocumentMetadata>) { throw new Error('Use WorkspaceEdit to update metadata.'); },
save() { return that._save(); }
});
get cellCount() { return that._cells.length; },
cellAt(index) {
index = that._validateIndex(index);
return that._cells[index].apiCell;
},
getCells(range) {
const cells = range ? that._getCells(range) : that._cells;
return cells.map(cell => cell.apiCell);
},
save() {
return that._save();
}
};
}
return this._notebook;
}
@@ -225,6 +217,35 @@ export class ExtHostNotebookDocument extends Disposable {
}
}
private _validateIndex(index: number): number {
if (index < 0) {
return 0;
} else if (index >= this._cells.length) {
return this._cells.length - 1;
} else {
return index;
}
}
private _validateRange(range: vscode.NotebookRange): vscode.NotebookRange {
if (range.start < 0) {
range = range.with({ start: 0 });
}
if (range.end > this._cells.length) {
range = range.with({ end: this._cells.length });
}
return range;
}
private _getCells(range: vscode.NotebookRange): ExtHostCell[] {
range = this._validateRange(range);
const result: ExtHostCell[] = [];
for (let i = range.start; i < range.end; i++) {
result.push(this._cells[i]);
}
return result;
}
private async _save(): Promise<boolean> {
if (this._disposed) {
return Promise.reject(new Error('Notebook has been closed'));
@@ -246,30 +267,17 @@ export class ExtHostNotebookDocument extends Disposable {
const newCells = cellDtos.map(cell => {
const extCell = new ExtHostCell(this, this._textDocumentsAndEditors, cell);
if (!initialization) {
addedCellDocuments.push(ExtHostCell.asModelAddData(this.notebookDocument, cell));
addedCellDocuments.push(ExtHostCell.asModelAddData(this.apiNotebook, cell));
}
if (!this._cellDisposableMapping.has(extCell.handle)) {
const store = new DisposableStore();
store.add(extCell);
this._cellDisposableMapping.set(extCell.handle, store);
}
return extCell;
});
for (let j = splice[0]; j < splice[0] + splice[1]; j++) {
this._cellDisposableMapping.get(this._cells[j].handle)?.dispose();
this._cellDisposableMapping.delete(this._cells[j].handle);
}
const changeEvent = new RawContentChangeEvent(splice[0], splice[1], [], newCells);
const deletedItems = this._cells.splice(splice[0], splice[1], ...newCells);
for (let cell of deletedItems) {
removedCellDocuments.push(cell.uri);
changeEvent.deletedItems.push(cell.cell);
changeEvent.deletedItems.push(cell.apiCell);
}
contentChangeEvents.push(changeEvent);
@@ -282,7 +290,7 @@ export class ExtHostNotebookDocument extends Disposable {
if (!initialization) {
this._emitter.emitModelChange(deepFreeze({
document: this.notebookDocument,
document: this.apiNotebook,
changes: RawContentChangeEvent.asApiEvents(contentChangeEvents)
}));
}
@@ -292,11 +300,11 @@ export class ExtHostNotebookDocument extends Disposable {
const cells = this._cells.splice(index, 1);
this._cells.splice(newIdx, 0, ...cells);
const changes = [
new RawContentChangeEvent(index, 1, cells.map(c => c.cell), []),
new RawContentChangeEvent(index, 1, cells.map(c => c.apiCell), []),
new RawContentChangeEvent(newIdx, 0, [], cells)
];
this._emitter.emitModelChange(deepFreeze({
document: this.notebookDocument,
document: this.apiNotebook,
changes: RawContentChangeEvent.asApiEvents(changes)
}));
}
@@ -304,18 +312,18 @@ export class ExtHostNotebookDocument extends Disposable {
private _setCellOutputs(index: number, outputs: IOutputDto[]): void {
const cell = this._cells[index];
cell.setOutputs(outputs);
this._emitter.emitCellOutputsChange(deepFreeze({ document: this.notebookDocument, cells: [cell.cell] }));
this._emitter.emitCellOutputsChange(deepFreeze({ document: this.apiNotebook, cells: [cell.apiCell] }));
}
private _setCellOutputItems(index: number, outputId: string, append: boolean, outputItems: IOutputItemDto[]): void {
const cell = this._cells[index];
cell.setOutputItems(outputId, append, outputItems);
this._emitter.emitCellOutputsChange(deepFreeze({ document: this.notebookDocument, cells: [cell.cell] }));
this._emitter.emitCellOutputsChange(deepFreeze({ document: this.apiNotebook, cells: [cell.apiCell] }));
}
private _changeCellLanguage(index: number, newModeId: string): void {
const cell = this._cells[index];
if (cell.cell.document.languageId !== newModeId) {
if (cell.apiCell.document.languageId !== newModeId) {
this._textDocuments.$acceptModelModeChanged(cell.uri, newModeId);
}
}
@@ -324,17 +332,17 @@ export class ExtHostNotebookDocument extends Disposable {
const cell = this._cells[index];
const originalInternalMetadata = cell.internalMetadata;
const originalExtMetadata = cell.cell.metadata;
const originalExtMetadata = cell.apiCell.metadata;
cell.setMetadata(newMetadata);
const newExtMetadata = cell.cell.metadata;
const newExtMetadata = cell.apiCell.metadata;
if (!equals(originalExtMetadata, newExtMetadata)) {
this._emitter.emitCellMetadataChange(deepFreeze({ document: this.notebookDocument, cell: cell.cell }));
this._emitter.emitCellMetadataChange(deepFreeze({ document: this.apiNotebook, cell: cell.apiCell }));
}
if (originalInternalMetadata.runState !== newMetadata.runState) {
const executionState = newMetadata.runState ?? extHostTypes.NotebookCellExecutionState.Idle;
this._emitter.emitCellExecutionStateChange(deepFreeze({ document: this.notebookDocument, cell: cell.cell, executionState }));
this._emitter.emitCellExecutionStateChange(deepFreeze({ document: this.apiNotebook, cell: cell.apiCell, executionState }));
}
}

View File

@@ -3,8 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Emitter, Event } from 'vs/base/common/event';
import { MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol';
import { MainThreadNotebookEditorsShape } from 'vs/workbench/api/common/extHost.protocol';
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
import * as extHostConverter from 'vs/workbench/api/common/extHostTypeConverters';
import { CellEditType, ICellEditOperation, ICellReplaceEdit } from 'vs/workbench/contrib/notebook/common/notebookCommon';
@@ -58,17 +57,6 @@ class NotebookEditorCellEditBuilder implements vscode.NotebookEditorEdit {
});
}
replaceCellOutput(index: number, outputs: vscode.NotebookCellOutput[]): void {
this._throwIfFinalized();
this._collectedEdits.push({
editType: CellEditType.Output,
index,
outputs: outputs.map(output => {
return extHostConverter.NotebookCellOutput.from(output);
})
});
}
replaceCells(from: number, to: number, cells: vscode.NotebookCellData[]): void {
this._throwIfFinalized();
if (from === to && cells.length === 0) {
@@ -85,27 +73,21 @@ class NotebookEditorCellEditBuilder implements vscode.NotebookEditorEdit {
export class ExtHostNotebookEditor {
private _selections: vscode.NotebookCellRange[] = [];
private _visibleRanges: vscode.NotebookCellRange[] = [];
private _selections: vscode.NotebookRange[] = [];
private _visibleRanges: vscode.NotebookRange[] = [];
private _viewColumn?: vscode.ViewColumn;
private _visible: boolean = false;
private _kernel?: vscode.NotebookKernel;
private readonly _hasDecorationsForKey = new Set<string>();
private readonly _onDidDispose = new Emitter<void>();
readonly onDidDispose: Event<void> = this._onDidDispose.event;
private _editor?: vscode.NotebookEditor;
constructor(
readonly id: string,
private readonly _viewType: string,
private readonly _proxy: MainThreadNotebookShape,
private readonly _proxy: MainThreadNotebookEditorsShape,
readonly notebookData: ExtHostNotebookDocument,
visibleRanges: vscode.NotebookCellRange[],
selections: vscode.NotebookCellRange[],
visibleRanges: vscode.NotebookRange[],
selections: vscode.NotebookRange[],
viewColumn: vscode.ViewColumn | undefined
) {
this._selections = selections;
@@ -113,21 +95,12 @@ export class ExtHostNotebookEditor {
this._viewColumn = viewColumn;
}
dispose() {
this._onDidDispose.fire();
this._onDidDispose.dispose();
}
get editor(): vscode.NotebookEditor {
get apiEditor(): vscode.NotebookEditor {
if (!this._editor) {
const that = this;
this._editor = {
get document() {
return that.notebookData.notebookDocument;
},
get selection() {
const primarySelection = that._selections[0];
return primarySelection && that.notebookData.getCellFromIndex(primarySelection.start)?.cell;
return that.notebookData.apiNotebook;
},
get selections() {
return that._selections;
@@ -138,24 +111,18 @@ export class ExtHostNotebookEditor {
revealRange(range, revealType) {
that._proxy.$tryRevealRange(
that.id,
extHostConverter.NotebookCellRange.from(range),
extHostConverter.NotebookRange.from(range),
revealType ?? extHostTypes.NotebookEditorRevealType.Default
);
},
get viewColumn() {
return that._viewColumn;
},
get onDidDispose() {
return that.onDidDispose;
},
edit(callback) {
const edit = new NotebookEditorCellEditBuilder(this.document.version);
callback(edit);
return that._applyEdit(edit.finalize());
},
get kernel() {
return that._kernel;
},
setDecorations(decorationType, range) {
return that.setDecorations(decorationType, range);
}
@@ -164,10 +131,6 @@ export class ExtHostNotebookEditor {
return this._editor;
}
_acceptKernel(kernel?: vscode.NotebookKernel) {
this._kernel = kernel;
}
get visible(): boolean {
return this._visible;
}
@@ -176,14 +139,18 @@ export class ExtHostNotebookEditor {
this._visible = value;
}
_acceptVisibleRanges(value: vscode.NotebookCellRange[]): void {
_acceptVisibleRanges(value: vscode.NotebookRange[]): void {
this._visibleRanges = value;
}
_acceptSelections(selections: vscode.NotebookCellRange[]): void {
_acceptSelections(selections: vscode.NotebookRange[]): void {
this._selections = selections;
}
_acceptViewColumn(value: vscode.ViewColumn | undefined) {
this._viewColumn = value;
}
private _applyEdit(editData: INotebookEditData): Promise<boolean> {
// return when there is nothing to do
@@ -217,10 +184,10 @@ export class ExtHostNotebookEditor {
compressedEditsIndex++;
}
return this._proxy.$tryApplyEdits(this._viewType, this.notebookData.uri, editData.documentVersionId, compressedEdits);
return this._proxy.$tryApplyEdits(this.id, editData.documentVersionId, compressedEdits);
}
setDecorations(decorationType: vscode.NotebookEditorDecorationType, range: vscode.NotebookCellRange): void {
setDecorations(decorationType: vscode.NotebookEditorDecorationType, range: vscode.NotebookRange): void {
if (range.isEmpty && !this._hasDecorationsForKey.has(decorationType.key)) {
// avoid no-op call to the renderer
return;
@@ -233,7 +200,7 @@ export class ExtHostNotebookEditor {
return this._proxy.$trySetDecorations(
this.id,
extHostConverter.NotebookCellRange.from(range),
extHostConverter.NotebookRange.from(range),
decorationType.key
);
}

View File

@@ -0,0 +1,259 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Emitter } from 'vs/base/common/event';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { ExtHostNotebookKernelsShape, IMainContext, INotebookKernelDto2, MainContext, MainThreadNotebookKernelsShape } from 'vs/workbench/api/common/extHost.protocol';
import * as vscode from 'vscode';
import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { URI, UriComponents } from 'vs/base/common/uri';
import * as extHostTypeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
import { asWebviewUri } from 'vs/workbench/api/common/shared/webview';
interface IKernelData {
extensionId: ExtensionIdentifier,
controller: vscode.NotebookController;
onDidChangeSelection: Emitter<{ selected: boolean; notebook: vscode.NotebookDocument; }>;
onDidReceiveMessage: Emitter<{ editor: vscode.NotebookEditor, message: any }>;
}
export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape {
private readonly _proxy: MainThreadNotebookKernelsShape;
private readonly _kernelData = new Map<number, IKernelData>();
private _handlePool: number = 0;
constructor(
mainContext: IMainContext,
private readonly _initData: IExtHostInitDataService,
private readonly _extHostNotebook: ExtHostNotebookController
) {
this._proxy = mainContext.getProxy(MainContext.MainThreadNotebookKernels);
}
createNotebookController(extension: IExtensionDescription, id: string, viewType: string, label: string, handler?: vscode.NotebookExecuteHandler, preloads?: vscode.NotebookKernelPreload[]): vscode.NotebookController {
for (let data of this._kernelData.values()) {
if (data.controller.id === id && ExtensionIdentifier.equals(extension.identifier, data.extensionId)) {
throw new Error(`notebook controller with id '${id}' ALREADY exist`);
}
}
const handle = this._handlePool++;
const that = this;
const _defaultExecutHandler = () => console.warn(`NO execute handler from notebook controller '${data.id}' of extension: '${extension.identifier}'`);
let isDisposed = false;
const commandDisposables = new DisposableStore();
const onDidChangeSelection = new Emitter<{ selected: boolean, notebook: vscode.NotebookDocument }>();
const onDidReceiveMessage = new Emitter<{ editor: vscode.NotebookEditor, message: any }>();
const data: INotebookKernelDto2 = {
id: `${extension.identifier.value}/${id}`,
viewType,
extensionId: extension.identifier,
extensionLocation: extension.extensionLocation,
label: label || extension.identifier.value,
preloads: preloads ? preloads.map(extHostTypeConverters.NotebookKernelPreload.from) : []
};
//
let _executeHandler: vscode.NotebookExecuteHandler = handler ?? _defaultExecutHandler;
let _interruptHandler: vscode.NotebookInterruptHandler | undefined;
// todo@jrieken the selector needs to be massaged
this._proxy.$addKernel(handle, data).catch(err => {
// this can happen when a kernel with that ID is already registered
console.log(err);
isDisposed = true;
});
// update: all setters write directly into the dto object
// and trigger an update. the actual update will only happen
// once per event loop execution
let tokenPool = 0;
const _update = () => {
if (isDisposed) {
return;
}
const myToken = ++tokenPool;
Promise.resolve().then(() => {
if (myToken === tokenPool) {
this._proxy.$updateKernel(handle, data);
}
});
};
const controller: vscode.NotebookController = {
get id() { return id; },
get viewType() { return data.viewType; },
onDidChangeNotebookAssociation: onDidChangeSelection.event,
get label() {
return data.label;
},
set label(value) {
data.label = value ?? extension.displayName ?? extension.name;
_update();
},
get detail() {
return data.detail ?? '';
},
set detail(value) {
data.detail = value;
_update();
},
get description() {
return data.description ?? '';
},
set description(value) {
data.description = value;
_update();
},
get supportedLanguages() {
return data.supportedLanguages;
},
set supportedLanguages(value) {
data.supportedLanguages = value;
_update();
},
get hasExecutionOrder() {
return data.hasExecutionOrder ?? false;
},
set hasExecutionOrder(value) {
data.hasExecutionOrder = value;
_update();
},
get preloads() {
return data.preloads ? data.preloads.map(extHostTypeConverters.NotebookKernelPreload.to) : [];
},
get executeHandler() {
return _executeHandler;
},
set executeHandler(value) {
_executeHandler = value ?? _defaultExecutHandler;
},
get interruptHandler() {
return _interruptHandler;
},
set interruptHandler(value) {
_interruptHandler = value;
data.supportsInterrupt = Boolean(value);
_update();
},
createNotebookCellExecutionTask(cell) {
if (isDisposed) {
throw new Error('notebook controller is DISPOSED');
}
//todo@jrieken
return that._extHostNotebook.createNotebookCellExecution(cell.notebook.uri, cell.index, data.id)!;
},
dispose: () => {
if (!isDisposed) {
isDisposed = true;
this._kernelData.delete(handle);
commandDisposables.dispose();
onDidChangeSelection.dispose();
onDidReceiveMessage.dispose();
this._proxy.$removeKernel(handle);
}
},
// --- ipc
onDidReceiveMessage: onDidReceiveMessage.event,
postMessage(message, editor) {
return that._proxy.$postMessage(handle, editor && that._extHostNotebook.getIdByEditor(editor), message);
},
asWebviewUri(uri: URI) {
return asWebviewUri(that._initData.environment, String(handle), uri);
},
// --- priority
updateNotebookAffinity(notebook, priority) {
that._proxy.$updateNotebookPriority(handle, notebook.uri, priority);
}
};
this._kernelData.set(handle, { extensionId: extension.identifier, controller, onDidChangeSelection, onDidReceiveMessage });
return controller;
}
$acceptSelection(handle: number, uri: UriComponents, value: boolean): void {
const obj = this._kernelData.get(handle);
if (obj) {
obj.onDidChangeSelection.fire({
selected: value,
notebook: this._extHostNotebook.lookupNotebookDocument(URI.revive(uri))!.apiNotebook
});
}
}
async $executeCells(handle: number, uri: UriComponents, handles: number[]): Promise<void> {
const obj = this._kernelData.get(handle);
if (!obj) {
// extension can dispose kernels in the meantime
return;
}
const document = this._extHostNotebook.lookupNotebookDocument(URI.revive(uri));
if (!document) {
throw new Error('MISSING notebook');
}
const cells: vscode.NotebookCell[] = [];
for (let cellHandle of handles) {
const cell = document.getCell(cellHandle);
if (cell) {
cells.push(cell.apiCell);
}
}
try {
await obj.controller.executeHandler.call(obj.controller, cells, document.apiNotebook, obj.controller);
} catch (err) {
//
console.error(err);
}
}
async $cancelCells(handle: number, uri: UriComponents, handles: number[]): Promise<void> {
const obj = this._kernelData.get(handle);
if (!obj) {
// extension can dispose kernels in the meantime
return;
}
const document = this._extHostNotebook.lookupNotebookDocument(URI.revive(uri));
if (!document) {
throw new Error('MISSING notebook');
}
if (obj.controller.interruptHandler) {
await obj.controller.interruptHandler.call(obj.controller, document.apiNotebook);
}
// we do both? interrupt and cancellation or should we be selective?
for (let cellHandle of handles) {
const cell = document.getCell(cellHandle);
if (cell) {
this._extHostNotebook.cancelOneNotebookCellExecution(cell);
}
}
}
$acceptRendererMessage(handle: number, editorId: string, message: any): void {
const obj = this._kernelData.get(handle);
if (!obj) {
// extension can dispose kernels in the meantime
return;
}
const editor = this._extHostNotebook.getEditorById(editorId);
if (!editor) {
throw new Error(`send message for UNKNOWN editor: ${editorId}`);
}
obj.onDidReceiveMessage.fire(Object.freeze({ editor: editor.apiEditor, message }));
}
}

View File

@@ -73,7 +73,7 @@ export abstract class AbstractExtHostOutputChannel extends Disposable implements
}
}
dispose(): void {
override dispose(): void {
super.dispose();
if (!this._disposed) {
@@ -90,7 +90,7 @@ export class ExtHostPushOutputChannel extends AbstractExtHostOutputChannel {
super(name, false, undefined, proxy);
}
append(value: string): void {
override append(value: string): void {
super.append(value);
this._id.then(id => this._proxy.$append(id, value));
this._onDidAppend.fire();
@@ -103,7 +103,7 @@ class ExtHostLogFileOutputChannel extends AbstractExtHostOutputChannel {
super(name, true, file, proxy);
}
append(value: string): void {
override append(value: string): void {
throw new Error('Not supported');
}
}

View File

@@ -259,6 +259,22 @@ export class ExtHostSCMInputBox implements vscode.SourceControlInputBox {
// noop
}
focus(): void {
checkProposedApiEnabled(this._extension);
if (!this._visible) {
this.visible = true;
}
this._proxy.$setInputBoxFocus(this._sourceControlHandle);
}
showValidationMessage(message: string, type: vscode.SourceControlInputBoxValidationType) {
checkProposedApiEnabled(this._extension);
this._proxy.$showValidationMessage(this._sourceControlHandle, message, type as any);
}
$onInputBoxValueChange(value: string): void {
this.updateValue(value);
}

View File

@@ -5,7 +5,7 @@
import type * as vscode from 'vscode';
import { Event, Emitter } from 'vs/base/common/event';
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IShellLaunchConfigDto, IShellAndArgsDto, ITerminalDimensionsDto, ITerminalLinkDto, TerminalIdentifier } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IShellAndArgsDto, ITerminalDimensionsDto, ITerminalLinkDto, TerminalIdentifier } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { URI } from 'vs/base/common/uri';
@@ -19,7 +19,7 @@ import { serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/ter
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { generateUuid } from 'vs/base/common/uuid';
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
import { ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalLaunchError, TerminalShellType } from 'vs/platform/terminal/common/terminal';
import { IShellLaunchConfigDto, ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalEnvironment, ITerminalLaunchError, TerminalShellType } from 'vs/platform/terminal/common/terminal';
import { TerminalDataBufferer } from 'vs/platform/terminal/common/terminalDataBuffering';
import { ITerminalProfile } from 'vs/workbench/contrib/terminal/common/terminal';
@@ -117,7 +117,9 @@ export class ExtHostTerminal {
shellPath?: string,
shellArgs?: string[] | string,
cwd?: string | URI,
env?: { [key: string]: string | null },
env?: ITerminalEnvironment,
icon?: string,
initialText?: string,
waitOnExit?: boolean,
strictEnv?: boolean,
hideFromUser?: boolean,
@@ -127,7 +129,7 @@ export class ExtHostTerminal {
if (typeof this._id !== 'string') {
throw new Error('Terminal has already been created');
}
await this._proxy.$createTerminal(this._id, { name: this._name, shellPath, shellArgs, cwd, env, waitOnExit, strictEnv, hideFromUser, isFeatureTerminal, isExtensionOwnedTerminal });
await this._proxy.$createTerminal(this._id, { name: this._name, shellPath, shellArgs, cwd, env, icon, initialText, waitOnExit, strictEnv, hideFromUser, isFeatureTerminal, isExtensionOwnedTerminal });
}
public async createExtensionTerminal(): Promise<number> {
@@ -225,6 +227,10 @@ export class ExtHostPseudoterminal implements ITerminalChildProcess {
}
}
async processBinary(data: string): Promise<void> {
// No-op, processBinary is not supported in extextion owned terminals.
}
acknowledgeDataEvent(charCount: number): void {
// No-op, flow control is not supported in extension owned terminals. If this is ever
// implemented it will need new pause and resume VS Code APIs.
@@ -328,9 +334,8 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
public abstract createTerminalFromOptions(options: vscode.TerminalOptions): vscode.Terminal;
public abstract getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string;
public abstract getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string;
public abstract $getAvailableProfiles(quickLaunchOnly: boolean): Promise<ITerminalProfile[]>;
public abstract $getAvailableProfiles(configuredProfilesOnly: boolean): Promise<ITerminalProfile[]>;
public abstract $getDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto>;
public abstract $acceptWorkspacePermissionsChanged(isAllowed: boolean): void;
public createExtensionTerminal(options: vscode.ExtensionTerminalOptions): vscode.Terminal {
const terminal = new ExtHostTerminal(this._proxy, generateUuid(), options, options.name);
@@ -771,23 +776,18 @@ export class WorkerExtHostTerminalService extends BaseExtHostTerminalService {
}
public getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string {
// Return the empty string to avoid throwing
return '';
throw new NotSupportedError();
}
public getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string {
throw new NotSupportedError();
}
public $getAvailableProfiles(quickLaunchOnly: boolean): Promise<ITerminalProfile[]> {
public $getAvailableProfiles(configuredProfilesOnly: boolean): Promise<ITerminalProfile[]> {
throw new NotSupportedError();
}
public async $getDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto> {
throw new NotSupportedError();
}
public $acceptWorkspacePermissionsChanged(isAllowed: boolean): void {
// No-op for web worker ext host as workspace permissions aren't used
}
}

View File

@@ -4,10 +4,12 @@
*--------------------------------------------------------------------------------------------*/
import { mapFind } from 'vs/base/common/arrays';
import { disposableTimeout } from 'vs/base/common/async';
import { Barrier, DeferredPromise, disposableTimeout, isThenable } from 'vs/base/common/async';
import { VSBuffer } from 'vs/base/common/buffer';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { Emitter } from 'vs/base/common/event';
import { once } from 'vs/base/common/functional';
import { Iterable } from 'vs/base/common/iterator';
import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { deepFreeze } from 'vs/base/common/objects';
import { isDefined } from 'vs/base/common/types';
@@ -17,34 +19,40 @@ import { ExtHostTestingResource, ExtHostTestingShape, MainContext, MainThreadTes
import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData';
import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { ExtHostTestItemEventType, getPrivateApiFor } from 'vs/workbench/api/common/extHostTestingPrivateApi';
import * as Convert from 'vs/workbench/api/common/extHostTypeConverters';
import { Disposable, TestItem as TestItemImpl, TestItemHookProperty } from 'vs/workbench/api/common/extHostTypes';
import { Disposable, TestItemImpl } from 'vs/workbench/api/common/extHostTypes';
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { OwnedTestCollection, SingleUseTestCollection, TestPosition } from 'vs/workbench/contrib/testing/common/ownedTestCollection';
import { AbstractIncrementalTestCollection, IncrementalChangeCollector, IncrementalTestCollectionItem, InternalTestItem, ISerializedTestResults, RunTestForProviderRequest, TestDiffOpType, TestIdWithSrc, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
import { AbstractIncrementalTestCollection, IncrementalChangeCollector, IncrementalTestCollectionItem, InternalTestItem, ISerializedTestResults, ITestItem, RunTestForProviderRequest, TestDiffOpType, TestIdWithSrc, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
import type * as vscode from 'vscode';
const getTestSubscriptionKey = (resource: ExtHostTestingResource, uri: URI) => `${resource}:${uri.toString()}`;
export class ExtHostTesting implements ExtHostTestingShape {
private readonly resultsChangedEmitter = new Emitter<void>();
private readonly providers = new Map<string, vscode.TestProvider>();
private readonly controllers = new Map<string, {
extensionId: string,
instance: vscode.TestController<unknown>
}>();
private readonly proxy: MainThreadTestingShape;
private readonly ownedTests = new OwnedTestCollection();
private readonly testSubscriptions = new Map<string, {
private readonly runQueue: TestRunQueue;
private readonly testControllers = new Map<string, {
collection: SingleUseTestCollection;
store: IDisposable;
subscribeFn: (id: string, provider: vscode.TestProvider) => void;
subscribeFn: (id: string, provider: vscode.TestController<unknown>) => void;
}>();
private workspaceObservers: WorkspaceFolderTestObserverFactory;
private textDocumentObservers: TextDocumentTestObserverFactory;
public onResultsChanged = this.resultsChangedEmitter.event;
public results: ReadonlyArray<vscode.TestResults> = [];
public results: ReadonlyArray<vscode.TestRunResult> = [];
constructor(@IExtHostRpcService rpc: IExtHostRpcService, @IExtHostDocumentsAndEditors private readonly documents: IExtHostDocumentsAndEditors, @IExtHostWorkspace private readonly workspace: IExtHostWorkspace) {
this.proxy = rpc.getProxy(MainContext.MainThreadTesting);
this.runQueue = new TestRunQueue(this.proxy);
this.workspaceObservers = new WorkspaceFolderTestObserverFactory(this.proxy);
this.textDocumentObservers = new TextDocumentTestObserverFactory(this.proxy, documents);
}
@@ -52,22 +60,22 @@ export class ExtHostTesting implements ExtHostTestingShape {
/**
* Implements vscode.test.registerTestProvider
*/
public registerTestProvider<T extends vscode.TestItem>(provider: vscode.TestProvider<T>): vscode.Disposable {
const providerId = generateUuid();
this.providers.set(providerId, provider);
this.proxy.$registerTestProvider(providerId);
public registerTestController<T>(extensionId: string, controller: vscode.TestController<T>): vscode.Disposable {
const controllerId = generateUuid();
this.controllers.set(controllerId, { instance: controller, extensionId });
this.proxy.$registerTestController(controllerId);
// give the ext a moment to register things rather than synchronously invoking within activate()
const toSubscribe = [...this.testSubscriptions.keys()];
const toSubscribe = [...this.testControllers.keys()];
setTimeout(() => {
for (const subscription of toSubscribe) {
this.testSubscriptions.get(subscription)?.subscribeFn(providerId, provider);
this.testControllers.get(subscription)?.subscribeFn(controllerId, controller);
}
}, 0);
return new Disposable(() => {
this.providers.delete(providerId);
this.proxy.$unregisterTestProvider(providerId);
this.controllers.delete(controllerId);
this.proxy.$unregisterTestController(controllerId);
});
}
@@ -88,8 +96,8 @@ export class ExtHostTesting implements ExtHostTestingShape {
/**
* Implements vscode.test.runTests
*/
public async runTests(req: vscode.TestRunOptions<vscode.TestItem>, token = CancellationToken.None) {
const testListToProviders = (tests: ReadonlyArray<vscode.TestItem>) =>
public async runTests(req: vscode.TestRunRequest<unknown>, token = CancellationToken.None) {
const testListToProviders = (tests: ReadonlyArray<vscode.TestItem<unknown>>) =>
tests
.map(this.getInternalTestForReference, this)
.filter(isDefined)
@@ -103,10 +111,10 @@ export class ExtHostTesting implements ExtHostTestingShape {
}
/**
* Implements vscode.test.publishTestResults
* Implements vscode.test.createTestRun
*/
public publishExtensionProvidedResults(results: vscode.TestResults, persist: boolean): void {
this.proxy.$publishExtensionProvidedResults(Convert.TestResults.from(generateUuid(), results), persist);
public createTestRun<T>(extensionId: string, request: vscode.TestRunRequest<T>, name: string | undefined, persist = true): vscode.TestRun<T> {
return this.runQueue.createTestRun(extensionId, request, name, persist);
}
/**
@@ -132,12 +140,12 @@ export class ExtHostTesting implements ExtHostTestingShape {
public async $subscribeToTests(resource: ExtHostTestingResource, uriComponents: UriComponents) {
const uri = URI.revive(uriComponents);
const subscriptionKey = getTestSubscriptionKey(resource, uri);
if (this.testSubscriptions.has(subscriptionKey)) {
if (this.testControllers.has(subscriptionKey)) {
return;
}
const cancellation = new CancellationTokenSource();
let method: undefined | ((p: vscode.TestProvider) => vscode.ProviderResult<vscode.TestItem>);
let method: undefined | ((p: vscode.TestController<unknown>) => vscode.ProviderResult<vscode.TestItem<unknown>>);
if (resource === ExtHostTestingResource.TextDocument) {
let document = this.documents.getDocument(uri);
@@ -156,14 +164,14 @@ export class ExtHostTesting implements ExtHostTestingShape {
if (document) {
const folder = await this.workspace.getWorkspaceFolder2(uri, false);
method = p => p.provideDocumentTestRoot
? p.provideDocumentTestRoot(document!.document, cancellation.token)
method = p => p.createDocumentTestRoot
? p.createDocumentTestRoot(document!.document, cancellation.token)
: createDefaultDocumentTestRoot(p, document!.document, folder, cancellation.token);
}
} else {
const folder = await this.workspace.getWorkspaceFolder2(uri, false);
if (folder) {
method = p => p.provideWorkspaceTestRoot(folder, cancellation.token);
method = p => p.createWorkspaceTestRoot(folder, cancellation.token);
}
}
@@ -171,7 +179,7 @@ export class ExtHostTesting implements ExtHostTestingShape {
return;
}
const subscribeFn = async (id: string, provider: vscode.TestProvider) => {
const subscribeFn = async (id: string, provider: vscode.TestController<unknown>) => {
try {
const root = await method!(provider);
if (root) {
@@ -186,15 +194,16 @@ export class ExtHostTesting implements ExtHostTestingShape {
const collection = disposable.add(this.ownedTests.createForHierarchy(
diff => this.proxy.$publishDiff(resource, uriComponents, diff)));
disposable.add(toDisposable(() => cancellation.dispose(true)));
for (const [id, provider] of this.providers) {
subscribeFn(id, provider);
const subscribes: Promise<void>[] = [];
for (const [id, controller] of this.controllers) {
subscribes.push(subscribeFn(id, controller.instance));
}
// note: we don't increment the root count initially -- this is done by the
// note: we don't increment the count initially -- this is done by the
// main thread, incrementing once per extension host. We just push the
// diff to signal that roots have been discovered.
collection.pushDiff([TestDiffOpType.DeltaRootsComplete, -1]);
this.testSubscriptions.set(subscriptionKey, { store: disposable, collection, subscribeFn });
Promise.all(subscribes).then(() => collection.pushDiff([TestDiffOpType.IncrementPendingExtHosts, -1]));
this.testControllers.set(subscriptionKey, { store: disposable, collection, subscribeFn });
}
/**
@@ -203,7 +212,7 @@ export class ExtHostTesting implements ExtHostTestingShape {
* @override
*/
public async $expandTest(test: TestIdWithSrc, levels: number) {
const sub = mapFind(this.testSubscriptions.values(), s => s.collection.treeId === test.src.tree ? s : undefined);
const sub = mapFind(this.testControllers.values(), s => s.collection.treeId === test.src.tree ? s : undefined);
await sub?.collection.expand(test.testId, levels < 0 ? Infinity : levels);
this.flushCollectionDiffs();
}
@@ -215,8 +224,8 @@ export class ExtHostTesting implements ExtHostTestingShape {
public $unsubscribeFromTests(resource: ExtHostTestingResource, uriComponents: UriComponents) {
const uri = URI.revive(uriComponents);
const subscriptionKey = getTestSubscriptionKey(resource, uri);
this.testSubscriptions.get(subscriptionKey)?.store.dispose();
this.testSubscriptions.delete(subscriptionKey);
this.testControllers.get(subscriptionKey)?.store.dispose();
this.testControllers.delete(subscriptionKey);
}
/**
@@ -237,14 +246,14 @@ export class ExtHostTesting implements ExtHostTestingShape {
* providers to be run.
* @override
*/
public async $runTestsForProvider(req: RunTestForProviderRequest, cancellation: CancellationToken): Promise<void> {
const provider = this.providers.get(req.tests[0].src.provider);
if (!provider) {
public async $runTestsForProvider(req: RunTestForProviderRequest, token: CancellationToken): Promise<void> {
const controller = this.controllers.get(req.tests[0].src.controller);
if (!controller) {
return;
}
const includeTests = req.tests
.map(({ testId, src }) => this.ownedTests.getTestById(testId, src.tree))
.map(({ testId, src }) => this.ownedTests.getTestById(testId, src?.tree))
.filter(isDefined)
.map(([_tree, test]) => test);
@@ -259,35 +268,19 @@ export class ExtHostTesting implements ExtHostTestingShape {
return;
}
try {
await provider.runTests({
setState: (test, state) => {
// for test providers that don't support excluding natively,
// make sure not to report excluded result otherwise summaries will be off.
for (const [tree, exclude] of excludeTests) {
const e = tree.comparePositions(exclude, test.id);
if (e === TestPosition.IsChild || e === TestPosition.IsSame) {
return;
}
}
const publicReq: vscode.TestRunRequest<unknown> = {
tests: includeTests.map(t => TestItemFilteredWrapper.unwrap(t.actual)),
exclude: excludeTests.map(([, t]) => TestItemFilteredWrapper.unwrap(t.actual)),
debug: req.debug,
};
this.flushCollectionDiffs();
this.proxy.$updateTestStateInRun(req.runId, test.id, Convert.TestState.from(state));
},
tests: includeTests.map(t => TestItemFilteredWrapper.unwrap(t.actual)),
exclude: excludeTests.map(([, t]) => TestItemFilteredWrapper.unwrap(t.actual)),
debug: req.debug,
}, cancellation);
for (const { collection } of this.testSubscriptions.values()) {
collection.flushDiff(); // ensure all states are updated
}
return;
} catch (e) {
console.error(e); // so it appears to attached debuggers
throw e;
}
await this.runQueue.enqueueRun({
dto: TestRunDto.fromInternal(req),
token,
extensionId: controller.extensionId,
req: publicReq,
doRun: () => controller!.instance.runTests(publicReq, token)
});
}
public $lookupTest(req: TestIdWithSrc): Promise<InternalTestItem | undefined> {
@@ -305,7 +298,7 @@ export class ExtHostTesting implements ExtHostTestingShape {
* main thread is updated.
*/
private flushCollectionDiffs() {
for (const { collection } of this.testSubscriptions.values()) {
for (const { collection } of this.testControllers.values()) {
collection.flushDiff();
}
}
@@ -313,18 +306,236 @@ export class ExtHostTesting implements ExtHostTestingShape {
/**
* Gets the internal test item associated with the reference from the extension.
*/
private getInternalTestForReference(test: vscode.TestItem) {
private getInternalTestForReference(test: vscode.TestItem<unknown>) {
// Find workspace items first, then owned tests, then document tests.
// If a test instance exists in both the workspace and document, prefer
// the workspace because it's less ephemeral.
return this.workspaceObservers.getMirroredTestDataByReference(test)
?? mapFind(this.testSubscriptions.values(), c => c.collection.getTestByReference(test))
?? mapFind(this.testControllers.values(), c => c.collection.getTestByReference(test))
?? this.textDocumentObservers.getMirroredTestDataByReference(test);
}
}
export const createDefaultDocumentTestRoot = async <T extends vscode.TestItem>(
provider: vscode.TestProvider<T>,
/**
* Queues runs for a single extension and provides the currently-executing
* run so that `createTestRun` can be properly correlated.
*/
class TestRunQueue {
private readonly state = new Map</* extensionId */ string, {
current: {
publicReq: vscode.TestRunRequest<unknown>,
factory: (name: string | undefined) => TestRunTask<unknown>,
},
queue: (() => (Promise<void> | void))[];
}>();
constructor(private readonly proxy: MainThreadTestingShape) { }
/**
* Registers and enqueues a test run. `doRun` will be called when an
* invokation to {@link TestController.runTests} should be called.
*/
public enqueueRun(opts: {
extensionId: string,
req: vscode.TestRunRequest<unknown>,
dto: TestRunDto,
token: CancellationToken,
doRun: () => Thenable<void> | void,
},
) {
let record = this.state.get(opts.extensionId);
if (!record) {
record = { queue: [], current: undefined as any };
this.state.set(opts.extensionId, record);
}
const deferred = new DeferredPromise<void>();
const runner = () => {
const tasks: TestRunTask<unknown>[] = [];
const shared = new Set<string>();
record!.current = {
publicReq: opts.req,
factory: name => {
const task = new TestRunTask(name, opts.dto, shared, this.proxy);
tasks.push(task);
opts.token.onCancellationRequested(() => task.end());
return task;
},
};
this.invokeRunner(opts.extensionId, opts.dto.id, opts.doRun, tasks).finally(() => deferred.complete());
};
record.queue.push(runner);
if (record.queue.length === 1) {
runner();
}
return deferred.p;
}
/**
* Implements the public `createTestRun` API.
*/
public createTestRun<T>(extensionId: string, request: vscode.TestRunRequest<T>, name: string | undefined, persist: boolean): vscode.TestRun<T> {
const state = this.state.get(extensionId);
// If the request is for the currently-executing `runTests`, then correlate
// it to that existing run. Otherwise return a new, detached run.
if (state?.current.publicReq === request) {
return state.current.factory(name);
}
const dto = TestRunDto.fromPublic(request);
const task = new TestRunTask(name, dto, new Set(), this.proxy);
this.proxy.$startedExtensionTestRun({
debug: request.debug,
exclude: request.exclude?.map(t => t.id) ?? [],
id: dto.id,
tests: request.tests.map(t => t.id),
persist: persist
});
task.onEnd.wait().then(() => this.proxy.$finishedExtensionTestRun(dto.id));
return task;
}
private invokeRunner<T>(extensionId: string, runId: string, fn: () => Thenable<void> | void, tasks: TestRunTask<T>[]): Promise<void> {
try {
const res = fn();
if (isThenable(res)) {
return res
.then(() => this.handleInvokeResult(extensionId, runId, tasks, undefined))
.catch(err => this.handleInvokeResult(extensionId, runId, tasks, err));
} else {
return this.handleInvokeResult(extensionId, runId, tasks, undefined);
}
} catch (e) {
return this.handleInvokeResult(extensionId, runId, tasks, e);
}
}
private async handleInvokeResult<T>(extensionId: string, runId: string, tasks: TestRunTask<T>[], error?: Error) {
const record = this.state.get(extensionId);
if (!record) {
return;
}
record.queue.shift();
if (record.queue.length > 0) {
record.queue[0]();
} else {
this.state.delete(extensionId);
}
await Promise.all(tasks.map(t => t.onEnd.wait()));
}
}
class TestRunDto {
public static fromPublic(request: vscode.TestRunRequest<unknown>) {
return new TestRunDto(
generateUuid(),
new Set(request.tests.map(t => t.id)),
new Set(request.exclude?.map(t => t.id) ?? Iterable.empty()),
);
}
public static fromInternal(request: RunTestForProviderRequest) {
return new TestRunDto(
request.runId,
new Set(request.tests.map(t => t.testId)),
new Set(request.excludeExtIds),
);
}
constructor(
public readonly id: string,
private readonly include: ReadonlySet<string>,
private readonly exclude: ReadonlySet<string>,
) { }
public isIncluded(test: vscode.TestItem<unknown>) {
for (let t: vscode.TestItem<unknown> | undefined = test; t; t = t.parent) {
if (this.include.has(t.id)) {
return true;
} else if (this.exclude.has(t.id)) {
return false;
}
}
return true;
}
}
class TestRunTask<T> implements vscode.TestRun<T> {
readonly #proxy: MainThreadTestingShape;
readonly #req: TestRunDto;
readonly #taskId = generateUuid();
readonly #sharedIds: Set<string>;
public readonly onEnd = new Barrier();
constructor(
public readonly name: string | undefined,
dto: TestRunDto,
sharedTestIds: Set<string>,
proxy: MainThreadTestingShape,
) {
this.#proxy = proxy;
this.#req = dto;
this.#sharedIds = sharedTestIds;
proxy.$startedTestRunTask(dto.id, { id: this.#taskId, name, running: true });
}
setState(test: vscode.TestItem<T>, state: vscode.TestResultState, duration?: number): void {
if (this.#req.isIncluded(test)) {
this.ensureTestIsKnown(test);
this.#proxy.$updateTestStateInRun(this.#req.id, this.#taskId, test.id, state, duration);
}
}
appendMessage(test: vscode.TestItem<T>, message: vscode.TestMessage): void {
if (this.#req.isIncluded(test)) {
this.ensureTestIsKnown(test);
this.#proxy.$appendTestMessageInRun(this.#req.id, this.#taskId, test.id, Convert.TestMessage.from(message));
}
}
appendOutput(output: string): void {
this.#proxy.$appendOutputToRun(this.#req.id, this.#taskId, VSBuffer.fromString(output));
}
end(): void {
this.#proxy.$finishedTestRunTask(this.#req.id, this.#taskId);
this.onEnd.open();
}
private ensureTestIsKnown(test: vscode.TestItem<T>) {
const sent = this.#sharedIds;
if (sent.has(test.id)) {
return;
}
const chain: ITestItem[] = [];
while (true) {
chain.unshift(Convert.TestItem.from(test));
if (sent.has(test.id)) {
break;
}
sent.add(test.id);
if (!test.parent) {
break;
}
test = test.parent;
}
this.#proxy.$addTestsToRun(this.#req.id, chain);
}
}
export const createDefaultDocumentTestRoot = async <T>(
provider: vscode.TestController<T>,
document: vscode.TextDocument,
folder: vscode.WorkspaceFolder | undefined,
token: CancellationToken,
@@ -333,7 +544,7 @@ export const createDefaultDocumentTestRoot = async <T extends vscode.TestItem>(
return;
}
const root = await provider.provideWorkspaceTestRoot(folder, token);
const root = await provider.createWorkspaceTestRoot(folder, token);
if (!root) {
return;
}
@@ -342,32 +553,35 @@ export const createDefaultDocumentTestRoot = async <T extends vscode.TestItem>(
TestItemFilteredWrapper.removeFilter(document);
});
return TestItemFilteredWrapper.getWrapperForTestItem(root, document);
const wrapper = TestItemFilteredWrapper.getWrapperForTestItem(root, document);
wrapper.refreshMatch();
return wrapper;
};
/*
* A class which wraps a vscode.TestItem that provides the ability to filter a TestItem's children
* to only the children that are located in a certain vscode.Uri.
*/
export class TestItemFilteredWrapper<T extends vscode.TestItem = vscode.TestItem> extends TestItemImpl {
private static wrapperMap = new WeakMap<vscode.TextDocument, WeakMap<vscode.TestItem, TestItemFilteredWrapper>>();
export class TestItemFilteredWrapper extends TestItemImpl {
private static wrapperMap = new WeakMap<vscode.TextDocument, WeakMap<vscode.TestItem<unknown>, TestItemFilteredWrapper>>();
public static removeFilter(document: vscode.TextDocument): void {
this.wrapperMap.delete(document);
}
// Wraps the TestItem specified in a TestItemFilteredWrapper and pulls from a cache if it already exists.
public static getWrapperForTestItem<T extends vscode.TestItem>(
item: T,
public static getWrapperForTestItem(
item: vscode.TestItem<unknown>,
filterDocument: vscode.TextDocument,
parent?: TestItemFilteredWrapper<T>,
): TestItemFilteredWrapper<T> {
parent?: TestItemFilteredWrapper,
): TestItemFilteredWrapper {
let innerMap = this.wrapperMap.get(filterDocument);
if (innerMap?.has(item)) {
return innerMap.get(item) as TestItemFilteredWrapper<T>;
return innerMap.get(item) as TestItemFilteredWrapper;
}
if (!innerMap) {
innerMap = new WeakMap<vscode.TestItem, TestItemFilteredWrapper>();
innerMap = new WeakMap();
this.wrapperMap.set(filterDocument, innerMap);
}
@@ -380,8 +594,8 @@ export class TestItemFilteredWrapper<T extends vscode.TestItem = vscode.TestItem
* If the TestItem is wrapped, returns the unwrapped item provided
* by the extension.
*/
public static unwrap(item: vscode.TestItem) {
return item instanceof TestItemFilteredWrapper ? item.actual : item;
public static unwrap<T>(item: vscode.TestItem<T> | TestItemFilteredWrapper) {
return item instanceof TestItemFilteredWrapper ? item.actual as vscode.TestItem<T> : item;
}
private _cachedMatchesFilter: boolean | undefined;
@@ -398,21 +612,39 @@ export class TestItemFilteredWrapper<T extends vscode.TestItem = vscode.TestItem
}
private constructor(
public readonly actual: T,
public readonly actual: vscode.TestItem<unknown>,
private filterDocument: vscode.TextDocument,
public readonly parent?: TestItemFilteredWrapper<T>,
public readonly actualParent?: TestItemFilteredWrapper,
) {
super(actual.id, actual.label, actual.uri, actual.expandable);
super(actual.id, actual.label, actual.uri, undefined);
if (!(actual instanceof TestItemImpl)) {
throw new Error(`TestItems provided to the VS Code API must extend \`vscode.TestItem\`, but ${actual.id} did not`);
}
(actual as TestItemImpl)[TestItemHookProperty] = {
setProp: (key, value) => (this as Record<string, unknown>)[key] = value,
created: child => TestItemFilteredWrapper.getWrapperForTestItem(child, this.filterDocument, this).refreshMatch(),
invalidate: () => this.invalidate(),
delete: child => this.children.delete(child),
};
this.debuggable = actual.debuggable;
this.runnable = actual.runnable;
this.description = actual.description;
this.error = actual.error;
this.status = actual.status;
this.range = actual.range;
this.resolveHandler = actual.resolveHandler;
const wrapperApi = getPrivateApiFor(this);
const actualApi = getPrivateApiFor(actual);
actualApi.bus.event(evt => {
switch (evt[0]) {
case ExtHostTestItemEventType.SetProp:
(this as Record<string, unknown>)[evt[1]] = evt[2];
break;
case ExtHostTestItemEventType.NewChild:
const wrapper = TestItemFilteredWrapper.getWrapperForTestItem(evt[1], this.filterDocument, this);
getPrivateApiFor(wrapper).parent = actual;
wrapper.refreshMatch();
break;
default:
wrapperApi.bus.fire(evt);
}
});
}
/**
@@ -420,17 +652,17 @@ export class TestItemFilteredWrapper<T extends vscode.TestItem = vscode.TestItem
* if the test itself has a location that matches, or if any of its
* children do.
*/
private refreshMatch() {
public refreshMatch() {
const didMatch = this._cachedMatchesFilter;
// The `children` of the wrapper only include the children who match the
// filter. Synchronize them.
for (const rawChild of this.actual.children) {
for (const rawChild of this.actual.children.values()) {
const wrapper = TestItemFilteredWrapper.getWrapperForTestItem(rawChild, this.filterDocument, this);
if (wrapper.hasNodeMatchingFilter) {
this.children.add(wrapper);
} else {
this.children.delete(wrapper);
if (!wrapper.hasNodeMatchingFilter) {
wrapper.dispose();
} else if (!this.children.has(wrapper.id)) {
this.addChild(wrapper);
}
}
@@ -438,18 +670,26 @@ export class TestItemFilteredWrapper<T extends vscode.TestItem = vscode.TestItem
this._cachedMatchesFilter = nowMatches;
if (nowMatches !== didMatch) {
this.parent?.refreshMatch();
this.actualParent?.refreshMatch();
}
return this._cachedMatchesFilter;
}
public override dispose() {
if (this.actualParent) {
getPrivateApiFor(this.actualParent).children.delete(this.id);
}
getPrivateApiFor(this).bus.fire([ExtHostTestItemEventType.Disposed]);
}
}
/**
* @private
*/
interface MirroredCollectionTestItem extends IncrementalTestCollectionItem {
revived: vscode.TestItem;
revived: vscode.TestItem<never>;
depth: number;
}
@@ -464,21 +704,21 @@ class MirroredChangeCollector extends IncrementalChangeCollector<MirroredCollect
return this.added.size === 0 && this.removed.size === 0 && this.updated.size === 0;
}
constructor(private readonly emitter: Emitter<vscode.TestChangeEvent>) {
constructor(private readonly emitter: Emitter<vscode.TestsChangeEvent>) {
super();
}
/**
* @override
*/
public add(node: MirroredCollectionTestItem): void {
public override add(node: MirroredCollectionTestItem): void {
this.added.add(node);
}
/**
* @override
*/
public update(node: MirroredCollectionTestItem): void {
public override update(node: MirroredCollectionTestItem): void {
Object.assign(node.revived, Convert.TestItem.toPlain(node.item));
if (!this.added.has(node)) {
this.updated.add(node);
@@ -488,7 +728,7 @@ class MirroredChangeCollector extends IncrementalChangeCollector<MirroredCollect
/**
* @override
*/
public remove(node: MirroredCollectionTestItem): void {
public override remove(node: MirroredCollectionTestItem): void {
if (this.added.has(node)) {
this.added.delete(node);
return;
@@ -507,7 +747,7 @@ class MirroredChangeCollector extends IncrementalChangeCollector<MirroredCollect
/**
* @override
*/
public getChangeEvent(): vscode.TestChangeEvent {
public getChangeEvent(): vscode.TestsChangeEvent {
const { added, updated, removed } = this;
return {
get added() { return [...added].map(n => n.revived); },
@@ -516,7 +756,7 @@ class MirroredChangeCollector extends IncrementalChangeCollector<MirroredCollect
};
}
public complete() {
public override complete() {
if (!this.isEmpty) {
this.emitter.fire(this.getChangeEvent());
}
@@ -528,7 +768,7 @@ class MirroredChangeCollector extends IncrementalChangeCollector<MirroredCollect
* @private
*/
export class MirroredTestCollection extends AbstractIncrementalTestCollection<MirroredCollectionTestItem> {
private changeEmitter = new Emitter<vscode.TestChangeEvent>();
private changeEmitter = new Emitter<vscode.TestsChangeEvent>();
/**
* Change emitter that fires with the same sematics as `TestObserver.onDidChangeTests`.
@@ -546,7 +786,7 @@ export class MirroredTestCollection extends AbstractIncrementalTestCollection<Mi
* Translates the item IDs to TestItems for exposure to extensions.
*/
public getAllAsTestItem(itemIds: Iterable<string>) {
let output: vscode.TestItem[] = [];
let output: vscode.TestItem<never>[] = [];
for (const itemId of itemIds) {
const item = this.items.get(itemId);
if (item) {
@@ -568,7 +808,7 @@ export class MirroredTestCollection extends AbstractIncrementalTestCollection<Mi
/**
* If the test item is a mirrored test item, returns its underlying ID.
*/
public getMirroredTestDataByReference(item: vscode.TestItem) {
public getMirroredTestDataByReference(item: vscode.TestItem<unknown>) {
return this.items.get(item.id);
}
@@ -579,7 +819,7 @@ export class MirroredTestCollection extends AbstractIncrementalTestCollection<Mi
return {
...item,
// todo@connor4312: make this work well again with children
revived: Convert.TestItem.toPlain(item.item) as vscode.TestItem,
revived: Convert.TestItem.toPlain(item.item) as vscode.TestItem<never>,
depth: parent ? parent.depth + 1 : 0,
children: new Set(),
};
@@ -588,7 +828,7 @@ export class MirroredTestCollection extends AbstractIncrementalTestCollection<Mi
/**
* @override
*/
protected createChangeCollector() {
protected override createChangeCollector() {
return new MirroredChangeCollector(this.changeEmitter);
}
}
@@ -628,7 +868,7 @@ abstract class AbstractTestObserverFactory {
/**
* Gets the internal test data by its reference, in any observer.
*/
public getMirroredTestDataByReference(ref: vscode.TestItem) {
public getMirroredTestDataByReference(ref: vscode.TestItem<unknown>) {
for (const { tests } of this.resources.values()) {
const v = tests.getMirroredTestDataByReference(ref);
if (v) {

View File

@@ -0,0 +1,44 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Emitter } from 'vs/base/common/event';
import { TestItemImpl } from 'vs/workbench/api/common/extHostTypes';
import * as vscode from 'vscode';
export const enum ExtHostTestItemEventType {
NewChild,
Disposed,
Invalidated,
SetProp,
}
export type ExtHostTestItemEvent =
| [evt: ExtHostTestItemEventType.NewChild, item: TestItemImpl]
| [evt: ExtHostTestItemEventType.Disposed]
| [evt: ExtHostTestItemEventType.Invalidated]
| [evt: ExtHostTestItemEventType.SetProp, key: keyof vscode.TestItem<never>, value: any];
export interface IExtHostTestItemApi {
children: Map<string, TestItemImpl>;
parent?: TestItemImpl;
bus: Emitter<ExtHostTestItemEvent>;
}
const eventPrivateApis = new WeakMap<TestItemImpl, IExtHostTestItemApi>();
/**
* Gets the private API for a test item implementation. This implementation
* is a managed object, but we keep a weakmap to avoid exposing any of the
* internals to extensions.
*/
export const getPrivateApiFor = (impl: TestItemImpl) => {
let api = eventPrivateApis.get(impl);
if (!api) {
api = { children: new Map(), bus: new Emitter() };
eventPrivateApis.set(impl, api);
}
return api;
};

View File

@@ -497,12 +497,12 @@ class ExtHostTreeView<T> extends Disposable {
private getHandlesToRefresh(elements: T[]): TreeItemHandle[] {
const elementsToUpdate = new Set<TreeItemHandle>();
for (const element of elements) {
const elementNode = this.nodes.get(element);
const elementNodes = elements.map(element => this.nodes.get(element));
for (const elementNode of elementNodes) {
if (elementNode && !elementsToUpdate.has(elementNode.item.handle)) {
// check if an ancestor of extElement is already in the elements to update list
// check if an ancestor of extElement is already in the elements list
let currentNode: TreeNode | undefined = elementNode;
while (currentNode && currentNode.parent && !elementsToUpdate.has(currentNode.parent.item.handle)) {
while (currentNode && currentNode.parent && elementNodes.findIndex(node => currentNode && currentNode.parent && node && node.item.handle === currentNode.parent.item.handle) === -1) {
const parentElement: T | undefined = this.elements.get(currentNode.parent.item.handle);
currentNode = parentElement ? this.nodes.get(parentElement) : undefined;
}
@@ -749,7 +749,7 @@ class ExtHostTreeView<T> extends Disposable {
this.nodes.clear();
}
dispose() {
override dispose() {
this._refreshCancellationSource.dispose();
this.clearAll();

View File

@@ -46,7 +46,7 @@ export interface IExtHostTunnelService extends ExtHostTunnelServiceShape {
getTunnels(): Promise<vscode.TunnelDescription[]>;
onDidChangeTunnels: vscode.Event<void>;
setTunnelExtensionFunctions(provider: vscode.RemoteAuthorityResolver | undefined): Promise<IDisposable>;
registerPortsAttributesProvider(portSelector: { pid?: number, portRange?: [number, number] }, provider: vscode.PortAttributesProvider): IDisposable;
registerPortsAttributesProvider(portSelector: { pid?: number, portRange?: [number, number], commandMatcher?: RegExp }, provider: vscode.PortAttributesProvider): IDisposable;
}
export const IExtHostTunnelService = createDecorator<IExtHostTunnelService>('IExtHostTunnelService');

View File

@@ -5,6 +5,7 @@
import { coalesce, isNonEmptyArray } from 'vs/base/common/arrays';
import * as htmlContent from 'vs/base/common/htmlContent';
import { DisposableStore } from 'vs/base/common/lifecycle';
import * as marked from 'vs/base/common/marked/marked';
import { parse } from 'vs/base/common/marshalling';
import { cloneAndChange } from 'vs/base/common/objects';
@@ -27,8 +28,9 @@ import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocum
import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook';
import { EditorGroupColumn, SaveReason } from 'vs/workbench/common/editor';
import * as notebooks from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange';
import * as search from 'vs/workbench/contrib/search/common/search';
import { ISerializedTestResults, ITestItem, ITestMessage, ITestState, SerializedTestResultItem, TestItemExpandState } from 'vs/workbench/contrib/testing/common/testCollection';
import { ISerializedTestResults, ITestItem, ITestMessage, SerializedTestResultItem } from 'vs/workbench/contrib/testing/common/testCollection';
import { ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import type * as vscode from 'vscode';
import * as types from './extHostTypes';
@@ -543,7 +545,7 @@ export namespace WorkspaceEdit {
resource: entry.uri,
edit: entry.edit,
notebookMetadata: entry.notebookMetadata,
notebookVersionId: extHostNotebooks?.lookupNotebookDocument(entry.uri)?.notebookDocument.version
notebookVersionId: extHostNotebooks?.lookupNotebookDocument(entry.uri)?.apiNotebook.version
});
} else if (entry._type === types.FileEditType.CellOutput) {
@@ -578,7 +580,7 @@ export namespace WorkspaceEdit {
_type: extHostProtocol.WorkspaceEditType.Cell,
metadata: entry.metadata,
resource: entry.uri,
notebookVersionId: extHostNotebooks?.lookupNotebookDocument(entry.uri)?.notebookDocument.version,
notebookVersionId: extHostNotebooks?.lookupNotebookDocument(entry.uri)?.apiNotebook.version,
edit: {
editType: notebooks.CellEditType.Replace,
index: entry.index,
@@ -1404,21 +1406,31 @@ export namespace LanguageSelector {
}
}
export namespace NotebookCellRange {
export namespace NotebookRange {
export function from(range: vscode.NotebookCellRange): notebooks.ICellRange {
export function from(range: vscode.NotebookRange): ICellRange {
return { start: range.start, end: range.end };
}
export function to(range: notebooks.ICellRange): types.NotebookCellRange {
return new types.NotebookCellRange(range.start, range.end);
export function to(range: ICellRange): types.NotebookRange {
return new types.NotebookRange(range.start, range.end);
}
}
export namespace NotebookCellMetadata {
export function to(data: notebooks.NotebookCellMetadata): types.NotebookCellMetadata {
return new types.NotebookCellMetadata(data.editable, data.breakpointMargin, data.hasExecutionOrder, data.statusMessage, data.inputCollapsed, data.outputCollapsed, data.custom);
return new types.NotebookCellMetadata().with({
...data,
...{
executionOrder: null,
lastRunSuccess: null,
runState: null,
runStartTime: null,
runStartTimeAdjustment: null,
runEndTime: null
}
});
}
}
@@ -1429,14 +1441,15 @@ export namespace NotebookDocumentMetadata {
}
export function to(data: notebooks.NotebookDocumentMetadata): types.NotebookDocumentMetadata {
return new types.NotebookDocumentMetadata(data.editable, data.cellEditable, data.cellHasExecutionOrder, data.custom, data.trusted);
return new types.NotebookDocumentMetadata().with(data);
}
}
export namespace NotebookCellPreviousExecutionResult {
export function to(data: notebooks.NotebookCellMetadata): vscode.NotebookCellExecutionSummary {
return {
duration: data.lastRunDuration,
startTime: data.runStartTime,
endTime: data.runEndTime,
executionOrder: data.executionOrder,
success: data.lastRunSuccess
};
@@ -1445,7 +1458,8 @@ export namespace NotebookCellPreviousExecutionResult {
export function from(data: vscode.NotebookCellExecutionSummary): Partial<notebooks.NotebookCellMetadata> {
return {
lastRunSuccess: data.success,
lastRunDuration: data.duration,
runStartTime: data.startTime,
runEndTime: data.endTime,
executionOrder: data.executionOrder
};
}
@@ -1612,38 +1626,52 @@ export namespace NotebookDecorationRenderOptions {
}
}
export namespace NotebookDocumentContentOptions {
export function from(options: vscode.NotebookDocumentContentOptions | undefined): notebooks.TransientOptions {
export namespace NotebookStatusBarItem {
export function from(item: vscode.NotebookCellStatusBarItem, commandsConverter: CommandsConverter, disposables: DisposableStore): notebooks.INotebookCellStatusBarItem {
const command = typeof item.command === 'string' ? { title: '', command: item.command } : item.command;
return {
transientOutputs: options ? options.transientOutputs : false,
transientMetadata: {
...(options?.transientMetadata ?? {}),
...{
executionOrder: true,
lastRunDuration: true,
runState: true,
runStartTime: true,
lastRunSuccess: true
}
}
alignment: item.alignment === types.NotebookCellStatusBarAlignment.Left ? notebooks.CellStatusbarAlignment.Left : notebooks.CellStatusbarAlignment.Right,
command: commandsConverter.toInternal(command, disposables), // TODO@roblou
text: item.text,
tooltip: item.tooltip,
accessibilityInformation: item.accessibilityInformation,
priority: item.priority
};
}
}
export namespace TestState {
export function from(item: vscode.TestState): ITestState {
export namespace NotebookDocumentContentOptions {
export function from(options: vscode.NotebookDocumentContentOptions | undefined): notebooks.TransientOptions {
return {
state: item.state,
duration: item.duration,
messages: item.messages.map(TestMessage.from),
transientOutputs: options?.transientOutputs ?? false,
transientCellMetadata: {
...options?.transientCellMetadata,
executionOrder: true,
runState: true,
runStartTime: true,
runStartTimeAdjustment: true,
runEndTime: true,
lastRunSuccess: true
},
transientDocumentMetadata: options?.transientDocumentMetadata ?? {}
};
}
}
export function to(item: ITestState): vscode.TestState {
const ts = new types.TestState(item.state);
ts.duration = item.duration;
ts.messages = item.messages.map(TestMessage.to);
return ts;
export namespace NotebookKernelPreload {
export function from(preload: vscode.NotebookKernelPreload): { uri: UriComponents; provides: string[] } {
return {
uri: preload.uri,
provides: typeof preload.provides === 'string'
? [preload.provides]
: preload.provides ?? []
};
}
export function to(preload: { uri: UriComponents; provides: string[] }): vscode.NotebookKernelPreload {
return {
uri: URI.revive(preload.uri),
provides: preload.provides
};
}
}
@@ -1668,18 +1696,18 @@ export namespace TestMessage {
}
export namespace TestItem {
export type Raw = vscode.TestItem;
export type Raw<T = unknown> = vscode.TestItem<T>;
export function from(item: vscode.TestItem): ITestItem {
export function from(item: vscode.TestItem<unknown>): ITestItem {
return {
extId: item.id,
label: item.label,
uri: item.uri,
range: Range.from(item.range),
range: Range.from(item.range) || null,
debuggable: item.debuggable ?? false,
description: item.description,
description: item.description || null,
runnable: item.runnable ?? true,
expandable: item.expandable,
error: item.error ? (MarkdownString.fromStrict(item.error) || null) : null,
};
}
@@ -1688,84 +1716,55 @@ export namespace TestItem {
extId: item.id,
label: item.label,
uri: item.uri,
range: Range.from(item.range),
range: Range.from(item.range) || null,
debuggable: false,
description: item.description,
description: item.description || null,
error: null,
runnable: true,
expandable: true,
};
}
export function toPlain(item: ITestItem): Omit<vscode.TestItem, 'children' | 'invalidate' | 'discoverChildren'> {
export function toPlain(item: ITestItem): Omit<vscode.TestItem<never>, 'children' | 'invalidate' | 'discoverChildren'> {
return {
id: item.extId,
label: item.label,
uri: URI.revive(item.uri),
range: Range.to(item.range),
expandable: item.expandable,
range: Range.to(item.range || undefined),
addChild: () => undefined,
dispose: () => undefined,
status: types.TestItemStatus.Pending,
data: undefined as never,
debuggable: item.debuggable,
description: item.description,
description: item.description || undefined,
runnable: item.runnable,
};
}
export function to(item: ITestItem): types.TestItem {
const testItem = new types.TestItem(item.extId, item.label, URI.revive(item.uri), item.expandable);
testItem.range = Range.to(item.range);
export function to(item: ITestItem): types.TestItemImpl {
const testItem = new types.TestItemImpl(item.extId, item.label, URI.revive(item.uri), undefined);
testItem.range = Range.to(item.range || undefined);
testItem.debuggable = item.debuggable;
testItem.description = item.description;
testItem.description = item.description || undefined;
testItem.runnable = item.runnable;
return testItem;
}
}
export namespace TestResults {
export function from(id: string, results: vscode.TestResults): ISerializedTestResults {
const serialized: ISerializedTestResults = {
completedAt: results.completedAt,
id,
items: [],
};
const queue: [parent: SerializedTestResultItem | null, children: Iterable<vscode.TestResultSnapshot>][] = [
[null, results.results],
];
while (queue.length) {
const [parent, children] = queue.pop()!;
for (const item of children) {
const serializedItem: SerializedTestResultItem = {
children: item.children?.map(c => c.id) ?? [],
computedState: item.result.state,
item: TestItem.fromResultSnapshot(item),
state: TestState.from(item.result),
retired: undefined,
expand: TestItemExpandState.Expanded,
parent: parent?.item.extId ?? null,
src: { provider: '', tree: -1 },
direct: !parent,
};
serialized.items.push(serializedItem);
if (item.children) {
queue.push([serializedItem, item.children]);
}
}
}
return serialized;
}
const convertTestResultItem = (item: SerializedTestResultItem, byInternalId: Map<string, SerializedTestResultItem>): vscode.TestResultSnapshot => ({
...TestItem.toPlain(item.item),
result: TestState.to(item.state),
taskStates: item.tasks.map(t => ({
state: t.state,
duration: t.duration,
messages: t.messages.map(TestMessage.to),
})),
children: item.children
.map(c => byInternalId.get(c))
.filter(isDefined)
.map(c => convertTestResultItem(c, byInternalId)),
});
export function to(serialized: ISerializedTestResults): vscode.TestResults {
export function to(serialized: ISerializedTestResults): vscode.TestRunResult {
const roots: SerializedTestResultItem[] = [];
const byInternalId = new Map<string, SerializedTestResultItem>();
for (const item of serialized.items) {

View File

@@ -7,12 +7,14 @@ import { coalesceInPlace, equals } from 'vs/base/common/arrays';
import { illegalArgument } from 'vs/base/common/errors';
import { IRelativePattern } from 'vs/base/common/glob';
import { isMarkdownString, MarkdownString as BaseMarkdownString } from 'vs/base/common/htmlContent';
import { ResourceMap } from 'vs/base/common/map';
import { ReadonlyMapView, ResourceMap } from 'vs/base/common/map';
import { isFalsyOrWhitespace } from 'vs/base/common/strings';
import { isStringArray } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid';
import { FileSystemProviderErrorCode, markAsFileSystemProviderError } from 'vs/platform/files/common/files';
import { RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { getPrivateApiFor, ExtHostTestItemEventType, IExtHostTestItemApi } from 'vs/workbench/api/common/extHostTestingPrivateApi';
import { CellEditType, ICellEditOperation } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import type * as vscode from 'vscode';
@@ -419,7 +421,7 @@ export class Selection extends Range {
return this._anchor === this._end;
}
toJSON() {
override toJSON() {
return {
start: this.start,
end: this.end,
@@ -676,34 +678,33 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit {
this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.DocumentMetadata, metadata: value }, notebookMetadata: value });
}
replaceNotebookCells(uri: URI, start: number, end: number, cells: vscode.NotebookCellData[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
if (start !== end || cells.length > 0) {
this._edits.push({ _type: FileEditType.CellReplace, uri, index: start, count: end - start, cells, metadata });
replaceNotebookCells(uri: URI, range: vscode.NotebookRange, cells: vscode.NotebookCellData[], metadata?: vscode.WorkspaceEditEntryMetadata): void;
replaceNotebookCells(uri: URI, start: number, end: number, cells: vscode.NotebookCellData[], metadata?: vscode.WorkspaceEditEntryMetadata): void;
replaceNotebookCells(uri: URI, startOrRange: number | vscode.NotebookRange, endOrCells: number | vscode.NotebookCellData[], cellsOrMetadata?: vscode.NotebookCellData[] | vscode.WorkspaceEditEntryMetadata, metadata?: vscode.WorkspaceEditEntryMetadata): void {
let start: number | undefined;
let end: number | undefined;
let cellData: vscode.NotebookCellData[] = [];
let workspaceEditMetadata: vscode.WorkspaceEditEntryMetadata | undefined;
if (NotebookRange.isNotebookRange(startOrRange) && NotebookCellData.isNotebookCellDataArray(endOrCells) && !NotebookCellData.isNotebookCellDataArray(cellsOrMetadata)) {
start = startOrRange.start;
end = startOrRange.end;
cellData = endOrCells;
workspaceEditMetadata = cellsOrMetadata;
} else if (typeof startOrRange === 'number' && typeof endOrCells === 'number' && NotebookCellData.isNotebookCellDataArray(cellsOrMetadata)) {
start = startOrRange;
end = endOrCells;
cellData = cellsOrMetadata;
workspaceEditMetadata = metadata;
}
}
replaceNotebookCellOutput(uri: URI, index: number, outputs: vscode.NotebookCellOutput[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
this._editNotebookCellOutput(uri, index, false, outputs, metadata);
}
if (start === undefined || end === undefined) {
throw new Error('Invalid arguments');
}
appendNotebookCellOutput(uri: URI, index: number, outputs: vscode.NotebookCellOutput[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
this._editNotebookCellOutput(uri, index, true, outputs, metadata);
}
replaceNotebookCellOutputItems(uri: URI, index: number, outputId: string, items: NotebookCellOutputItem[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
this._editNotebookCellOutputItems(uri, index, outputId, false, items, metadata);
}
appendNotebookCellOutputItems(uri: URI, index: number, outputId: string, items: NotebookCellOutputItem[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
this._editNotebookCellOutputItems(uri, index, outputId, true, items, metadata);
}
private _editNotebookCellOutputItems(uri: URI, index: number, id: string, append: boolean, items: vscode.NotebookCellOutputItem[], metadata: vscode.WorkspaceEditEntryMetadata | undefined): void {
this._edits.push({ _type: FileEditType.CellOutputItem, metadata, uri, index, outputId: id, append, newOutputItems: items });
}
private _editNotebookCellOutput(uri: URI, index: number, append: boolean, outputs: vscode.NotebookCellOutput[], metadata: vscode.WorkspaceEditEntryMetadata | undefined): void {
this._edits.push({ _type: FileEditType.CellOutput, metadata, uri, index, append, newOutputs: outputs, newMetadata: undefined });
if (start !== end || cellData.length > 0) {
this._edits.push({ _type: FileEditType.CellReplace, uri, index: start, count: end - start, cells: cellData, metadata: workspaceEditMetadata });
}
}
replaceNotebookCellMetadata(uri: URI, index: number, cellMetadata: vscode.NotebookCellMetadata, metadata?: vscode.WorkspaceEditEntryMetadata): void {
@@ -2895,7 +2896,17 @@ export enum ColorThemeKind {
//#region Notebook
export class NotebookCellRange {
export class NotebookRange {
static isNotebookRange(thing: any): thing is vscode.NotebookRange {
if (thing instanceof NotebookRange) {
return true;
}
if (!thing) {
return false;
}
return typeof (<NotebookRange>thing).start === 'number'
&& typeof (<NotebookRange>thing).end === 'number';
}
private _start: number;
private _end: number;
@@ -2916,58 +2927,59 @@ export class NotebookCellRange {
if (start < 0) {
throw illegalArgument('start must be positive');
}
if (end < start) {
throw illegalArgument('end cannot be smaller than start');
if (end < 0) {
throw illegalArgument('end must be positive');
}
this._start = start;
this._end = end;
if (start <= end) {
this._start = start;
this._end = end;
} else {
this._start = end;
this._end = start;
}
}
with(change: { start?: number, end?: number }): NotebookRange {
let start = this._start;
let end = this._end;
if (change.start !== undefined) {
start = change.start;
}
if (change.end !== undefined) {
end = change.end;
}
if (start === this._start && end === this._end) {
return this;
}
return new NotebookRange(start, end);
}
}
export class NotebookCellMetadata {
readonly inputCollapsed?: boolean;
readonly outputCollapsed?: boolean;
readonly [key: string]: any;
constructor(
readonly editable?: boolean,
readonly breakpointMargin?: boolean,
readonly hasExecutionOrder?: boolean,
readonly statusMessage?: string,
readonly inputCollapsed?: boolean,
readonly outputCollapsed?: boolean,
readonly custom?: Record<string, any>,
) { }
constructor(inputCollapsed?: boolean, outputCollapsed?: boolean);
constructor(data: Record<string, any>);
constructor(inputCollapsedOrData: (boolean | undefined) | Record<string, any>, outputCollapsed?: boolean) {
if (typeof inputCollapsedOrData === 'object') {
Object.assign(this, inputCollapsedOrData);
} else {
this.inputCollapsed = inputCollapsedOrData;
this.outputCollapsed = outputCollapsed;
}
}
with(change: {
editable?: boolean | null,
breakpointMargin?: boolean | null,
hasExecutionOrder?: boolean | null,
statusMessage?: string | null,
inputCollapsed?: boolean | null,
outputCollapsed?: boolean | null,
custom?: Record<string, any> | null,
[key: string]: any
}): NotebookCellMetadata {
let { editable, breakpointMargin, hasExecutionOrder, statusMessage, inputCollapsed, outputCollapsed, custom } = change;
let { inputCollapsed, outputCollapsed, ...remaining } = change;
if (editable === undefined) {
editable = this.editable;
} else if (editable === null) {
editable = undefined;
}
if (breakpointMargin === undefined) {
breakpointMargin = this.breakpointMargin;
} else if (breakpointMargin === null) {
breakpointMargin = undefined;
}
if (hasExecutionOrder === undefined) {
hasExecutionOrder = this.hasExecutionOrder;
} else if (hasExecutionOrder === null) {
hasExecutionOrder = undefined;
}
if (statusMessage === undefined) {
statusMessage = this.statusMessage;
} else if (statusMessage === null) {
statusMessage = undefined;
}
if (inputCollapsed === undefined) {
inputCollapsed = this.inputCollapsed;
} else if (inputCollapsed === null) {
@@ -2978,103 +2990,78 @@ export class NotebookCellMetadata {
} else if (outputCollapsed === null) {
outputCollapsed = undefined;
}
if (custom === undefined) {
custom = this.custom;
} else if (custom === null) {
custom = undefined;
}
if (editable === this.editable &&
breakpointMargin === this.breakpointMargin &&
hasExecutionOrder === this.hasExecutionOrder &&
statusMessage === this.statusMessage &&
inputCollapsed === this.inputCollapsed &&
if (inputCollapsed === this.inputCollapsed &&
outputCollapsed === this.outputCollapsed &&
custom === this.custom
Object.keys(remaining).length === 0
) {
return this;
}
return new NotebookCellMetadata(
editable,
breakpointMargin,
hasExecutionOrder,
statusMessage,
inputCollapsed,
outputCollapsed,
custom,
{
inputCollapsed,
outputCollapsed,
...remaining
}
);
}
}
export class NotebookDocumentMetadata {
readonly trusted: boolean;
readonly [key: string]: any;
constructor(
readonly editable: boolean = true,
readonly cellEditable: boolean = true,
readonly cellHasExecutionOrder: boolean = true,
readonly custom: { [key: string]: any; } = {},
readonly trusted: boolean = true,
) { }
constructor(trusted?: boolean);
constructor(data: Record<string, any>);
constructor(trustedOrData: boolean | Record<string, any> = true) {
if (typeof trustedOrData === 'object') {
Object.assign(this, trustedOrData);
this.trusted = trustedOrData.trusted ?? true;
} else {
this.trusted = trustedOrData;
}
}
with(change: {
editable?: boolean | null,
cellEditable?: boolean | null,
cellHasExecutionOrder?: boolean | null,
custom?: { [key: string]: any; } | null,
trusted?: boolean | null,
[key: string]: any
}): NotebookDocumentMetadata {
let { editable, cellEditable, cellHasExecutionOrder, custom, trusted } = change;
let { trusted, ...remaining } = change;
if (editable === undefined) {
editable = this.editable;
} else if (editable === null) {
editable = undefined;
}
if (cellEditable === undefined) {
cellEditable = this.cellEditable;
} else if (cellEditable === null) {
cellEditable = undefined;
}
if (cellHasExecutionOrder === undefined) {
cellHasExecutionOrder = this.cellHasExecutionOrder;
} else if (cellHasExecutionOrder === null) {
cellHasExecutionOrder = undefined;
}
if (custom === undefined) {
custom = this.custom;
} else if (custom === null) {
custom = undefined;
}
if (trusted === undefined) {
trusted = this.trusted;
} else if (trusted === null) {
trusted = undefined;
}
if (editable === this.editable &&
cellEditable === this.cellEditable &&
cellHasExecutionOrder === this.cellHasExecutionOrder &&
custom === this.custom &&
trusted === this.trusted
if (trusted === this.trusted &&
Object.keys(remaining).length === 0
) {
return this;
}
return new NotebookDocumentMetadata(
editable,
cellEditable,
cellHasExecutionOrder,
custom,
trusted
{
trusted,
...remaining
}
);
}
}
export class NotebookCellData {
static isNotebookCellDataArray(value: unknown): value is vscode.NotebookCellData[] {
return Array.isArray(value) && (<unknown[]>value).every(elem => NotebookCellData.isNotebookCellData(elem));
}
static isNotebookCellData(value: unknown): value is vscode.NotebookCellData {
// return value instanceof NotebookCellData;
return true;
}
kind: NotebookCellKind;
source: string;
language: string;
@@ -3111,17 +3098,21 @@ export class NotebookCellOutputItem {
}
constructor(
readonly mime: string,
readonly value: unknown, // JSON'able
readonly metadata?: Record<string, any>
) { }
public mime: string,
public value: unknown, // JSON'able
public metadata?: Record<string, any>
) {
if (isFalsyOrWhitespace(this.mime)) {
throw new Error('INVALID mime type, must not be empty or falsy');
}
}
}
export class NotebookCellOutput {
readonly outputs: NotebookCellOutputItem[];
readonly id: string;
readonly metadata?: Record<string, any>;
id: string;
outputs: NotebookCellOutputItem[];
metadata?: Record<string, any>;
constructor(
outputs: NotebookCellOutputItem[],
@@ -3162,6 +3153,21 @@ export enum NotebookEditorRevealType {
AtTop = 3
}
export class NotebookCellStatusBarItem {
constructor(
public text: string,
public alignment: NotebookCellStatusBarAlignment,
public command?: string | vscode.Command,
public tooltip?: string,
public priority?: number,
public accessibilityInformation?: vscode.AccessibilityInformation) { }
}
export enum NotebookControllerAffinity {
Default = 1,
Preferred = 2
}
//#endregion
@@ -3223,7 +3229,7 @@ export class LinkedEditingRanges {
}
//#region Testing
export enum TestResult {
export enum TestResultState {
Unset = 0,
Queued = 1,
Running = 2,
@@ -3240,16 +3246,17 @@ export enum TestMessageSeverity {
Hint = 3
}
export const TestItemHookProperty = Symbol('TestItemHookProperty');
export interface ITestItemHook {
created(item: vscode.TestItem): void;
setProp<K extends keyof vscode.TestItem>(key: K, value: vscode.TestItem[K]): void;
invalidate(id: string): void;
delete(id: string): void;
export enum TestItemStatus {
Pending = 0,
Resolved = 1,
}
const testItemPropAccessor = <K extends keyof vscode.TestItem>(item: TestItem, key: K, defaultValue: vscode.TestItem[K]) => {
const testItemPropAccessor = <K extends keyof vscode.TestItem<never>>(
api: IExtHostTestItemApi,
key: K,
defaultValue: vscode.TestItem<never>[K],
equals: (a: vscode.TestItem<never>[K], b: vscode.TestItem<never>[K]) => boolean
) => {
let value = defaultValue;
return {
enumerable: true,
@@ -3257,80 +3264,41 @@ const testItemPropAccessor = <K extends keyof vscode.TestItem>(item: TestItem, k
get() {
return value;
},
set(newValue: vscode.TestItem[K]) {
item[TestItemHookProperty]?.setProp(key, newValue);
value = newValue;
set(newValue: vscode.TestItem<never>[K]) {
if (!equals(value, newValue)) {
value = newValue;
api.bus.fire([ExtHostTestItemEventType.SetProp, key, newValue]);
}
},
};
};
export class TestChildrenCollection implements vscode.TestChildrenCollection<vscode.TestItem> {
#map = new Map<string, vscode.TestItem>();
#hookRef: () => ITestItemHook | undefined;
const strictEqualComparator = <T>(a: T, b: T) => a === b;
const rangeComparator = (a: vscode.Range | undefined, b: vscode.Range | undefined) => {
if (a === b) { return true; }
if (!a || !b) { return false; }
return a.isEqual(b);
};
public get size() {
return this.#map.size;
}
export class TestItemImpl implements vscode.TestItem<unknown> {
public readonly id!: string;
public readonly uri!: vscode.Uri;
public readonly children!: ReadonlyMap<string, TestItemImpl>;
public readonly parent!: TestItemImpl | undefined;
constructor(hookRef: () => ITestItemHook | undefined) {
this.#hookRef = hookRef;
}
public add(child: vscode.TestItem) {
const map = this.#map;
const hook = this.#hookRef();
const existing = map.get(child.id);
if (existing === child) {
return;
}
if (existing) {
hook?.delete(child.id);
}
map.set(child.id, child);
hook?.created(child);
}
public get(id: string) {
return this.#map.get(id);
}
public clear() {
for (const key of this.#map.keys()) {
this.delete(key);
}
}
public delete(childOrId: vscode.TestItem | string) {
const id = typeof childOrId === 'string' ? childOrId : childOrId.id;
if (this.#map.has(id)) {
this.#map.delete(id);
this.#hookRef()?.delete(id);
}
}
public toJSON() {
return [...this.#map.values()];
}
public [Symbol.iterator]() {
return this.#map.values();
}
}
export class TestItem implements vscode.TestItem {
public id!: string;
public range!: vscode.Range | undefined;
public description!: string | undefined;
public runnable!: boolean;
public debuggable!: boolean;
public children!: TestChildrenCollection;
public uri!: vscode.Uri;
public [TestItemHookProperty]!: ITestItemHook | undefined;
public error!: string | vscode.MarkdownString;
public status!: vscode.TestItemStatus;
/** Extension-owned resolve handler */
public resolveHandler?: (token: vscode.CancellationToken) => void;
constructor(id: string, public label: string, uri: vscode.Uri, public data: unknown) {
const api = getPrivateApiFor(this);
constructor(id: string, public label: string, uri: vscode.Uri, public expandable: boolean) {
Object.defineProperties(this, {
id: {
value: id,
@@ -3342,38 +3310,52 @@ export class TestItem implements vscode.TestItem {
enumerable: true,
writable: false,
},
parent: {
enumerable: false,
get: () => api.parent,
},
children: {
value: new TestChildrenCollection(() => this[TestItemHookProperty]),
value: new ReadonlyMapView(api.children),
enumerable: true,
writable: false,
},
[TestItemHookProperty]: {
enumerable: false,
writable: true,
configurable: false,
},
range: testItemPropAccessor(this, 'range', undefined),
description: testItemPropAccessor(this, 'description', undefined),
runnable: testItemPropAccessor(this, 'runnable', true),
debuggable: testItemPropAccessor(this, 'debuggable', true),
range: testItemPropAccessor(api, 'range', undefined, rangeComparator),
description: testItemPropAccessor(api, 'description', undefined, strictEqualComparator),
runnable: testItemPropAccessor(api, 'runnable', true, strictEqualComparator),
debuggable: testItemPropAccessor(api, 'debuggable', true, strictEqualComparator),
status: testItemPropAccessor(api, 'status', TestItemStatus.Resolved, strictEqualComparator),
error: testItemPropAccessor(api, 'error', undefined, strictEqualComparator),
});
}
public invalidate() {
this[TestItemHookProperty]?.invalidate(this.id);
getPrivateApiFor(this).bus.fire([ExtHostTestItemEventType.Invalidated]);
}
public discoverChildren(progress: vscode.Progress<{ busy: boolean }>, _token: vscode.CancellationToken) {
progress.report({ busy: false });
public dispose() {
const api = getPrivateApiFor(this);
if (api.parent) {
getPrivateApiFor(api.parent).children.delete(this.id);
}
api.bus.fire([ExtHostTestItemEventType.Disposed]);
}
public addChild(child: vscode.TestItem<unknown>) {
if (!(child instanceof TestItemImpl)) {
throw new Error('Test child must be created through vscode.test.createTestItem()');
}
const api = getPrivateApiFor(this);
if (api.children.has(child.id)) {
throw new Error(`Attempted to insert a duplicate test item ID ${child.id}`);
}
api.children.set(child.id, child);
api.bus.fire([ExtHostTestItemEventType.NewChild, child]);
}
}
export class TestState implements vscode.TestState {
public messages: TestMessage[] = [];
public duration?: number;
constructor(public state: TestResult) { }
}
export class TestMessage implements vscode.TestMessage {
public severity = TestMessageSeverity.Error;
@@ -3402,7 +3384,7 @@ export enum ExternalUriOpenerPriority {
export enum WorkspaceTrustState {
Untrusted = 0,
Trusted = 1,
Unknown = 2
Unspecified = 2
}
export enum PortAutoForwardAction {

View File

@@ -3,11 +3,14 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { VSBuffer } from 'vs/base/common/buffer';
import { Emitter, Event } from 'vs/base/common/event';
import { URI } from 'vs/base/common/uri';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { normalizeVersion, parseVersion } from 'vs/platform/extensions/common/extensionValidator';
import { ILogService } from 'vs/platform/log/common/log';
import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
import { deserializeWebviewMessage } from 'vs/workbench/api/common/extHostWebviewMessaging';
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview';
import type * as vscode from 'vscode';
@@ -28,6 +31,8 @@ export class ExtHostWebview implements vscode.Webview {
#isDisposed: boolean = false;
#hasCalledAsWebviewUri = false;
#serializeBuffersForPostMessage = false;
constructor(
handle: extHostProtocol.WebviewHandle,
proxy: extHostProtocol.MainThreadWebviewsShape,
@@ -43,6 +48,7 @@ export class ExtHostWebview implements vscode.Webview {
this.#initData = initData;
this.#workspace = workspace;
this.#extension = extension;
this.#serializeBuffersForPostMessage = shouldSerializeBuffersForPostMessage(extension);
this.#deprecationService = deprecationService;
}
@@ -104,7 +110,8 @@ export class ExtHostWebview implements vscode.Webview {
if (this.#isDisposed) {
return false;
}
return this.#proxy.$postMessage(this.#handle, message);
const serialized = serializeMessage(message, { serializeBuffersForPostMessage: this.#serializeBuffersForPostMessage });
return this.#proxy.$postMessage(this.#handle, serialized.message, ...serialized.buffers);
}
private assertNotDisposed() {
@@ -114,6 +121,49 @@ export class ExtHostWebview implements vscode.Webview {
}
}
export function shouldSerializeBuffersForPostMessage(extension: IExtensionDescription): boolean {
if (!extension.enableProposedApi) {
return false;
}
try {
const version = normalizeVersion(parseVersion(extension.engines.vscode));
return !!version && version.majorBase >= 1 && version.minorBase >= 56;
} catch {
return false;
}
}
export function serializeMessage(message: any, options: { serializeBuffersForPostMessage?: boolean }): { message: string, buffers: VSBuffer[] } {
if (options.serializeBuffersForPostMessage) {
// Extract all ArrayBuffers from the message and replace them with references.
const vsBuffers: Array<{ original: ArrayBuffer, vsBuffer: VSBuffer }> = [];
const replacer = (_key: string, value: any) => {
if (value && value instanceof ArrayBuffer) {
let index = vsBuffers.findIndex(x => x.original === value);
if (index === -1) {
const bytes = new Uint8Array(value);
const vsBuffer = VSBuffer.wrap(bytes);
index = vsBuffers.length;
vsBuffers.push({ original: value, vsBuffer });
}
return <extHostProtocol.WebviewMessageArrayBufferReference>{
$$vscode_array_buffer_reference$$: true,
index,
};
}
return value;
};
const serializedMessage = JSON.stringify(message, replacer);
return { message: serializedMessage, buffers: vsBuffers.map(x => x.vsBuffer) };
} else {
return { message: JSON.stringify(message), buffers: [] };
}
}
export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
private readonly _webviewProxy: extHostProtocol.MainThreadWebviewsShape;
@@ -132,10 +182,12 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
public $onMessage(
handle: extHostProtocol.WebviewHandle,
message: any
jsonMessage: string,
...buffers: VSBuffer[]
): void {
const webview = this.getWebview(handle);
if (webview) {
const { message } = deserializeWebviewMessage(jsonMessage, buffers);
webview._onMessageEmitter.fire(message);
}
}

View File

@@ -0,0 +1,122 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { VSBuffer } from 'vs/base/common/buffer';
import * as extHostProtocol from './extHost.protocol';
class ArrayBufferSet {
public readonly buffers: ArrayBuffer[] = [];
public add(buffer: ArrayBuffer): number {
let index = this.buffers.indexOf(buffer);
if (index < 0) {
index = this.buffers.length;
this.buffers.push(buffer);
}
return index;
}
}
export function serializeWebviewMessage(
message: any,
transfer?: readonly ArrayBuffer[]
): { message: string, buffers: VSBuffer[] } {
if (transfer) {
// Extract all ArrayBuffers from the message and replace them with references.
const arrayBuffers = new ArrayBufferSet();
const replacer = (_key: string, value: any) => {
if (value instanceof ArrayBuffer) {
const index = arrayBuffers.add(value);
return <extHostProtocol.WebviewMessageArrayBufferReference>{
$$vscode_array_buffer_reference$$: true,
index,
};
} else if (ArrayBuffer.isView(value)) {
const type = getTypedArrayType(value);
if (type) {
const index = arrayBuffers.add(value.buffer);
return <extHostProtocol.WebviewMessageArrayBufferReference>{
$$vscode_array_buffer_reference$$: true,
index,
view: {
type: type,
byteLength: value.byteLength,
byteOffset: value.byteOffset,
}
};
}
}
return value;
};
const serializedMessage = JSON.stringify(message, replacer);
const buffers = arrayBuffers.buffers.map(arrayBuffer => {
const bytes = new Uint8Array(arrayBuffer);
return VSBuffer.wrap(bytes);
});
return { message: serializedMessage, buffers };
} else {
return { message: JSON.stringify(message), buffers: [] };
}
}
function getTypedArrayType(value: ArrayBufferView): extHostProtocol.WebviewMessageArrayBufferViewType | undefined {
switch (value.constructor.name) {
case 'Int8Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Int8Array;
case 'Uint8Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Uint8Array;
case 'Uint8ClampedArray': return extHostProtocol.WebviewMessageArrayBufferViewType.Uint8ClampedArray;
case 'Int16Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Int16Array;
case 'Uint16Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Uint16Array;
case 'Int32Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Int32Array;
case 'Uint32Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Uint32Array;
case 'Float32Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Float32Array;
case 'Float64Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Float64Array;
case 'BigInt64Array': return extHostProtocol.WebviewMessageArrayBufferViewType.BigInt64Array;
case 'BigUint64Array': return extHostProtocol.WebviewMessageArrayBufferViewType.BigUint64Array;
}
return undefined;
}
export function deserializeWebviewMessage(jsonMessage: string, buffers: VSBuffer[]): { message: any, arrayBuffers: ArrayBuffer[] } {
const arrayBuffers: ArrayBuffer[] = buffers.map(buffer => {
const arrayBuffer = new ArrayBuffer(buffer.byteLength);
const uint8Array = new Uint8Array(arrayBuffer);
uint8Array.set(buffer.buffer);
return arrayBuffer;
});
const reviver = !buffers.length ? undefined : (_key: string, value: any) => {
if (typeof value === 'object' && (value as extHostProtocol.WebviewMessageArrayBufferReference).$$vscode_array_buffer_reference$$) {
const ref = value as extHostProtocol.WebviewMessageArrayBufferReference;
const { index } = ref;
const arrayBuffer = arrayBuffers[index];
if (ref.view) {
switch (ref.view.type) {
case extHostProtocol.WebviewMessageArrayBufferViewType.Int8Array: return new Int8Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Int8Array.BYTES_PER_ELEMENT);
case extHostProtocol.WebviewMessageArrayBufferViewType.Uint8Array: return new Uint8Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Uint8Array.BYTES_PER_ELEMENT);
case extHostProtocol.WebviewMessageArrayBufferViewType.Uint8ClampedArray: return new Uint8ClampedArray(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Uint8ClampedArray.BYTES_PER_ELEMENT);
case extHostProtocol.WebviewMessageArrayBufferViewType.Int16Array: return new Int16Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Int16Array.BYTES_PER_ELEMENT);
case extHostProtocol.WebviewMessageArrayBufferViewType.Uint16Array: return new Uint16Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Uint16Array.BYTES_PER_ELEMENT);
case extHostProtocol.WebviewMessageArrayBufferViewType.Int32Array: return new Int32Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Int32Array.BYTES_PER_ELEMENT);
case extHostProtocol.WebviewMessageArrayBufferViewType.Uint32Array: return new Uint32Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Uint32Array.BYTES_PER_ELEMENT);
case extHostProtocol.WebviewMessageArrayBufferViewType.Float32Array: return new Float32Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Float32Array.BYTES_PER_ELEMENT);
case extHostProtocol.WebviewMessageArrayBufferViewType.Float64Array: return new Float64Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Float64Array.BYTES_PER_ELEMENT);
case extHostProtocol.WebviewMessageArrayBufferViewType.BigInt64Array: return new BigInt64Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / BigInt64Array.BYTES_PER_ELEMENT);
case extHostProtocol.WebviewMessageArrayBufferViewType.BigUint64Array: return new BigUint64Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / BigUint64Array.BYTES_PER_ELEMENT);
default: throw new Error('Unknown array buffer view type');
}
}
return arrayBuffer;
}
return value;
};
const message = JSON.parse(jsonMessage, reviver);
return { message, arrayBuffers };
}

View File

@@ -9,7 +9,7 @@ import { URI } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import { serializeWebviewOptions, ExtHostWebview, ExtHostWebviews, toExtensionData } from 'vs/workbench/api/common/extHostWebview';
import { serializeWebviewOptions, ExtHostWebview, ExtHostWebviews, toExtensionData, shouldSerializeBuffersForPostMessage } from 'vs/workbench/api/common/extHostWebview';
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { EditorGroupColumn } from 'vs/workbench/common/editor';
import type * as vscode from 'vscode';
@@ -60,7 +60,7 @@ class ExtHostWebviewPanel extends Disposable implements vscode.WebviewPanel {
this.#webview = webview;
}
public dispose() {
public override dispose() {
if (this.#isDisposed) {
return;
}
@@ -199,11 +199,13 @@ export class ExtHostWebviewPanels implements extHostProtocol.ExtHostWebviewPanel
preserveFocus: typeof showOptions === 'object' && !!showOptions.preserveFocus
};
const serializeBuffersForPostMessage = shouldSerializeBuffersForPostMessage(extension);
const handle = ExtHostWebviewPanels.newHandle();
this._proxy.$createWebviewPanel(toExtensionData(extension), handle, viewType, {
title,
panelOptions: serializeWebviewPanelOptions(options),
webviewOptions: serializeWebviewOptions(extension, this.workspace, options),
serializeBuffersForPostMessage,
}, webviewShowOptions);
const webview = this.webviews.createNewWebview(handle, options, extension);
@@ -263,7 +265,9 @@ export class ExtHostWebviewPanels implements extHostProtocol.ExtHostWebviewPanel
}
this._serializers.set(viewType, { serializer, extension });
this._proxy.$registerSerializer(viewType);
this._proxy.$registerSerializer(viewType, {
serializeBuffersForPostMessage: shouldSerializeBuffersForPostMessage(extension)
});
return new extHostTypes.Disposable(() => {
this._serializers.delete(viewType);

View File

@@ -43,7 +43,7 @@ class ExtHostWebviewView extends Disposable implements vscode.WebviewView {
this.#isVisible = isVisible;
}
public dispose() {
public override dispose() {
if (this.#isDisposed) {
return;
}
@@ -146,7 +146,10 @@ export class ExtHostWebviewViews implements extHostProtocol.ExtHostWebviewViewsS
}
this._viewProviders.set(viewType, { provider, extension });
this._proxy.$registerWebviewViewProvider(toExtensionData(extension), viewType, webviewOptions);
this._proxy.$registerWebviewViewProvider(toExtensionData(extension), viewType, {
retainContextWhenHidden: webviewOptions?.retainContextWhenHidden,
serializeBuffersForPostMessage: false,
});
return new extHostTypes.Disposable(() => {
this._viewProviders.delete(viewType);

View File

@@ -20,12 +20,11 @@ import { FileSystemProviderCapabilities } from 'vs/platform/files/common/files';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ILogService } from 'vs/platform/log/common/log';
import { Severity } from 'vs/platform/notification/common/notification';
import { WorkspaceTrustStateChangeEvent } from 'vs/platform/workspace/common/workspaceTrust';
import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { Range, RelativePattern, WorkspaceTrustState } from 'vs/workbench/api/common/extHostTypes';
import { Range, RelativePattern } from 'vs/workbench/api/common/extHostTypes';
import { ITextQueryBuilderOptions } from 'vs/workbench/contrib/search/common/queryBuilder';
import { IRawFileMatch2, resultIsMatch } from 'vs/workbench/services/search/common/search';
import * as vscode from 'vscode';
@@ -169,8 +168,8 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
private readonly _onDidChangeWorkspace = new Emitter<vscode.WorkspaceFoldersChangeEvent>();
readonly onDidChangeWorkspace: Event<vscode.WorkspaceFoldersChangeEvent> = this._onDidChangeWorkspace.event;
private readonly _onDidChangeWorkspaceTrustState = new Emitter<vscode.WorkspaceTrustStateChangeEvent>();
readonly onDidChangeWorkspaceTrustState: Event<vscode.WorkspaceTrustStateChangeEvent> = this._onDidChangeWorkspaceTrustState.event;
private readonly _onDidGrantWorkspaceTrust = new Emitter<void>();
readonly onDidGrantWorkspaceTrust: Event<void> = this._onDidGrantWorkspaceTrust.event;
private readonly _logService: ILogService;
private readonly _requestIdProvider: Counter;
@@ -185,7 +184,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
private readonly _activeSearchCallbacks: ((match: IRawFileMatch2) => any)[] = [];
private _workspaceTrustState: WorkspaceTrustState = WorkspaceTrustState.Unknown;
private _trusted: boolean = false;
constructor(
@IExtHostRpcService extHostRpc: IExtHostRpcService,
@@ -204,8 +203,8 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
this._confirmedWorkspace = data ? new ExtHostWorkspaceImpl(data.id, data.name, [], data.configuration ? URI.revive(data.configuration) : null, !!data.isUntitled, uri => ignorePathCasing(uri, extHostFileSystemInfo)) : undefined;
}
$initializeWorkspace(data: IWorkspaceData | null, trustState: WorkspaceTrustState): void {
this._workspaceTrustState = trustState;
$initializeWorkspace(data: IWorkspaceData | null, trusted: boolean): void {
this._trusted = trusted;
this.$acceptWorkspaceData(data);
this._barrier.open();
}
@@ -559,17 +558,20 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
// --- trust ---
get trustState(): WorkspaceTrustState {
return this._workspaceTrustState;
get trusted(): boolean {
return this._trusted;
}
requireWorkspaceTrust(options?: vscode.WorkspaceTrustRequestOptions): Promise<WorkspaceTrustState> {
return this._proxy.$requireWorkspaceTrust(options);
requestWorkspaceTrust(options?: vscode.WorkspaceTrustRequestOptions): Promise<boolean | undefined> {
const promise = this._proxy.$requestWorkspaceTrust(options);
return options?.modal ? promise : Promise.resolve(this._trusted);
}
$onDidChangeWorkspaceTrustState(state: WorkspaceTrustStateChangeEvent): void {
this._workspaceTrustState = state.currentTrustState;
this._onDidChangeWorkspaceTrustState.fire(Object.freeze(state));
$onDidGrantWorkspaceTrust(): void {
if (!this._trusted) {
this._trusted = true;
this._onDidGrantWorkspaceTrust.fire();
}
}
}

View File

@@ -138,6 +138,12 @@ const apiMenus: IAPIMenu[] = [
proposed: true,
supportsSubmenus: false
},
{
key: 'statusBar/remoteIndicator',
id: MenuId.StatusBarRemoteIndicatorMenu,
description: localize('menus.statusBarRemoteIndicator', "The remote indicator menu in the status bar"),
supportsSubmenus: false
},
{
key: 'view/title',
id: MenuId.ViewTitle,
@@ -212,6 +218,11 @@ const apiMenus: IAPIMenu[] = [
key: 'ports/item/origin/inline',
id: MenuId.TunnelOriginInline,
description: localize('view.tunnelOriginInline', "The Ports view item origin inline menu")
},
{
key: 'ports/item/port/inline',
id: MenuId.TunnelPortInline,
description: localize('view.tunnelPortInline', "The Ports view item port inline menu")
}
];