chore(vscode): update to 1.55.2

This commit is contained in:
Akash Satheesan
2021-04-09 11:32:27 +05:30
1102 changed files with 39988 additions and 23544 deletions

View File

@@ -32,7 +32,11 @@ function adjustHandler(handler: (executor: ICommandsExecutor, ...args: any[]) =>
interface INewWindowAPICommandOptions {
reuseWindow?: boolean;
remoteAuthority?: string;
/**
* If set, defines the remoteAuthority of the new window. `null` will open a local window.
* If not set, defaults to remoteAuthority of the current window.
*/
remoteAuthority?: string | null;
}
export class NewWindowAPICommand {
@@ -95,6 +99,7 @@ interface RecentEntry {
uri: URI;
type: 'workspace' | 'folder' | 'file';
label?: string;
remoteAuthority?: string;
}
CommandsRegistry.registerCommand('_workbench.addToRecentlyOpened', async function (accessor: ServicesAccessor, recentEntry: RecentEntry) {
@@ -102,13 +107,14 @@ CommandsRegistry.registerCommand('_workbench.addToRecentlyOpened', async functio
let recent: IRecent | undefined = undefined;
const uri = recentEntry.uri;
const label = recentEntry.label;
const remoteAuthority = recentEntry.remoteAuthority;
if (recentEntry.type === 'workspace') {
const workspace = await workspacesService.getWorkspaceIdentifier(uri);
recent = { workspace, label };
recent = { workspace, label, remoteAuthority };
} else if (recentEntry.type === 'folder') {
recent = { folderUri: uri, label };
recent = { folderUri: uri, label, remoteAuthority };
} else {
recent = { fileUri: uri, label };
recent = { fileUri: uri, label, remoteAuthority };
}
return workspacesService.addRecentlyOpened([recent]);
});

View File

@@ -7,7 +7,6 @@ import * as nls from 'vs/nls';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import * as errors from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
import * as path from 'vs/base/common/path';
import Severity from 'vs/base/common/severity';
import { URI } from 'vs/base/common/uri';
import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
@@ -27,7 +26,7 @@ import { ExtHostDocumentContentProvider } from 'vs/workbench/api/common/extHostD
import { ExtHostDocumentSaveParticipant } from 'vs/workbench/api/common/extHostDocumentSaveParticipant';
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
import { Extension, IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
import { ExtHostFileSystem } from 'vs/workbench/api/common/extHostFileSystem';
import { ExtHostFileSystemEventService } from 'vs/workbench/api/common/extHostFileSystemEventService';
import { ExtHostLanguageFeatures } from 'vs/workbench/api/common/extHostLanguageFeatures';
@@ -52,8 +51,7 @@ import { throwProposedApiError, checkProposedApiEnabled, checkRequiresWorkspaceT
import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
import type * as vscode from 'vscode';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { originalFSPath } from 'vs/base/common/resources';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { values } from 'vs/base/common/collections';
import { ExtHostEditorInsets } from 'vs/workbench/api/common/extHostCodeInsets';
import { ExtHostLabelService } from 'vs/workbench/api/common/extHostLabelService';
@@ -85,6 +83,7 @@ import { ExtHostTesting } from 'vs/workbench/api/common/extHostTesting';
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';
export interface IExtensionApiFactory {
(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode;
@@ -101,6 +100,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostConsumerFileSystem = accessor.get(IExtHostConsumerFileSystem);
const extensionService = accessor.get(IExtHostExtensionService);
const extHostWorkspace = accessor.get(IExtHostWorkspace);
const extHostTelemetry = accessor.get(IExtHostTelemetry);
const extHostConfiguration = accessor.get(IExtHostConfiguration);
const uriTransformer = accessor.get(IURITransformerService);
const rpcProtocol = accessor.get(IExtHostRpcService);
@@ -122,6 +122,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
rpcProtocol.set(ExtHostContext.ExtHostTunnelService, extHostTunnelService);
rpcProtocol.set(ExtHostContext.ExtHostWindow, extHostWindow);
rpcProtocol.set(ExtHostContext.ExtHostSecretState, extHostSecretState);
rpcProtocol.set(ExtHostContext.ExtHostTelemetry, extHostTelemetry);
// automatically create and register addressable instances
const extHostDecorations = rpcProtocol.set(ExtHostContext.ExtHostDecorations, accessor.get(IExtHostDecorations));
@@ -139,7 +140,7 @@ 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, initData.environment, extHostLogService, extensionStoragePaths));
const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, extHostDocuments, initData.environment, extHostLogService, extensionStoragePaths));
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));
@@ -167,7 +168,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
rpcProtocol.assertRegistered(expected);
// Other instances
const extHostBulkEdits = new ExtHostBulkEdits(rpcProtocol, extHostDocumentsAndEditors, extHostNotebook);
const extHostBulkEdits = new ExtHostBulkEdits(rpcProtocol, extHostDocumentsAndEditors);
const extHostClipboard = new ExtHostClipboard(rpcProtocol);
const extHostMessageService = new ExtHostMessageService(rpcProtocol, extHostLogService);
const extHostDialogs = new ExtHostDialogs(rpcProtocol);
@@ -292,6 +293,16 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
get shell() {
return extHostTerminalService.getDefaultShell(false, configProvider);
},
get isTelemetryEnabled() {
return extHostTelemetry.getTelemetryEnabled();
},
get onDidChangeTelemetryEnabled(): Event<boolean> {
return extHostTelemetry.onDidChangeTelemetryEnabled;
},
get isNewAppInstall() {
const installAge = Date.now() - new Date(initData.telemetryInfo.firstSessionDate).getTime();
return isNaN(installAge) ? false : installAge < 1000 * 60 * 60 * 24; // install age is less than a day
},
openExternal(uri: URI, options?: { allowContributedOpeners?: boolean | string; }) {
return extHostWindow.openUri(uri, {
allowTunneling: !!initData.remote.authority,
@@ -354,14 +365,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
// namespace: extensions
const extensions: typeof vscode.extensions = {
getExtension(extensionId: string): Extension<any> | undefined {
getExtension(extensionId: string): vscode.Extension<any> | undefined {
const desc = extensionRegistry.getExtensionDescription(extensionId);
if (desc) {
return new Extension(extensionService, extension.identifier, desc, extensionKind);
}
return undefined;
},
get all(): Extension<any>[] {
get all(): vscode.Extension<any>[] {
return extensionRegistry.getAllExtensionDescriptions().map((desc) => new Extension(extensionService, extension.identifier, desc, extensionKind));
},
get onDidChange() {
@@ -414,7 +425,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
return extHostLanguageFeatures.registerEvaluatableExpressionProvider(extension, checkSelector(selector), provider, extension.identifier);
},
registerInlineValuesProvider(selector: vscode.DocumentSelector, provider: vscode.InlineValuesProvider): vscode.Disposable {
checkProposedApiEnabled(extension);
return extHostLanguageFeatures.registerInlineValuesProvider(extension, checkSelector(selector), provider, extension.identifier);
},
registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable {
@@ -691,9 +701,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
checkProposedApiEnabled(extension);
return extHostNotebook.onDidChangeNotebookEditorVisibleRanges(listener, thisArgs, disposables);
},
showNotebookDocument(document, options?) {
showNotebookDocument(uriOrDocument, options?) {
checkProposedApiEnabled(extension);
return extHostNotebook.showNotebookDocument(document, options);
return extHostNotebook.showNotebookDocument(uriOrDocument, options);
},
registerExternalUriOpener(id: string, opener: vscode.ExternalUriOpener, metadata: vscode.ExternalUriOpenerMetadata) {
checkProposedApiEnabled(extension);
@@ -890,6 +900,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
checkProposedApiEnabled(extension);
return extHostTunnelService.onDidChangeTunnels(listener, thisArg, disposables);
},
registerPortAttributesProvider: (portSelector: { pid?: number, portRange?: [number, number] }, provider: vscode.PortAttributesProvider) => {
checkProposedApiEnabled(extension);
return extHostTunnelService.registerPortsAttributesProvider(portSelector, provider);
},
registerTimelineProvider: (scheme: string | string[], provider: vscode.TimelineProvider) => {
checkProposedApiEnabled(extension);
return extHostTimeline.registerTimelineProvider(scheme, provider, extension.identifier, extHostCommands.converter);
@@ -899,10 +913,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
checkRequiresWorkspaceTrust(extension);
return extHostWorkspace.trustState;
},
requireWorkspaceTrust: (modal?: boolean) => {
requireWorkspaceTrust: (options?: vscode.WorkspaceTrustRequestOptions) => {
checkProposedApiEnabled(extension);
checkRequiresWorkspaceTrust(extension);
return extHostWorkspace.requireWorkspaceTrust(modal);
return extHostWorkspace.requireWorkspaceTrust(options);
},
onDidChangeWorkspaceTrustState: (listener, thisArgs?, disposables?) => {
return extHostWorkspace.onDidChangeWorkspaceTrustState(listener, thisArgs, disposables);
@@ -1012,18 +1026,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
};
// namespace: notebook
const notebook: (typeof vscode.notebook & {
// to ensure that notebook extensions not break before they update APIs.
visibleNotebookEditors: vscode.NotebookEditor[];
onDidChangeVisibleNotebookEditors: Event<vscode.NotebookEditor[]>;
activeNotebookEditor: vscode.NotebookEditor | undefined;
onDidChangeActiveNotebookEditor: Event<vscode.NotebookEditor | undefined>;
onDidChangeNotebookEditorSelection: Event<vscode.NotebookEditorSelectionChangeEvent>;
onDidChangeNotebookEditorVisibleRanges: Event<vscode.NotebookEditorVisibleRangesChangeEvent>;
}) = {
openNotebookDocument: (uriComponents, viewType) => {
const notebook: typeof vscode.notebook = {
openNotebookDocument: (uriComponents) => {
checkProposedApiEnabled(extension);
return extHostNotebook.openNotebookDocument(uriComponents, viewType);
return extHostNotebook.openNotebookDocument(uriComponents);
},
get onDidOpenNotebookDocument(): Event<vscode.NotebookDocument> {
checkProposedApiEnabled(extension);
@@ -1041,18 +1047,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
checkProposedApiEnabled(extension);
return extHostNotebook.notebookDocuments.map(d => d.notebookDocument);
},
get visibleNotebookEditors(): vscode.NotebookEditor[] {
checkProposedApiEnabled(extension);
return extHostNotebook.visibleNotebookEditors;
},
get onDidChangeVisibleNotebookEditors() {
checkProposedApiEnabled(extension);
return extHostNotebook.onDidChangeVisibleNotebookEditors;
},
get onDidChangeActiveNotebookKernel() {
checkProposedApiEnabled(extension);
return extHostNotebook.onDidChangeActiveNotebookKernel;
},
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 }
@@ -1068,14 +1070,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
checkProposedApiEnabled(extension);
return extHostNotebook.createNotebookEditorDecorationType(options);
},
get activeNotebookEditor(): vscode.NotebookEditor | undefined {
checkProposedApiEnabled(extension);
return extHostNotebook.activeNotebookEditor;
},
onDidChangeActiveNotebookEditor(listener, thisArgs?, disposables?) {
checkProposedApiEnabled(extension);
return extHostNotebook.onDidChangeActiveNotebookEditor(listener, thisArgs, disposables);
},
onDidChangeNotebookDocumentMetadata(listener, thisArgs?, disposables?) {
checkProposedApiEnabled(extension);
return extHostNotebook.onDidChangeNotebookDocumentMetadata(listener, thisArgs, disposables);
@@ -1084,22 +1078,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
checkProposedApiEnabled(extension);
return extHostNotebook.onDidChangeNotebookCells(listener, thisArgs, disposables);
},
onDidChangeNotebookEditorSelection(listener, thisArgs?, disposables?) {
onDidChangeCellExecutionState(listener, thisArgs?, disposables?) {
checkProposedApiEnabled(extension);
return extHostNotebook.onDidChangeNotebookEditorSelection(listener, thisArgs, disposables);
},
onDidChangeNotebookEditorVisibleRanges(listener, thisArgs?, disposables?) {
checkProposedApiEnabled(extension);
return extHostNotebook.onDidChangeNotebookEditorVisibleRanges(listener, thisArgs, disposables);
return extHostNotebook.onDidChangeNotebookCellExecutionState(listener, thisArgs, disposables);
},
onDidChangeCellOutputs(listener, thisArgs?, disposables?) {
checkProposedApiEnabled(extension);
return extHostNotebook.onDidChangeCellOutputs(listener, thisArgs, disposables);
},
onDidChangeCellLanguage(listener, thisArgs?, disposables?) {
checkProposedApiEnabled(extension);
return extHostNotebook.onDidChangeCellLanguage(listener, thisArgs, disposables);
},
onDidChangeCellMetadata(listener, thisArgs?, disposables?) {
checkProposedApiEnabled(extension);
return extHostNotebook.onDidChangeCellMetadata(listener, thisArgs, disposables);
@@ -1111,6 +1097,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
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);
}
};
@@ -1140,7 +1130,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
CandidatePortSource: CandidatePortSource,
CodeAction: extHostTypes.CodeAction,
CodeActionKind: extHostTypes.CodeActionKind,
CodeActionTrigger: extHostTypes.CodeActionTrigger,
CodeActionTriggerKind: extHostTypes.CodeActionTriggerKind,
CodeLens: extHostTypes.CodeLens,
Color: extHostTypes.Color,
ColorInformation: extHostTypes.ColorInformation,
@@ -1194,6 +1184,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
MarkdownString: extHostTypes.MarkdownString,
OverviewRulerLane: OverviewRulerLane,
ParameterInformation: extHostTypes.ParameterInformation,
PortAutoForwardAction: extHostTypes.PortAutoForwardAction,
Position: extHostTypes.Position,
ProcessExecution: extHostTypes.ProcessExecution,
ProgressLocation: extHostTypes.ProgressLocation,
@@ -1239,124 +1230,31 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
ViewColumn: extHostTypes.ViewColumn,
WorkspaceEdit: extHostTypes.WorkspaceEdit,
// proposed api types
get InlineHint() {
return extHostTypes.InlineHint;
},
get InlineHintKind() {
return extHostTypes.InlineHintKind;
},
get RemoteAuthorityResolverError() {
// checkProposedApiEnabled(extension);
return extHostTypes.RemoteAuthorityResolverError;
},
get ResolvedAuthority() {
// checkProposedApiEnabled(extension);
return extHostTypes.ResolvedAuthority;
},
get SourceControlInputBoxValidationType() {
// checkProposedApiEnabled(extension);
return extHostTypes.SourceControlInputBoxValidationType;
},
get ExtensionRuntime() {
// checkProposedApiEnabled(extension);
return extHostTypes.ExtensionRuntime;
},
get TimelineItem() {
// checkProposedApiEnabled(extension);
return extHostTypes.TimelineItem;
},
get NotebookCellRange() {
return extHostTypes.NotebookCellRange;
},
get NotebookCellKind() {
// checkProposedApiEnabled(extension);
return extHostTypes.NotebookCellKind;
},
get NotebookCellRunState() {
// checkProposedApiEnabled(extension);
return extHostTypes.NotebookCellRunState;
},
get NotebookDocumentMetadata() {
return extHostTypes.NotebookDocumentMetadata;
},
get NotebookCellMetadata() {
return extHostTypes.NotebookCellMetadata;
},
get NotebookRunState() {
// checkProposedApiEnabled(extension);
return extHostTypes.NotebookRunState;
},
get NotebookCellStatusBarAlignment() {
// checkProposedApiEnabled(extension);
return extHostTypes.NotebookCellStatusBarAlignment;
},
get NotebookEditorRevealType() {
// checkProposedApiEnabled(extension);
return extHostTypes.NotebookEditorRevealType;
},
get NotebookCellOutput() {
// checkProposedApiEnabled(extension);
return extHostTypes.NotebookCellOutput;
},
get NotebookCellOutputItem() {
// checkProposedApiEnabled(extension);
return extHostTypes.NotebookCellOutputItem;
},
get LinkedEditingRanges() {
// checkProposedApiEnabled(extension);
return extHostTypes.LinkedEditingRanges;
},
get TestRunState() {
// checkProposedApiEnabled(extension);
return extHostTypes.TestRunState;
},
get TestMessageSeverity() {
// checkProposedApiEnabled(extension);
return extHostTypes.TestMessageSeverity;
},
get WorkspaceTrustState() {
// checkProposedApiEnabled(extension);
return extHostTypes.WorkspaceTrustState;
}
InlineHint: extHostTypes.InlineHint,
InlineHintKind: extHostTypes.InlineHintKind,
RemoteAuthorityResolverError: extHostTypes.RemoteAuthorityResolverError,
ResolvedAuthority: extHostTypes.ResolvedAuthority,
SourceControlInputBoxValidationType: extHostTypes.SourceControlInputBoxValidationType,
ExtensionRuntime: extHostTypes.ExtensionRuntime,
TimelineItem: extHostTypes.TimelineItem,
NotebookCellRange: extHostTypes.NotebookCellRange,
NotebookCellKind: extHostTypes.NotebookCellKind,
NotebookCellExecutionState: extHostTypes.NotebookCellExecutionState,
NotebookDocumentMetadata: extHostTypes.NotebookDocumentMetadata,
NotebookCellMetadata: extHostTypes.NotebookCellMetadata,
NotebookCellData: extHostTypes.NotebookCellData,
NotebookData: extHostTypes.NotebookData,
NotebookCellStatusBarAlignment: extHostTypes.NotebookCellStatusBarAlignment,
NotebookEditorRevealType: extHostTypes.NotebookEditorRevealType,
NotebookCellOutput: extHostTypes.NotebookCellOutput,
NotebookCellOutputItem: extHostTypes.NotebookCellOutputItem,
LinkedEditingRanges: extHostTypes.LinkedEditingRanges,
TestItem: extHostTypes.TestItem,
TestState: extHostTypes.TestState,
TestResult: extHostTypes.TestResult,
TestMessage: extHostTypes.TestMessage,
TestMessageSeverity: extHostTypes.TestMessageSeverity,
WorkspaceTrustState: extHostTypes.WorkspaceTrustState
};
};
}
class Extension<T> implements vscode.Extension<T> {
#extensionService: IExtHostExtensionService;
#originExtensionId: ExtensionIdentifier;
#identifier: ExtensionIdentifier;
readonly id: string;
readonly extensionUri: URI;
readonly extensionPath: string;
readonly packageJSON: IExtensionDescription;
readonly extensionKind: vscode.ExtensionKind;
constructor(extensionService: IExtHostExtensionService, originExtensionId: ExtensionIdentifier, description: IExtensionDescription, kind: extHostTypes.ExtensionKind) {
this.#extensionService = extensionService;
this.#originExtensionId = originExtensionId;
this.#identifier = description.identifier;
this.id = description.identifier.value;
this.extensionUri = description.extensionLocation;
this.extensionPath = path.normalize(originalFSPath(description.extensionLocation));
this.packageJSON = description;
this.extensionKind = kind;
}
get isActive(): boolean {
return this.#extensionService.isActivated(this.#identifier);
}
get exports(): T {
if (this.packageJSON.api === 'none') {
return undefined!; // Strict nulloverride - Public api
}
return <T>this.#extensionService.getExtensionExports(this.#identifier);
}
activate(): Thenable<T> {
return this.#extensionService.activateByIdWithErrors(this.#identifier, { startup: false, extensionId: this.#originExtensionId, activationEvent: 'api' }).then(() => this.exports);
}
}

View File

@@ -22,6 +22,7 @@ import { IExtHostWindow, ExtHostWindow } from 'vs/workbench/api/common/extHostWi
import { IExtHostConsumerFileSystem, ExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer';
import { IExtHostFileSystemInfo, ExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo';
import { IExtHostSecretState, ExtHostSecretState } from 'vs/workbench/api/common/exHostSecretState';
import { ExtHostTelemetry, IExtHostTelemetry } from 'vs/workbench/api/common/extHostTelemetry';
registerSingleton(IExtensionStoragePaths, ExtensionStoragePaths);
registerSingleton(IExtHostApiDeprecationService, ExtHostApiDeprecationService);
@@ -41,3 +42,4 @@ registerSingleton(IExtHostTunnelService, ExtHostTunnelService);
registerSingleton(IExtHostWindow, ExtHostWindow);
registerSingleton(IExtHostWorkspace, ExtHostWorkspace);
registerSingleton(IExtHostSecretState, ExtHostSecretState);
registerSingleton(IExtHostTelemetry, ExtHostTelemetry);

View File

@@ -47,20 +47,21 @@ import * as search from 'vs/workbench/services/search/common/search';
import { EditorGroupColumn, SaveReason } from 'vs/workbench/common/editor';
import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator';
import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
import { TunnelCreationOptions, TunnelProviderFeatures, TunnelOptions } from 'vs/platform/remote/common/tunnel';
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 } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEventDto, NotebookDataDto, IMainCellDto, INotebookDocumentFilter, TransientMetadata, INotebookCellStatusBarEntry, ICellRange, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter, IOutputDto, TransientOptions, IImmediateCellEditOperation } from 'vs/workbench/contrib/notebook/common/notebookCommon';
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 { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility';
import { IExtensionIdWithVersion } from 'vs/platform/userDataSync/common/extensionsStorageSync';
import { InternalTestItem, ITestState, RunTestForProviderRequest, RunTestsRequest, TestIdWithProvider, TestsDiff, ISerializedTestResults } from 'vs/workbench/contrib/testing/common/testCollection';
import { InternalTestItem, ITestState, RunTestForProviderRequest, RunTestsRequest, TestIdWithSrc, TestsDiff, ISerializedTestResults } from 'vs/workbench/contrib/testing/common/testCollection';
import { CandidatePort } from 'vs/workbench/services/remote/common/remoteExplorerService';
import { WorkspaceTrustStateChangeEvent } from 'vs/platform/workspace/common/workspaceTrust';
import { WorkspaceTrustRequestOptions, WorkspaceTrustStateChangeEvent } 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 { ITerminalProfile } from 'vs/workbench/contrib/terminal/common/terminal';
export interface IEnvironment {
isExtensionDevelopmentDebug: boolean;
@@ -457,7 +458,7 @@ export interface TerminalLaunchConfig {
waitOnExit?: boolean;
strictEnv?: boolean;
hideFromUser?: boolean;
isExtensionTerminal?: boolean;
isExtensionCustomPtyTerminal?: boolean;
isFeatureTerminal?: boolean;
isExtensionOwnedTerminal?: boolean;
}
@@ -504,6 +505,8 @@ export interface BaseTransferQuickInput {
id: number;
title?: string;
type?: 'quickPick' | 'inputBox';
enabled?: boolean;
@@ -558,6 +561,7 @@ export interface TransferInputBox extends BaseTransferQuickInput {
}
export interface IInputBoxOptions {
title?: string;
value?: string;
valueSelection?: [number, number];
prompt?: string;
@@ -592,11 +596,11 @@ export interface MainThreadTelemetryShape extends IDisposable {
}
export interface MainThreadEditorInsetsShape extends IDisposable {
$createEditorInset(handle: number, id: string, uri: UriComponents, line: number, height: number, options: modes.IWebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): Promise<void>;
$createEditorInset(handle: number, id: string, uri: UriComponents, line: number, height: number, options: IWebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): Promise<void>;
$disposeEditorInset(handle: number): void;
$setHtml(handle: number, value: string): void;
$setOptions(handle: number, options: modes.IWebviewOptions): void;
$setOptions(handle: number, options: IWebviewOptions): void;
$postMessage(handle: number, value: any): Promise<boolean>;
}
@@ -646,18 +650,45 @@ export enum WebviewEditorCapabilities {
SupportsHotExit,
}
export interface IWebviewPortMapping {
readonly webviewPort: number;
readonly extensionHostPort: number;
}
export interface IWebviewOptions {
readonly enableScripts?: boolean;
readonly enableCommandUris?: boolean;
readonly localResourceRoots?: ReadonlyArray<UriComponents>;
readonly portMapping?: ReadonlyArray<IWebviewPortMapping>;
}
export interface IWebviewPanelOptions {
readonly enableFindWidget?: boolean;
readonly retainContextWhenHidden?: boolean;
}
export interface CustomTextEditorCapabilities {
readonly supportsMove?: boolean;
}
export interface MainThreadWebviewsShape extends IDisposable {
$setHtml(handle: WebviewHandle, value: string): void;
$setOptions(handle: WebviewHandle, options: modes.IWebviewOptions): void;
$setOptions(handle: WebviewHandle, options: IWebviewOptions): void;
$postMessage(handle: WebviewHandle, value: any): Promise<boolean>
}
export interface MainThreadWebviewPanelsShape extends IDisposable {
$createWebviewPanel(extension: WebviewExtensionDescription, handle: WebviewHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: modes.IWebviewPanelOptions & modes.IWebviewOptions): void;
$createWebviewPanel(
extension: WebviewExtensionDescription,
handle: WebviewHandle,
viewType: string,
initData: {
title: string;
webviewOptions: IWebviewOptions;
panelOptions: IWebviewPanelOptions;
},
showOptions: WebviewPanelShowOptions,
): void;
$disposeWebview(handle: WebviewHandle): void;
$reveal(handle: WebviewHandle, showOptions: WebviewPanelShowOptions): void;
$setTitle(handle: WebviewHandle, value: string): void;
@@ -668,8 +699,8 @@ export interface MainThreadWebviewPanelsShape extends IDisposable {
}
export interface MainThreadCustomEditorsShape extends IDisposable {
$registerTextEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, capabilities: CustomTextEditorCapabilities): void;
$registerCustomEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean): void;
$registerTextEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: IWebviewPanelOptions, capabilities: CustomTextEditorCapabilities): void;
$registerCustomEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: IWebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean): void;
$unregisterEditorProvider(viewType: string): void;
$onDidEdit(resource: UriComponents, viewType: string, editId: number, label: string | undefined): void;
@@ -702,12 +733,33 @@ export interface ExtHostWebviewsShape {
export interface ExtHostWebviewPanelsShape {
$onDidChangeWebviewPanelViewStates(newState: WebviewPanelViewStateData): void;
$onDidDisposeWebviewPanel(handle: WebviewHandle): Promise<void>;
$deserializeWebviewPanel(newWebviewHandle: WebviewHandle, viewType: string, title: string, state: any, position: EditorGroupColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise<void>;
$deserializeWebviewPanel(
newWebviewHandle: WebviewHandle,
viewType: string,
initData: {
title: string;
state: any;
webviewOptions: IWebviewOptions;
panelOptions: IWebviewPanelOptions;
},
position: EditorGroupColumn,
): Promise<void>;
}
export interface ExtHostCustomEditorsShape {
$resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewHandle, viewType: string, title: string, position: EditorGroupColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, cancellation: CancellationToken): Promise<void>;
$createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, cancellation: CancellationToken): Promise<{ editable: boolean }>;
$resolveWebviewEditor(
resource: UriComponents,
newWebviewHandle: WebviewHandle,
viewType: string,
initData: {
title: string;
webviewOptions: IWebviewOptions;
panelOptions: IWebviewPanelOptions;
},
position: EditorGroupColumn,
cancellation: CancellationToken
): Promise<void>;
$createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, untitledDocumentData: VSBuffer | undefined, cancellation: CancellationToken): Promise<{ editable: boolean }>;
$disposeCustomDocument(resource: UriComponents, viewType: string): Promise<void>;
$undo(resource: UriComponents, viewType: string, editId: number, isDirty: boolean): Promise<void>;
@@ -788,14 +840,19 @@ export interface MainThreadNotebookShape extends IDisposable {
}): Promise<void>;
$updateNotebookProviderOptions(viewType: string, options?: { transientOutputs: boolean; transientMetadata: TransientMetadata; }): 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, viewType?: string): Promise<URI>;
$tryOpenDocument(uriComponents: UriComponents): Promise<UriComponents>;
$tryShowNotebookDocument(uriComponents: UriComponents, viewType: string, options: INotebookDocumentShowOptions): Promise<string>;
$tryRevealRange(id: string, range: ICellRange, revealType: NotebookEditorRevealType): Promise<void>;
$registerNotebookEditorDecorationType(key: string, options: INotebookDecorationRenderOptions): void;
@@ -834,7 +891,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(modal?: boolean): Promise<WorkspaceTrustState>
$requireWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Promise<WorkspaceTrustState>;
}
export interface IFileChangeDto {
@@ -890,7 +947,6 @@ export interface MainThreadExtensionServiceShape extends IDisposable {
$onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void;
$onExtensionActivationError(extensionId: ExtensionIdentifier, error: ExtensionActivationError): Promise<void>;
$onExtensionRuntimeError(extensionId: ExtensionIdentifier, error: SerializedError): void;
$onExtensionHostExit(code: number): Promise<void>;
$setPerformanceMarks(marks: performance.PerformanceMark[]): Promise<void>;
}
@@ -1000,6 +1056,11 @@ export enum CandidatePortSource {
Output = 2
}
export interface PortAttributesProviderSelector {
pid?: number;
portRange?: [number, number];
}
export interface MainThreadTunnelServiceShape extends IDisposable {
$openTunnel(tunnelOptions: TunnelOptions, source: string | undefined): Promise<TunnelDto | undefined>;
$closeTunnel(remote: { host: string, port: number }): Promise<void>;
@@ -1007,8 +1068,10 @@ export interface MainThreadTunnelServiceShape extends IDisposable {
$setTunnelProvider(features: TunnelProviderFeatures): Promise<void>;
$setRemoteTunnelService(processId: number): Promise<void>;
$setCandidateFilter(): Promise<void>;
$onFoundNewCandidates(candidates: { host: string, port: number, detail: string }[]): Promise<void>;
$onFoundNewCandidates(candidates: CandidatePort[]): Promise<void>;
$setCandidatePortSource(source: CandidatePortSource): Promise<void>;
$registerPortsAttributesProvider(selector: PortAttributesProviderSelector, providerHandle: number): Promise<void>;
$unregisterPortsAttributesProvider(providerHandle: number): Promise<void>;
}
export interface MainThreadTimelineShape extends IDisposable {
@@ -1046,7 +1109,7 @@ export interface IModelAddedData {
isDirty: boolean;
}
export interface ExtHostDocumentsShape {
$acceptModelModeChanged(strURL: UriComponents, oldModeId: string, newModeId: string): void;
$acceptModelModeChanged(strURL: UriComponents, newModeId: string): void;
$acceptModelSaved(strURL: UriComponents): void;
$acceptDirtyStateChanged(strURL: UriComponents, isDirty: boolean): void;
$acceptModelChanged(strURL: UriComponents, e: IModelChangedEvent, isDirty: boolean): void;
@@ -1173,6 +1236,8 @@ export type IResolveAuthorityResult = IResolveAuthorityErrorResult | IResolveAut
export interface ExtHostExtensionServiceShape {
$resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise<IResolveAuthorityResult>;
$startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise<void>;
$extensionTestsExecute(): Promise<number>;
$extensionTestsExit(code: number): Promise<void>;
$activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise<void>;
$activate(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<boolean>;
$setRemoteEnvironment(env: { [key: string]: string | null; }): Promise<void>;
@@ -1383,9 +1448,6 @@ export interface IWorkspaceCellEditDto {
export interface IWorkspaceEditDto {
edits: Array<IWorkspaceFileEditDto | IWorkspaceTextEditDto | IWorkspaceCellEditDto>;
// todo@jrieken reject should go into rename
rejectReason?: string;
}
export function reviveWorkspaceEditDto(data: IWorkspaceEditDto | undefined): modes.WorkspaceEdit {
@@ -1478,6 +1540,7 @@ export interface ILinkedEditingRangesDto {
}
export interface IInlineValueContextDto {
frameId: number;
stoppedLocation: IRange;
}
@@ -1505,7 +1568,7 @@ export interface ExtHostLanguageFeaturesShape {
$provideWorkspaceSymbols(handle: number, search: string, token: CancellationToken): Promise<IWorkspaceSymbolsDto>;
$resolveWorkspaceSymbol(handle: number, symbol: IWorkspaceSymbolDto, token: CancellationToken): Promise<IWorkspaceSymbolDto | undefined>;
$releaseWorkspaceSymbols(handle: number, id: number): void;
$provideRenameEdits(handle: number, resource: UriComponents, position: IPosition, newName: string, token: CancellationToken): Promise<IWorkspaceEditDto | undefined>;
$provideRenameEdits(handle: number, resource: UriComponents, position: IPosition, newName: string, token: CancellationToken): Promise<IWorkspaceEditDto & { rejectReason?: string } | undefined>;
$resolveRenameLocation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.RenameLocation | undefined>;
$provideDocumentSemanticTokens(handle: number, resource: UriComponents, previousResultId: number, token: CancellationToken): Promise<VSBuffer | null>;
$releaseDocumentSemanticTokens(handle: number, semanticColoringResultId: number): void;
@@ -1541,6 +1604,11 @@ export interface ExtHostQuickOpenShape {
$onDidHide(sessionId: number): void;
}
export interface ExtHostTelemetryShape {
$initializeTelemetryEnabled(enabled: boolean): void;
$onDidChangeTelemetryEnabled(enabled: boolean): void;
}
export interface IShellLaunchConfigDto {
name?: string;
executable?: string;
@@ -1548,12 +1616,6 @@ export interface IShellLaunchConfigDto {
cwd?: string | UriComponents;
env?: { [key: string]: string | null; };
hideFromUser?: boolean;
flowControl?: boolean;
}
export interface IShellDefinitionDto {
label: string;
path: string;
}
export interface IShellAndArgsDto {
@@ -1586,7 +1648,6 @@ export interface ExtHostTerminalServiceShape {
$acceptTerminalTitleChange(id: number, name: string): void;
$acceptTerminalDimensions(id: number, cols: number, rows: number): void;
$acceptTerminalMaximumDimensions(id: number, cols: number, rows: number): void;
$spawnExtHostProcess(id: number, shellLaunchConfig: IShellLaunchConfigDto, activeWorkspaceRootUri: UriComponents | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<ITerminalLaunchError | undefined>;
$startExtensionTerminal(id: number, initialDimensions: ITerminalDimensionsDto | undefined): Promise<ITerminalLaunchError | undefined>;
$acceptProcessAckDataEvent(id: number, charCount: number): void;
$acceptProcessInput(id: number, data: string): void;
@@ -1596,7 +1657,8 @@ export interface ExtHostTerminalServiceShape {
$acceptProcessRequestCwd(id: number): void;
$acceptProcessRequestLatency(id: number): number;
$acceptWorkspacePermissionsChanged(isAllowed: boolean): void;
$getAvailableShells(): Promise<IShellDefinitionDto[]>;
// TODO: Change quickLaunchOnly to "includeAutoDetected" or something similar
$getAvailableProfiles(quickLaunchOnly: boolean): Promise<ITerminalProfile[]>;
$getDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto>;
$provideLinks(id: number, line: string): Promise<ITerminalLinkDto[]>;
$activateLink(id: number, linkId: number): void;
@@ -1644,6 +1706,7 @@ export interface IDataBreakpointDto extends IBreakpointDto {
canPersist: boolean;
label: string;
accessTypes?: DebugProtocol.DataBreakpointAccessType[];
accessType: DebugProtocol.DataBreakpointAccessType;
}
export interface ISourceBreakpointDto extends IBreakpointDto {
@@ -1751,12 +1814,12 @@ export interface INotebookVisibleRangesEvent {
}
export interface INotebookEditorPropertiesChangeData {
visibleRanges: INotebookVisibleRangesEvent | null;
selections: INotebookSelectionChangeEvent | null;
visibleRanges?: INotebookVisibleRangesEvent;
selections?: INotebookSelectionChangeEvent;
}
export interface INotebookDocumentPropertiesChangeData {
metadata: NotebookDocumentMetadata | null;
metadata?: NotebookDocumentMetadata;
}
export interface INotebookModelAddedData {
@@ -1765,7 +1828,6 @@ export interface INotebookModelAddedData {
cells: IMainCellDto[],
viewType: string;
metadata?: NotebookDocumentMetadata;
contentOptions: { transientOutputs: boolean; transientMetadata: TransientMetadata; }
}
export interface INotebookEditorAddData {
@@ -1773,6 +1835,7 @@ export interface INotebookEditorAddData {
documentUri: UriComponents;
selections: ICellRange[];
visibleRanges: ICellRange[];
viewColumn?: number
}
export interface INotebookDocumentsAndEditorsDelta {
@@ -1796,6 +1859,7 @@ export interface INotebookKernelInfoDto2 {
isPreferred?: boolean;
preloads?: UriComponents[];
supportedLanguages?: string[]
implementsInterrupt?: boolean;
}
export interface ExtHostNotebookShape {
@@ -1803,13 +1867,18 @@ export interface ExtHostNotebookShape {
$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, cellHandle: number | undefined): Promise<void>;
$cancelNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): 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;
$openNotebook(viewType: string, uri: UriComponents, backupId?: string): Promise<NotebookDataDto>;
$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>;
$acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEventDto, isDirty: boolean): void;
$acceptDirtyStateChanged(uriComponents: UriComponents, isDirty: boolean): void;
$acceptModelSaved(uriComponents: UriComponents): void;
@@ -1835,6 +1904,7 @@ export interface ExtHostTunnelServiceShape {
$onDidTunnelsChange(): Promise<void>;
$registerCandidateFinder(enable: boolean): Promise<void>;
$applyCandidateFilter(candidates: CandidatePort[]): Promise<CandidatePort[]>;
$providePortAttributes(handles: number[], ports: number[], pid: number | undefined, commandline: string | undefined, cancellationToken: CancellationToken): Promise<ProvidedPortAttributes[]>;
}
export interface ExtHostTimelineShape {
@@ -1850,9 +1920,10 @@ export interface ExtHostTestingShape {
$runTestsForProvider(req: RunTestForProviderRequest, token: CancellationToken): Promise<void>;
$subscribeToTests(resource: ExtHostTestingResource, uri: UriComponents): void;
$unsubscribeFromTests(resource: ExtHostTestingResource, uri: UriComponents): void;
$lookupTest(test: TestIdWithProvider): Promise<InternalTestItem | undefined>;
$lookupTest(test: TestIdWithSrc): Promise<InternalTestItem | undefined>;
$acceptDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void;
$publishTestResults(results: ISerializedTestResults[]): void;
$expandTest(src: TestIdWithSrc, levels: number): Promise<void>;
}
export interface MainThreadTestingShape {
@@ -1864,7 +1935,6 @@ export interface MainThreadTestingShape {
$updateTestStateInRun(runId: string, testId: string, state: ITestState): void;
$runTests(req: RunTestsRequest, token: CancellationToken): Promise<string>;
$publishExtensionProvidedResults(results: ISerializedTestResults, persist: boolean): void;
$retireTest(extId: string): void;
}
// --- proxy identifiers
@@ -1968,4 +2038,5 @@ export const ExtHostContext = {
ExtHostAuthentication: createMainId<ExtHostAuthenticationShape>('ExtHostAuthentication'),
ExtHostTimeline: createMainId<ExtHostTimelineShape>('ExtHostTimeline'),
ExtHostTesting: createMainId<ExtHostTestingShape>('ExtHostTesting'),
ExtHostTelemetry: createMainId<ExtHostTelemetryShape>('ExtHostTelemetry'),
};

View File

@@ -162,7 +162,7 @@ const newCommands: ApiCommand[] = [
new ApiCommand(
'vscode.executeDocumentRenameProvider', '_executeDocumentRenameProvider', 'Execute rename provider.',
[ApiCommandArgument.Uri, ApiCommandArgument.Position, ApiCommandArgument.String.with('newName', 'The new symbol name')],
new ApiCommandResult<IWorkspaceEditDto, types.WorkspaceEdit | undefined>('A promise that resolves to a WorkspaceEdit.', value => {
new ApiCommandResult<IWorkspaceEditDto & { rejectReason?: string }, types.WorkspaceEdit | undefined>('A promise that resolves to a WorkspaceEdit.', value => {
if (!value) {
return undefined;
}

View File

@@ -5,7 +5,6 @@
import { MainContext, MainThreadBulkEditsShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { WorkspaceEdit } from 'vs/workbench/api/common/extHostTypeConverters';
import type * as vscode from 'vscode';
@@ -17,13 +16,12 @@ export class ExtHostBulkEdits {
constructor(
@IExtHostRpcService extHostRpc: IExtHostRpcService,
private readonly _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors,
private readonly _extHostNotebooks: ExtHostNotebookController,
) {
this._proxy = extHostRpc.getProxy(MainContext.MainThreadBulkEdits);
}
applyWorkspaceEdit(edit: vscode.WorkspaceEdit): Promise<boolean> {
const dto = WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors, this._extHostNotebooks);
const dto = WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors);
return this._proxy.$tryApplyWorkspaceEdit(dto);
}
}

View File

@@ -3,13 +3,13 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { VSBuffer } from 'vs/base/common/buffer';
import { CancellationToken } from 'vs/base/common/cancellation';
import { hash } from 'vs/base/common/hash';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { Schemas } from 'vs/base/common/network';
import { joinPath } from 'vs/base/common/resources';
import { URI, UriComponents } from 'vs/base/common/uri';
import * as modes from 'vs/editor/common/modes';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
@@ -209,7 +209,7 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
}));
}
async $createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, cancellation: CancellationToken) {
async $createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, untitledDocumentData: VSBuffer | undefined, cancellation: CancellationToken) {
const entry = this._editorProviders.get(viewType);
if (!entry) {
throw new Error(`No provider found for '${viewType}'`);
@@ -220,7 +220,7 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
}
const revivedResource = URI.revive(resource);
const document = await entry.provider.openCustomDocument(revivedResource, { backupId }, cancellation);
const document = await entry.provider.openCustomDocument(revivedResource, { backupId, untitledDocumentData: untitledDocumentData?.buffer }, cancellation);
let storageRoot: URI | undefined;
if (this.supportEditing(entry.provider) && this._extensionStoragePaths) {
@@ -251,9 +251,12 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
resource: UriComponents,
handle: extHostProtocol.WebviewHandle,
viewType: string,
title: string,
initData: {
title: string;
webviewOptions: extHostProtocol.IWebviewOptions;
panelOptions: extHostProtocol.IWebviewPanelOptions;
},
position: EditorGroupColumn,
options: modes.IWebviewOptions & modes.IWebviewPanelOptions,
cancellation: CancellationToken,
): Promise<void> {
const entry = this._editorProviders.get(viewType);
@@ -263,8 +266,8 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
const viewColumn = typeConverters.ViewColumn.to(position);
const webview = this._extHostWebview.createNewWebview(handle, options, entry.extension);
const panel = this._extHostWebviewPanels.createNewWebviewPanel(handle, viewType, title, viewColumn, options, webview);
const webview = this._extHostWebview.createNewWebview(handle, initData.webviewOptions, entry.extension);
const panel = this._extHostWebviewPanels.createNewWebviewPanel(handle, viewType, initData.title, viewColumn, initData.panelOptions, webview);
const revivedResource = URI.revive(resource);

View File

@@ -30,7 +30,7 @@ import type * as vscode from 'vscode';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { withNullAsUndefined } from 'vs/base/common/types';
import { IProcessEnvironment } from 'vs/base/common/platform';
import * as process from 'vs/base/common/process';
export const IExtHostDebugService = createDecorator<IExtHostDebugService>('IExtHostDebugService');
@@ -930,7 +930,7 @@ export class ExtHostDebugConsole {
export class ExtHostVariableResolverService extends AbstractVariableResolverService {
constructor(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors | undefined, configurationService: ExtHostConfigProvider, env?: IProcessEnvironment, workspaceService?: IExtHostWorkspace) {
constructor(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors | undefined, configurationService: ExtHostConfigProvider, workspaceService?: IExtHostWorkspace) {
super({
getFolderUri: (folderName: string): URI | undefined => {
const found = folders.filter(f => f.name === folderName);
@@ -946,10 +946,10 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ
return configurationService.getConfiguration(undefined, folderUri).get<string>(section);
},
getAppRoot: (): string | undefined => {
return env ? env['VSCODE_CWD'] : undefined;
return process.cwd();
},
getExecPath: (): string | undefined => {
return env ? env['VSCODE_EXEC_PATH'] : undefined;
return process.env['VSCODE_EXEC_PATH'];
},
getFilePath: (): string | undefined => {
if (editorService) {
@@ -990,7 +990,7 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ
}
return undefined;
}
}, undefined, env);
}, undefined, process.env);
}
}

View File

@@ -10,7 +10,6 @@ import type * as vscode from 'vscode';
import { MainContext, MainThreadDiagnosticsShape, ExtHostDiagnosticsShape, IMainContext } from './extHost.protocol';
import { DiagnosticSeverity } from './extHostTypes';
import * as converter from './extHostTypeConverters';
import { mergeSort } from 'vs/base/common/arrays';
import { Event, Emitter } from 'vs/base/common/event';
import { ILogService } from 'vs/platform/log/common/log';
import { ResourceMap } from 'vs/base/common/map';
@@ -78,7 +77,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
let lastUri: vscode.Uri | undefined;
// ensure stable-sort
first = mergeSort([...first], DiagnosticCollection._compareIndexedTuplesByUri);
first = [...first].sort(DiagnosticCollection._compareIndexedTuplesByUri);
for (const tuple of first) {
const [uri, diagnostics] = tuple;

View File

@@ -102,7 +102,7 @@ export class ExtHostDocuments implements ExtHostDocumentsShape {
return this._proxy.$tryCreateDocument(options).then(data => URI.revive(data));
}
public $acceptModelModeChanged(uriComponents: UriComponents, oldModeId: string, newModeId: string): void {
public $acceptModelModeChanged(uriComponents: UriComponents, newModeId: string): void {
const uri = URI.revive(uriComponents);
const data = this._documentsAndEditors.getDocument(uri);
if (!data) {

View File

@@ -25,7 +25,7 @@ import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensio
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, ExtensionMode, ExtensionRuntime } from 'vs/workbench/api/common/extHostTypes';
import { RemoteAuthorityResolverError, ExtensionKind, ExtensionMode, ExtensionRuntime } from 'vs/workbench/api/common/extHostTypes';
import { ResolvedAuthority, ResolvedOptions, RemoteAuthorityResolverErrorCode, IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
@@ -95,6 +95,8 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
private readonly _almostReadyToRunExtensions: Barrier;
private readonly _readyToStartExtensionHost: Barrier;
private readonly _readyToRunExtensions: Barrier;
private readonly _eagerExtensionsActivated: Barrier;
protected readonly _registry: ExtensionDescriptionRegistry;
private readonly _storage: ExtHostStorage;
private readonly _secretState: ExtHostSecretState;
@@ -140,6 +142,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
this._almostReadyToRunExtensions = new Barrier();
this._readyToStartExtensionHost = new Barrier();
this._readyToRunExtensions = new Barrier();
this._eagerExtensionsActivated = new Barrier();
this._registry = new ExtensionDescriptionRegistry(this._initData.extensions);
this._storage = new ExtHostStorage(this._extHostContext);
this._secretState = new ExtHostSecretState(this._extHostContext);
@@ -388,7 +391,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
const extensionMode = extensionDescription.isUnderDevelopment
? (this._initData.environment.extensionTestsLocationURI ? ExtensionMode.Test : ExtensionMode.Development)
: ExtensionMode.Production;
const installAge = Date.now() - new Date(this._initData.telemetryInfo.firstSessionDate).getTime();
const extensionKind = this._initData.remote.isRemote ? ExtensionKind.Workspace : ExtensionKind.UI;
this._logService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.identifier.value}`);
@@ -398,6 +401,8 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
this._storagePath.whenReady
]).then(() => {
const that = this;
let extension: vscode.Extension<any> | undefined;
return Object.freeze<vscode.ExtensionContext>({
globalState,
workspaceState,
@@ -413,22 +418,16 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
get storageUri() { return that._storagePath.workspaceValue(extensionDescription); },
get globalStorageUri() { return that._storagePath.globalValue(extensionDescription); },
get extensionMode() { return extensionMode; },
get extensionId() {
checkProposedApiEnabled(extensionDescription);
return extensionDescription.identifier.value;
},
get extensionVersion() {
checkProposedApiEnabled(extensionDescription);
return extensionDescription.version;
get extension() {
if (extension === undefined) {
extension = new Extension(that, extensionDescription.identifier, extensionDescription, extensionKind);
}
return extension;
},
get extensionRuntime() {
checkProposedApiEnabled(extensionDescription);
return that.extensionRuntime;
},
get isNewInstall() {
checkProposedApiEnabled(extensionDescription);
return isNaN(installAge) ? false : installAge < 1000 * 60 * 60 * 24; // installAge is less than a day;
},
get environmentVariableCollection() { return that._extHostTerminalService.getEnvironmentVariableCollection(extensionDescription); }
});
});
@@ -550,84 +549,61 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
);
}
private _handleExtensionTests(): Promise<void> {
return this._doHandleExtensionTests().then(undefined, error => {
public async $extensionTestsExecute(): Promise<number> {
await this._eagerExtensionsActivated.wait();
try {
return this._doHandleExtensionTests();
} catch (error) {
console.error(error); // ensure any error message makes it onto the console
return Promise.reject(error);
});
throw error;
}
}
private async _doHandleExtensionTests(): Promise<void> {
private async _doHandleExtensionTests(): Promise<number> {
const { extensionDevelopmentLocationURI, extensionTestsLocationURI } = this._initData.environment;
if (!(extensionDevelopmentLocationURI && extensionTestsLocationURI && extensionTestsLocationURI.scheme === Schemas.file)) {
return Promise.resolve(undefined);
if (!extensionDevelopmentLocationURI || !extensionTestsLocationURI || extensionTestsLocationURI.scheme !== Schemas.file) {
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
let testRunner: ITestRunner | INewTestRunner | undefined;
let requireError: Error | undefined;
try {
testRunner = await this._loadCommonJSModule(null, URI.file(extensionTestsPath), new ExtensionActivationTimesBuilder(false));
} catch (error) {
requireError = error;
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));
}
// Execute the runner if it follows the old `run` spec
if (testRunner && typeof testRunner.run === 'function') {
return new Promise<void>((c, e) => {
const oldTestRunnerCallback = (error: Error, failures: number | undefined) => {
if (error) {
e(error.toString());
} else {
c(undefined);
}
// after tests have run, we shutdown the host
this._testRunnerExit(error || (typeof failures === 'number' && failures > 0) ? 1 /* ERROR */ : 0 /* OK */);
};
const runResult = testRunner!.run(extensionTestsPath, oldTestRunnerCallback);
// Using the new API `run(): Promise<void>`
if (runResult && runResult.then) {
runResult
.then(() => {
c();
this._testRunnerExit(0);
})
.catch((err: Error) => {
e(err.toString());
this._testRunnerExit(1);
});
return new Promise<number>((resolve, reject) => {
const oldTestRunnerCallback = (error: Error, failures: number | undefined) => {
if (error) {
reject(error);
} else {
resolve((typeof failures === 'number' && failures > 0) ? 1 /* ERROR */ : 0 /* OK */);
}
});
}
};
// Otherwise make sure to shutdown anyway even in case of an error
else {
this._testRunnerExit(1 /* ERROR */);
}
const runResult = testRunner.run(extensionTestsPath, oldTestRunnerCallback);
return Promise.reject(new Error(requireError ? requireError.toString() : nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", extensionTestsPath)));
// Using the new API `run(): Promise<void>`
if (runResult && runResult.then) {
runResult
.then(() => {
resolve(0);
})
.catch((err: Error) => {
reject(err.toString());
});
}
});
}
private _testRunnerExit(code: number): void {
public async $extensionTestsExit(code: number): Promise<void> {
this._logService.info(`extension host terminating: test runner requested exit with code ${code}`);
this._logService.info(`exiting with code ${code}`);
this._logService.flush();
// wait at most 5000ms for the renderer to confirm our exit request and for the renderer socket to drain
// (this is to ensure all outstanding messages reach the renderer)
const exitPromise = this._mainThreadExtensionsProxy.$onExtensionHostExit(code);
const drainPromise = this._extHostContext.drain();
Promise.race([Promise.all([exitPromise, drainPromise]), timeout(5000)]).then(() => {
this._logService.info(`exiting with code ${code}`);
this._logService.flush();
this._hostUtils.exit(code);
});
this._hostUtils.exit(code);
}
private _startExtensionHost(): Promise<void> {
@@ -639,8 +615,8 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
return this._readyToStartExtensionHost.wait()
.then(() => this._readyToRunExtensions.open())
.then(() => this._handleEagerExtensions())
.then(() => this._handleExtensionTests())
.then(() => {
this._eagerExtensionsActivated.open();
this._logService.info(`eager extensions activated`);
});
}
@@ -840,3 +816,42 @@ export interface IExtHostExtensionService extends AbstractExtHostExtensionServic
onDidChangeRemoteConnectionData: Event<void>;
getRemoteConnectionData(): IRemoteConnectionData | null;
}
export class Extension<T> implements vscode.Extension<T> {
#extensionService: IExtHostExtensionService;
#originExtensionId: ExtensionIdentifier;
#identifier: ExtensionIdentifier;
readonly id: string;
readonly extensionUri: URI;
readonly extensionPath: string;
readonly packageJSON: IExtensionDescription;
readonly extensionKind: vscode.ExtensionKind;
constructor(extensionService: IExtHostExtensionService, originExtensionId: ExtensionIdentifier, description: IExtensionDescription, kind: ExtensionKind) {
this.#extensionService = extensionService;
this.#originExtensionId = originExtensionId;
this.#identifier = description.identifier;
this.id = description.identifier.value;
this.extensionUri = description.extensionLocation;
this.extensionPath = path.normalize(originalFSPath(description.extensionLocation));
this.packageJSON = description;
this.extensionKind = kind;
}
get isActive(): boolean {
return this.#extensionService.isActivated(this.#identifier);
}
get exports(): T {
if (this.packageJSON.api === 'none') {
return undefined!; // Strict nulloverride - Public api
}
return <T>this.#extensionService.getExtensionExports(this.#identifier);
}
activate(): Thenable<T> {
return this.#extensionService.activateByIdWithErrors(this.#identifier, { startup: false, extensionId: this.#originExtensionId, activationEvent: 'api' }).then(() => this.exports);
}
}

View File

@@ -412,7 +412,8 @@ class CodeActionAdapter {
const codeActionContext: vscode.CodeActionContext = {
diagnostics: allDiagnostics,
only: context.only ? new CodeActionKind(context.only) : undefined
only: context.only ? new CodeActionKind(context.only) : undefined,
triggerKind: typeConvert.CodeActionTriggerKind.to(context.trigger),
};
return asPromise(() => this._provider.provideCodeActions(doc, ran, codeActionContext, token)).then((commandsOrActions): extHostProtocol.ICodeActionListDto | undefined => {

View File

@@ -5,11 +5,11 @@
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import * as UUID from 'vs/base/common/uuid';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ExtHostNotebookShape, ICommandDto, IMainContext, IModelAddedData, INotebookDocumentPropertiesChangeData, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookEditorPropertiesChangeData, INotebookKernelInfoDto2, MainContext, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostNotebookShape, ICommandDto, IMainContext, IModelAddedData, INotebookDocumentPropertiesChangeData, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookEditorAddData, INotebookEditorPropertiesChangeData, INotebookKernelInfoDto2, MainContext, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol';
import { ILogService } from 'vs/platform/log/common/log';
import { CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
@@ -17,7 +17,7 @@ import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePa
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview';
import { CellStatusbarAlignment, CellUri, ICellRange, INotebookCellStatusBarEntry, INotebookExclusiveDocumentFilter, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookDataDto, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellEditType, CellStatusbarAlignment, CellUri, ICellRange, INotebookCellStatusBarEntry, INotebookExclusiveDocumentFilter, NotebookCellMetadata, NotebookCellExecutionState, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookDataDto, NullablePartialNotebookCellMetadata, IImmediateCellEditOperation } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import * as vscode from 'vscode';
import { ResourceMap } from 'vs/base/common/map';
import { ExtHostCell, ExtHostNotebookDocument } from './extHostNotebookDocument';
@@ -25,7 +25,9 @@ import { ExtHostNotebookEditor } from './extHostNotebookEditor';
import { IdGenerator } from 'vs/base/common/idGenerator';
import { IRelativePattern } from 'vs/base/common/glob';
import { assertIsDefined } from 'vs/base/common/types';
import { VSBuffer } from 'vs/base/common/buffer';
import { hash } from 'vs/base/common/hash';
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
class ExtHostWebviewCommWrapper extends Disposable {
private readonly _onDidReceiveDocumentMessage = new Emitter<any>();
@@ -123,7 +125,8 @@ export class ExtHostNotebookKernelProviderAdapter extends Disposable {
detail: kernel.detail,
isPreferred: kernel.isPreferred,
preloads: kernel.preloads,
supportedLanguages: kernel.supportedLanguages
supportedLanguages: kernel.supportedLanguages,
implementsInterrupt: !!kernel.interrupt
};
});
@@ -149,42 +152,25 @@ export class ExtHostNotebookKernelProviderAdapter extends Disposable {
}
}
async executeNotebook(kernelId: string, document: ExtHostNotebookDocument, cell: ExtHostCell | undefined) {
async executeNotebook(kernelId: string, document: ExtHostNotebookDocument, cellRange: ICellRange[]): Promise<void> {
const kernel = this._friendlyIdToKernel.get(document.uri)?.get(kernelId);
if (!kernel) {
return;
}
if (cell) {
return withToken(token => (kernel.executeCell as any)(document.notebookDocument, cell.cell, token));
} else {
return withToken(token => (kernel.executeAllCells as any)(document.notebookDocument, token));
}
const extCellRange = cellRange.map(c => typeConverters.NotebookCellRange.to(c));
return kernel.executeCellsRequest(document.notebookDocument, extCellRange);
}
async cancelNotebook(kernelId: string, document: ExtHostNotebookDocument, cell: ExtHostCell | undefined) {
async interruptNotebookExecution(kernelId: string, document: ExtHostNotebookDocument): Promise<void> {
const kernel = this._friendlyIdToKernel.get(document.uri)?.get(kernelId);
if (!kernel) {
if (!kernel || !kernel.interrupt) {
return;
}
if (cell) {
return kernel.cancelCellExecution(document.notebookDocument, cell.cell);
} else {
return kernel.cancelAllCellsExecution(document.notebookDocument);
}
}
}
// TODO@roblou remove 'token' passed to all execute APIs once extensions are updated
async function withToken(cb: (token: CancellationToken) => any) {
const source = new CancellationTokenSource();
try {
await cb(source.token);
} finally {
source.dispose();
return kernel.interrupt(document.notebookDocument);
}
}
@@ -207,6 +193,7 @@ export class NotebookEditorDecorationType {
}
}
type NotebookContentProviderData = {
readonly provider: vscode.NotebookContentProvider;
readonly extension: IExtensionDescription;
@@ -232,12 +219,12 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
readonly onDidChangeNotebookCells = this._onDidChangeNotebookCells.event;
private readonly _onDidChangeCellOutputs = new Emitter<vscode.NotebookCellOutputsChangeEvent>();
readonly onDidChangeCellOutputs = this._onDidChangeCellOutputs.event;
private readonly _onDidChangeCellLanguage = new Emitter<vscode.NotebookCellLanguageChangeEvent>();
readonly onDidChangeCellLanguage = this._onDidChangeCellLanguage.event;
private readonly _onDidChangeCellMetadata = new Emitter<vscode.NotebookCellMetadataChangeEvent>();
readonly onDidChangeCellMetadata = this._onDidChangeCellMetadata.event;
private readonly _onDidChangeActiveNotebookEditor = new Emitter<vscode.NotebookEditor | undefined>();
readonly onDidChangeActiveNotebookEditor = this._onDidChangeActiveNotebookEditor.event;
private readonly _onDidChangeCellExecutionState = new Emitter<vscode.NotebookCellExecutionStateChangeEvent>();
readonly onDidChangeNotebookCellExecutionState = this._onDidChangeCellExecutionState.event;
private _activeNotebookEditor: ExtHostNotebookEditor | undefined;
get activeNotebookEditor(): vscode.NotebookEditor | undefined {
@@ -259,10 +246,13 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
private _onDidChangeVisibleNotebookEditors = new Emitter<vscode.NotebookEditor[]>();
onDidChangeVisibleNotebookEditors = this._onDidChangeVisibleNotebookEditors.event;
private _activeExecutions = new ResourceMap<NotebookCellExecutionTask>();
constructor(
mainContext: IMainContext,
commands: ExtHostCommands,
private _documentsAndEditors: ExtHostDocumentsAndEditors,
private _textDocumentsAndEditors: ExtHostDocumentsAndEditors,
private _textDocuments: ExtHostDocuments,
private readonly _webviewInitData: WebviewInitData,
private readonly logService: ILogService,
private readonly _extensionStoragePaths: IExtensionStoragePaths,
@@ -337,7 +327,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
let listener: IDisposable | undefined;
if (provider.onDidChangeNotebookContentOptions) {
listener = provider.onDidChangeNotebookContentOptions(() => {
this._proxy.$updateNotebookProviderOptions(viewType, provider.options);
const internalOptions = typeConverters.NotebookDocumentContentOptions.from(provider.options);
this._proxy.$updateNotebookProviderOptions(viewType, internalOptions);
});
}
@@ -349,9 +340,10 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
console.warn(`Notebook content provider view options file name pattern is invalid ${options?.viewOptions?.filenamePattern}`);
}
const internalOptions = typeConverters.NotebookDocumentContentOptions.from(options);
this._proxy.$registerNotebookProvider({ id: extension.identifier, location: extension.extensionLocation, description: extension.description }, viewType, {
transientOutputs: options?.transientOutputs || false,
transientMetadata: options?.transientMetadata || {},
transientOutputs: internalOptions.transientOutputs,
transientMetadata: internalOptions.transientMetadata,
viewOptions: options?.viewOptions && viewOptionsFilenamePattern ? { displayName: options.viewOptions.displayName, filenamePattern: viewOptionsFilenamePattern, exclusive: options.viewOptions.exclusive || false } : undefined
});
@@ -382,13 +374,13 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
return new NotebookEditorDecorationType(this._proxy, options).value;
}
async openNotebookDocument(uriComponents: UriComponents, viewType?: string): Promise<vscode.NotebookDocument> {
const cached = this._documents.get(URI.revive(uriComponents));
async openNotebookDocument(uri: URI): Promise<vscode.NotebookDocument> {
const cached = this._documents.get(uri);
if (cached) {
return cached.notebookDocument;
}
await this._proxy.$tryOpenDocument(uriComponents, viewType);
const document = this._documents.get(URI.revive(uriComponents));
const canonicalUri = await this._proxy.$tryOpenDocument(uri);
const document = this._documents.get(URI.revive(canonicalUri));
return assertIsDefined(document?.notebookDocument);
}
@@ -408,7 +400,12 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
return callback(provider, document);
}
async showNotebookDocument(notebookDocument: vscode.NotebookDocument, options?: vscode.NotebookDocumentShowOptions): Promise<vscode.NotebookEditor> {
async showNotebookDocument(notebookOrUri: vscode.NotebookDocument | URI, options?: vscode.NotebookDocumentShowOptions): Promise<vscode.NotebookEditor> {
if (URI.isUri(notebookOrUri)) {
notebookOrUri = await this.openNotebookDocument(notebookOrUri);
}
let resolvedOptions: INotebookDocumentShowOptions;
if (typeof options === 'object') {
resolvedOptions = {
@@ -423,7 +420,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
};
}
const editorId = await this._proxy.$tryShowNotebookDocument(notebookDocument.uri, notebookDocument.viewType, resolvedOptions);
const editorId = await this._proxy.$tryShowNotebookDocument(notebookOrUri.uri, notebookOrUri.viewType, resolvedOptions);
const editor = editorId && this._editors.get(editorId)?.editor;
if (editor) {
@@ -431,9 +428,9 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
}
if (editorId) {
throw new Error(`Could NOT open editor for "${notebookDocument.toString()}" because another editor opened in the meantime.`);
throw new Error(`Could NOT open editor for "${notebookOrUri.toString()}" because another editor opened in the meantime.`);
} else {
throw new Error(`Could NOT open editor for "${notebookDocument.toString()}".`);
throw new Error(`Could NOT open editor for "${notebookOrUri.toString()}".`);
}
}
@@ -474,32 +471,88 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
await provider.provider.resolveNotebook(document.notebookDocument, webComm.contentProviderComm);
}
async $executeNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise<void> {
async $executeNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellRange: ICellRange[]): Promise<void> {
await this._withAdapter(handle, uri, async (adapter, document) => {
const cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined;
return adapter.executeNotebook(kernelId, document, cell);
return adapter.executeNotebook(kernelId, document, cellRange);
});
}
async $cancelNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise<void> {
await this._withAdapter(handle, uri, async (adapter, document) => {
const cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined;
// --- serialize/deserialize
return adapter.cancelNotebook(kernelId, document, cell);
private _handlePool = 0;
private readonly _notebookSerializer = new Map<number, vscode.NotebookSerializer>();
registerNotebookSerializer(extension: IExtensionDescription, viewType: string, serializer: vscode.NotebookSerializer, options?: vscode.NotebookDocumentContentOptions): vscode.Disposable {
const handle = this._handlePool++;
this._notebookSerializer.set(handle, serializer);
const internalOptions = typeConverters.NotebookDocumentContentOptions.from(options);
this._proxy.$registerNotebookSerializer(
handle,
{ id: extension.identifier, location: extension.extensionLocation, description: extension.description },
viewType,
internalOptions
);
return toDisposable(() => {
this._proxy.$unregisterNotebookSerializer(handle);
});
}
async $dataToNotebook(handle: number, bytes: VSBuffer): Promise<NotebookDataDto> {
const serializer = this._notebookSerializer.get(handle);
if (!serializer) {
throw new Error('NO serializer found');
}
const data = await serializer.dataToNotebook(bytes.buffer);
return {
metadata: typeConverters.NotebookDocumentMetadata.from(data.metadata),
cells: data.cells.map(typeConverters.NotebookCellData.from),
};
}
async $notebookToData(handle: number, data: NotebookDataDto): Promise<VSBuffer> {
const serializer = this._notebookSerializer.get(handle);
if (!serializer) {
throw new Error('NO serializer found');
}
const bytes = await serializer.notebookToData({
metadata: typeConverters.NotebookDocumentMetadata.to(data.metadata),
cells: data.cells.map(typeConverters.NotebookCellData.to)
});
return VSBuffer.wrap(bytes);
}
async $cancelNotebookCellExecution(handle: number, uri: UriComponents, kernelId: string, cellRange: ICellRange[]): Promise<void> {
await this._withAdapter(handle, uri, async (adapter, document) => {
return adapter.interruptNotebookExecution(kernelId, document);
});
const document = this._documents.get(URI.revive(uri));
if (!document) {
return;
}
for (let range of cellRange) {
for (let i = range.start; i < range.end; i++) {
const cell = document.getCellFromIndex(i);
if (cell) {
this.cancelOneNotebookCellExecution(cell);
}
}
}
}
private cancelOneNotebookCellExecution(cell: ExtHostCell): void {
const execution = this._activeExecutions.get(cell.uri);
execution?.cancel();
}
// --- open, save, saveAs, backup
async $openNotebook(viewType: string, uri: UriComponents, backupId?: string): Promise<NotebookDataDto> {
async $openNotebook(viewType: string, uri: UriComponents, backupId: string | undefined, untitledDocumentData: VSBuffer | undefined, token: CancellationToken): Promise<NotebookDataDto> {
const { provider } = this._getProviderData(viewType);
const data = await provider.openNotebook(URI.revive(uri), { backupId });
const data = await provider.openNotebook(URI.revive(uri), { backupId, untitledDocumentData: untitledDocumentData?.buffer }, token);
return {
metadata: {
...notebookDocumentMetadataDefaults,
...data.metadata
},
metadata: typeConverters.NotebookDocumentMetadata.from(data.metadata),
cells: data.cells.map(typeConverters.NotebookCellData.from),
};
}
@@ -569,32 +622,31 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
$acceptEditorPropertiesChanged(id: string, data: INotebookEditorPropertiesChangeData): void {
this.logService.debug('ExtHostNotebook#$acceptEditorPropertiesChanged', id, data);
let editor: { editor: ExtHostNotebookEditor; } | undefined;
this._editors.forEach(e => {
if (e.editor.id === id) {
editor = e;
}
});
const editor = this._editors.get(id);
if (!editor) {
return;
throw new Error(`unknown text editor: ${id}`);
}
// ONE: make all state updates
if (data.visibleRanges) {
editor.editor._acceptVisibleRanges(data.visibleRanges.ranges.map(typeConverters.NotebookCellRange.to));
}
if (data.selections) {
editor.editor._acceptSelections(data.selections.selections.map(typeConverters.NotebookCellRange.to));
}
// TWO: send all events after states have been updated
if (data.visibleRanges) {
this._onDidChangeNotebookEditorVisibleRanges.fire({
notebookEditor: editor.editor.editor,
visibleRanges: editor.editor.editor.visibleRanges
});
}
if (data.selections) {
editor.editor._acceptSelections(data.selections.selections);
this._onDidChangeNotebookEditorSelection.fire({
this._onDidChangeNotebookEditorSelection.fire(Object.freeze({
notebookEditor: editor.editor.editor,
selection: editor.editor.editor.selection
});
selections: editor.editor.editor.selections
}));
}
}
@@ -602,9 +654,12 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
this.logService.debug('ExtHostNotebook#$acceptDocumentPropertiesChanged', uri.path, data);
const document = this._getNotebookDocument(URI.revive(uri));
document.acceptDocumentPropertiesChanged(data);
if (data.metadata) {
this._onDidChangeNotebookDocumentMetadata.fire({ document: document.notebookDocument });
}
}
private _createExtHostEditor(document: ExtHostNotebookDocument, editorId: string, selections: ICellRange[], visibleRanges: extHostTypes.NotebookCellRange[]) {
private _createExtHostEditor(document: ExtHostNotebookDocument, editorId: string, data: INotebookEditorAddData) {
const revivedUri = document.uri;
let webComm = this._webviewComm.get(editorId);
@@ -617,11 +672,12 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
editorId,
document.notebookDocument.viewType,
this._proxy,
document
document,
data.visibleRanges.map(typeConverters.NotebookCellRange.to),
data.selections.map(typeConverters.NotebookCellRange.to),
typeof data.viewColumn === 'number' ? typeConverters.ViewColumn.to(data.viewColumn) : undefined
);
editor._acceptSelections(selections);
editor._acceptVisibleRanges(visibleRanges);
this._editors.get(editorId)?.editor.dispose();
this._editors.set(editorId, { editor });
@@ -638,7 +694,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
if (document) {
document.dispose();
this._documents.delete(revivedUri);
this._documentsAndEditors.$acceptDocumentsAndEditorsDelta({ removedDocuments: document.notebookDocument.cells.map(cell => cell.uri) });
this._textDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({ removedDocuments: document.notebookDocument.cells.map(cell => cell.document.uri) });
this._onDidCloseNotebookDocument.fire(document.notebookDocument);
}
@@ -667,7 +723,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
const document = new ExtHostNotebookDocument(
this._proxy,
this._documentsAndEditors,
this._textDocumentsAndEditors,
this._textDocuments,
{
emitModelChange(event: vscode.NotebookCellsChangeEvent): void {
that._onDidChangeNotebookCells.fire(event);
@@ -675,34 +732,24 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
emitCellOutputsChange(event: vscode.NotebookCellOutputsChangeEvent): void {
that._onDidChangeCellOutputs.fire(event);
},
emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void {
that._onDidChangeCellLanguage.fire(event);
},
emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void {
that._onDidChangeCellMetadata.fire(event);
},
emitDocumentMetadataChange(event: vscode.NotebookDocumentMetadataChangeEvent): void {
that._onDidChangeNotebookDocumentMetadata.fire(event);
emitCellExecutionStateChange(event: vscode.NotebookCellExecutionStateChangeEvent): void {
that._onDidChangeCellExecutionState.fire(event);
}
},
viewType,
modelData.contentOptions,
modelData.metadata ? typeConverters.NotebookDocumentMetadata.to(modelData.metadata) : new extHostTypes.NotebookDocumentMetadata(),
uri,
);
document.acceptModelChanged({
versionId: modelData.versionId,
rawEvents: [
{
kind: NotebookCellsChangeType.Initialize,
changes: [[
0,
0,
modelData.cells
]]
}
]
rawEvents: [{
kind: NotebookCellsChangeType.Initialize,
changes: [[0, 0, modelData.cells]]
}]
}, false);
// add cell document as vscode.TextDocument
@@ -710,7 +757,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
this._documents.get(uri)?.dispose();
this._documents.set(uri, document);
this._documentsAndEditors.$acceptDocumentsAndEditorsDelta({ addedDocuments: addedCellDocuments });
this._textDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({ addedDocuments: addedCellDocuments });
this._onDidOpenNotebookDocument.fire(document.notebookDocument);
}
@@ -726,7 +773,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
const document = this._documents.get(revivedUri);
if (document) {
this._createExtHostEditor(document, editorModelData.id, editorModelData.selections, editorModelData.visibleRanges.map(typeConverters.NotebookCellRange.to));
this._createExtHostEditor(document, editorModelData.id, editorModelData);
editorChanged = true;
}
}
@@ -771,23 +818,13 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
this._onDidChangeVisibleNotebookEditors.fire(this.visibleNotebookEditors);
}
if (delta.newActiveEditor === null) {
// clear active notebook as current active editor is non-notebook editor
this._activeNotebookEditor = undefined;
} else if (delta.newActiveEditor) {
this._activeNotebookEditor = this._editors.get(delta.newActiveEditor)?.editor;
}
if (delta.newActiveEditor !== undefined) {
if (delta.newActiveEditor) {
this._activeNotebookEditor = this._editors.get(delta.newActiveEditor)?.editor;
this._activeNotebookEditor?._acceptActive(true);
for (const e of this._editors.values()) {
if (e.editor !== this._activeNotebookEditor) {
e.editor._acceptActive(false);
}
}
} else {
// clear active notebook as current active editor is non-notebook editor
this._activeNotebookEditor = undefined;
for (const e of this._editors.values()) {
e.editor._acceptActive(false);
}
}
this._onDidChangeActiveNotebookEditor.fire(this._activeNotebookEditor?.editor);
}
}
@@ -796,7 +833,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
const statusBarItem = new NotebookCellStatusBarItemInternal(this._proxy, this._commandsConverter, cell, alignment, priority);
// Look up the ExtHostCell for this NotebookCell URI, bind to its disposable lifecycle
const parsedUri = CellUri.parse(cell.uri);
const parsedUri = CellUri.parse(cell.document.uri);
if (parsedUri) {
const document = this._documents.get(parsedUri.notebook);
if (document) {
@@ -809,6 +846,35 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
return statusBarItem;
}
createNotebookCellExecution(docUri: vscode.Uri, index: number, kernelId: string): vscode.NotebookCellExecutionTask | undefined {
const document = this.lookupNotebookDocument(docUri);
if (!document) {
throw new Error(`Invalid cell uri/index: ${docUri}, ${index}`);
}
const cell = document.getCellFromIndex(index);
if (!cell) {
throw new Error(`Invalid cell uri/index: ${docUri}, ${index}`);
}
// TODO@roblou also validate kernelId, once kernel has moved from editor to document
if (this._activeExecutions.has(cell.uri)) {
return;
}
const execution = new NotebookCellExecutionTask(docUri, document, cell, this._proxy);
this._activeExecutions.set(cell.uri, execution);
const listener = execution.onDidChangeState(() => {
if (execution.state === NotebookCellExecutionTaskState.Resolved) {
execution.dispose();
listener.dispose();
this._activeExecutions.delete(cell.uri);
}
});
return execution.asApiObject();
}
}
export class NotebookCellStatusBarItemInternal extends Disposable {
@@ -946,7 +1012,7 @@ export class NotebookCellStatusBarItemInternal extends Disposable {
const entry: INotebookCellStatusBarEntry = {
alignment: this.alignment === extHostTypes.NotebookCellStatusBarAlignment.Left ? CellStatusbarAlignment.LEFT : CellStatusbarAlignment.RIGHT,
cellResource: this.cell.uri,
cellResource: this.cell.document.uri,
command: this._command?.internal,
text: this.text,
tooltip: this.tooltip,
@@ -985,3 +1051,157 @@ function createNotebookCellStatusBarApiItem(internalItem: NotebookCellStatusBarI
dispose() { internalItem.dispose(); }
});
}
enum NotebookCellExecutionTaskState {
Init,
Started,
Resolved
}
class NotebookCellExecutionTask extends Disposable {
private _onDidChangeState = new Emitter<void>();
readonly onDidChangeState = this._onDidChangeState.event;
private _state = NotebookCellExecutionTaskState.Init;
get state(): NotebookCellExecutionTaskState { return this._state; }
private readonly _tokenSource: CancellationTokenSource;
private _executionOrder: number | undefined;
constructor(
private readonly _uri: vscode.Uri,
private readonly _document: ExtHostNotebookDocument,
private readonly _cell: ExtHostCell,
private readonly _proxy: MainThreadNotebookShape) {
super();
this._tokenSource = this._register(new CancellationTokenSource());
this._executionOrder = _cell.internalMetadata.executionOrder;
this.mixinMetadata({
runState: NotebookCellExecutionState.Pending,
lastRunDuration: null,
executionOrder: null
});
}
cancel(): void {
this._tokenSource.cancel();
}
private async applyEdits(edits: IImmediateCellEditOperation[]): Promise<void> {
return this._proxy.$applyEdits(this._uri, edits, false);
}
private verifyStateForOutput() {
if (this._state === NotebookCellExecutionTaskState.Init) {
throw new Error('Must call start before modifying cell output');
}
if (this._state === NotebookCellExecutionTaskState.Resolved) {
throw new Error('Cannot modify cell output after calling resolve');
}
}
private mixinMetadata(mixinMetadata: NullablePartialNotebookCellMetadata) {
const edits: IImmediateCellEditOperation[] = [
{ editType: CellEditType.PartialMetadata, handle: this._cell.handle, metadata: mixinMetadata }
];
this.applyEdits(edits);
}
private cellIndexToHandle(cellIndex: number | undefined): number | undefined {
const cell = typeof cellIndex === 'number' ? this._document.getCellFromIndex(cellIndex) : this._cell;
if (!cell) {
return;
}
return cell.handle;
}
asApiObject(): vscode.NotebookCellExecutionTask {
const that = this;
return Object.freeze(<vscode.NotebookCellExecutionTask>{
get document() { return that._document.notebookDocument; },
get cell() { return that._cell.cell; },
get executionOrder() { return that._executionOrder; },
set executionOrder(v: number | undefined) {
that._executionOrder = v;
that.mixinMetadata({
executionOrder: v
});
},
start(context?: vscode.NotebookCellExecuteStartContext): void {
if (that._state === NotebookCellExecutionTaskState.Resolved || that._state === NotebookCellExecutionTaskState.Started) {
throw new Error('Cannot call start again');
}
that._state = NotebookCellExecutionTaskState.Started;
that._onDidChangeState.fire();
that.mixinMetadata({
runState: NotebookCellExecutionState.Executing,
runStartTime: context?.startTime
});
},
end(result?: vscode.NotebookCellExecuteEndContext): void {
if (that._state === NotebookCellExecutionTaskState.Resolved) {
throw new Error('Cannot call resolve twice');
}
that._state = NotebookCellExecutionTaskState.Resolved;
that._onDidChangeState.fire();
that.mixinMetadata({
runState: NotebookCellExecutionState.Idle,
lastRunSuccess: result?.success ?? null,
lastRunDuration: result?.duration ?? null,
});
},
clearOutput(cellIndex?: number): Thenable<void> {
that.verifyStateForOutput();
return this.replaceOutput([], cellIndex);
},
async appendOutput(outputs: vscode.NotebookCellOutput | vscode.NotebookCellOutput[], cellIndex?: number): Promise<void> {
that.verifyStateForOutput();
const handle = that.cellIndexToHandle(cellIndex);
if (typeof handle !== 'number') {
return;
}
outputs = Array.isArray(outputs) ? outputs : [outputs];
return that.applyEdits([{ editType: CellEditType.Output, handle, append: true, outputs: outputs.map(typeConverters.NotebookCellOutput.from) }]);
},
async replaceOutput(outputs: vscode.NotebookCellOutput | vscode.NotebookCellOutput[], cellIndex?: number): Promise<void> {
that.verifyStateForOutput();
const handle = that.cellIndexToHandle(cellIndex);
if (typeof handle !== 'number') {
return;
}
outputs = Array.isArray(outputs) ? outputs : [outputs];
return that.applyEdits([{ editType: CellEditType.Output, handle, outputs: outputs.map(typeConverters.NotebookCellOutput.from) }]);
},
async appendOutputItems(items: vscode.NotebookCellOutputItem | vscode.NotebookCellOutputItem[], outputId: string): Promise<void> {
that.verifyStateForOutput();
items = Array.isArray(items) ? items : [items];
return that.applyEdits([{ editType: CellEditType.OutputItems, append: true, items: items.map(typeConverters.NotebookCellOutputItem.from), outputId }]);
},
async replaceOutputItems(items: vscode.NotebookCellOutputItem | vscode.NotebookCellOutputItem[], outputId: string): Promise<void> {
that.verifyStateForOutput();
items = Array.isArray(items) ? items : [items];
return that.applyEdits([{ editType: CellEditType.OutputItems, items: items.map(typeConverters.NotebookCellOutputItem.from), outputId }]);
},
token: that._tokenSource.token
});
}
}

View File

@@ -56,7 +56,6 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD
}
};
this._disposables.add(extHostNotebooks.onDidChangeCellLanguage(e => documentChange(e.document)));
this._disposables.add(extHostNotebooks.onDidChangeNotebookCells(e => documentChange(e.document)));
}
@@ -75,8 +74,8 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD
const cellLengths: number[] = [];
const cellLineCounts: number[] = [];
for (const cell of this._notebook.cells) {
if (cell.cellKind === types.NotebookCellKind.Code && (!this._selector || score(this._selector, cell.uri, cell.language, true))) {
this._cellUris.set(cell.uri, this._cells.length);
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);
cellLengths.push(cell.document.getText().length + 1);
cellLineCounts.push(cell.document.lineCount);
@@ -162,7 +161,7 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD
const range = new types.Range(startPos, endPos);
const startCell = this._cells[startIdx.index];
return new types.Location(startCell.uri, <types.Range>startCell.document.validateRange(range));
return new types.Location(startCell.document.uri, <types.Range>startCell.document.validateRange(range));
}
contains(uri: vscode.Uri): boolean {

View File

@@ -6,24 +6,28 @@
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 { 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';
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
import { IMainCellDto, IOutputDto, IOutputItemDto, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { IMainCellDto, IOutputDto, IOutputItemDto, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2 } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import * as vscode from 'vscode';
class RawContentChangeEvent {
constructor(readonly start: number, readonly deletedCount: number, readonly deletedItems: vscode.NotebookCell[], readonly items: ExtHostCell[]) { }
static asApiEvent(event: RawContentChangeEvent): vscode.NotebookCellsChangeData {
return Object.freeze({
start: event.start,
deletedCount: event.deletedCount,
deletedItems: event.deletedItems,
items: event.items.map(data => data.cell)
static asApiEvents(events: RawContentChangeEvent[]): readonly vscode.NotebookCellsChangeData[] {
return events.map(event => {
return {
start: event.start,
deletedCount: event.deletedCount,
deletedItems: event.deletedItems,
items: event.items.map(data => data.cell)
};
});
}
}
@@ -47,7 +51,9 @@ export class ExtHostCell {
private _outputs: extHostTypes.NotebookCellOutput[];
private _metadata: extHostTypes.NotebookCellMetadata;
private _previousResult: vscode.NotebookCellExecutionSummary | undefined;
private _internalMetadata: NotebookCellMetadata;
readonly handle: number;
readonly uri: URI;
readonly cellKind: CellKind;
@@ -63,7 +69,9 @@ export class ExtHostCell {
this.uri = URI.revive(_cellData.uri);
this.cellKind = _cellData.cellKind;
this._outputs = _cellData.outputs.map(extHostTypeConverters.NotebookCellOutput.to);
this._metadata = extHostTypeConverters.NotebookCellMetadata.to(_cellData.metadata ?? {});
this._internalMetadata = _cellData.metadata ?? {};
this._metadata = extHostTypeConverters.NotebookCellMetadata.to(this._internalMetadata);
this._previousResult = extHostTypeConverters.NotebookCellPreviousExecutionResult.to(this._internalMetadata);
}
dispose() {
@@ -71,6 +79,10 @@ export class ExtHostCell {
this._onDidDispose.dispose();
}
get internalMetadata(): NotebookCellMetadata {
return this._internalMetadata;
}
get cell(): vscode.NotebookCell {
if (!this._cell) {
const that = this;
@@ -78,17 +90,14 @@ export class ExtHostCell {
if (!data) {
throw new Error(`MISSING extHostDocument for notebook cell: ${this.uri}`);
}
this._cell = Object.freeze({
this._cell = Object.freeze<vscode.NotebookCell>({
get index() { return that._notebook.getCellIndex(that); },
notebook: that._notebook.notebookDocument,
uri: that.uri,
cellKind: extHostTypeConverters.NotebookCellKind.to(this._cellData.cellKind),
kind: extHostTypeConverters.NotebookCellKind.to(this._cellData.cellKind),
document: data.document,
get language() { return data!.document.languageId; },
get outputs() { return that._outputs.slice(0); },
set outputs(_value) { throw new Error('Use WorkspaceEdit to update cell outputs.'); },
get metadata() { return that._metadata; },
set metadata(_value) { throw new Error('Use WorkspaceEdit to update cell metadata.'); },
get latestExecutionSummary() { return that._previousResult; }
});
}
return this._cell;
@@ -110,16 +119,17 @@ export class ExtHostCell {
}
setMetadata(newMetadata: NotebookCellMetadata): void {
this._internalMetadata = newMetadata;
this._metadata = extHostTypeConverters.NotebookCellMetadata.to(newMetadata);
this._previousResult = extHostTypeConverters.NotebookCellPreviousExecutionResult.to(newMetadata);
}
}
export interface INotebookEventEmitter {
emitModelChange(events: vscode.NotebookCellsChangeEvent): void;
emitDocumentMetadataChange(event: vscode.NotebookDocumentMetadataChangeEvent): void;
emitCellOutputsChange(event: vscode.NotebookCellOutputsChangeEvent): void;
emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void;
emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void;
emitCellExecutionStateChange(event: vscode.NotebookCellExecutionStateChangeEvent): void;
}
@@ -140,10 +150,10 @@ export class ExtHostNotebookDocument extends Disposable {
constructor(
private readonly _proxy: MainThreadNotebookShape,
private readonly _documentsAndEditors: ExtHostDocumentsAndEditors,
private readonly _textDocumentsAndEditors: ExtHostDocumentsAndEditors,
private readonly _textDocuments: ExtHostDocuments,
private readonly _emitter: INotebookEventEmitter,
private readonly _viewType: string,
private readonly _contentOptions: vscode.NotebookDocumentContentOptions,
private _metadata: extHostTypes.NotebookDocumentMetadata,
readonly uri: URI,
) {
@@ -170,7 +180,6 @@ export class ExtHostNotebookDocument extends Disposable {
get cells(): ReadonlyArray<vscode.NotebookCell> { return that._cells.map(cell => cell.cell); },
get metadata() { return that._metadata; },
set metadata(_value: Required<vscode.NotebookDocumentMetadata>) { throw new Error('Use WorkspaceEdit to update metadata.'); },
get contentOptions() { return that._contentOptions; },
save() { return that._save(); }
});
}
@@ -188,39 +197,37 @@ export class ExtHostNotebookDocument extends Disposable {
}
acceptDocumentPropertiesChanged(data: INotebookDocumentPropertiesChangeData) {
const newMetadata = {
...notebookDocumentMetadataDefaults,
...data.metadata
};
this._metadata = this._metadata.with(newMetadata);
this._emitter.emitDocumentMetadataChange({ document: this.notebookDocument });
if (data.metadata) {
this._metadata = this._metadata.with(data.metadata);
}
}
acceptModelChanged(event: NotebookCellsChangedEventDto, isDirty: boolean): void {
this._versionId = event.versionId;
this._isDirty = isDirty;
event.rawEvents.forEach(e => {
if (e.kind === NotebookCellsChangeType.Initialize) {
this._spliceNotebookCells(e.changes, true);
} if (e.kind === NotebookCellsChangeType.ModelChange) {
this._spliceNotebookCells(e.changes, false);
} else if (e.kind === NotebookCellsChangeType.Move) {
this._moveCell(e.index, e.newIdx);
} else if (e.kind === NotebookCellsChangeType.Output) {
this._setCellOutputs(e.index, e.outputs);
} else if (e.kind === NotebookCellsChangeType.OutputItem) {
this._setCellOutputItems(e.index, e.outputId, e.append, e.outputItems);
} else if (e.kind === NotebookCellsChangeType.ChangeLanguage) {
this._changeCellLanguage(e.index, e.language);
} else if (e.kind === NotebookCellsChangeType.ChangeCellMetadata) {
this._changeCellMetadata(e.index, e.metadata);
for (const rawEvent of event.rawEvents) {
if (rawEvent.kind === NotebookCellsChangeType.Initialize) {
this._spliceNotebookCells(rawEvent.changes, true);
} if (rawEvent.kind === NotebookCellsChangeType.ModelChange) {
this._spliceNotebookCells(rawEvent.changes, false);
} else if (rawEvent.kind === NotebookCellsChangeType.Move) {
this._moveCell(rawEvent.index, rawEvent.newIdx);
} else if (rawEvent.kind === NotebookCellsChangeType.Output) {
this._setCellOutputs(rawEvent.index, rawEvent.outputs);
} else if (rawEvent.kind === NotebookCellsChangeType.OutputItem) {
this._setCellOutputItems(rawEvent.index, rawEvent.outputId, rawEvent.append, rawEvent.outputItems);
} else if (rawEvent.kind === NotebookCellsChangeType.ChangeLanguage) {
this._changeCellLanguage(rawEvent.index, rawEvent.language);
} else if (rawEvent.kind === NotebookCellsChangeType.ChangeCellMetadata) {
this._changeCellMetadata(rawEvent.index, rawEvent.metadata);
}
});
}
}
private async _save(): Promise<boolean> {
if (this._disposed) {
return Promise.reject(new Error('Document has been closed'));
return Promise.reject(new Error('Notebook has been closed'));
}
return this._proxy.$trySaveDocument(this.uri);
}
@@ -238,7 +245,7 @@ export class ExtHostNotebookDocument extends Disposable {
const cellDtos = splice[2];
const newCells = cellDtos.map(cell => {
const extCell = new ExtHostCell(this, this._documentsAndEditors, cell);
const extCell = new ExtHostCell(this, this._textDocumentsAndEditors, cell);
if (!initialization) {
addedCellDocuments.push(ExtHostCell.asModelAddData(this.notebookDocument, cell));
@@ -268,62 +275,67 @@ export class ExtHostNotebookDocument extends Disposable {
contentChangeEvents.push(changeEvent);
});
this._documentsAndEditors.acceptDocumentsAndEditorsDelta({
this._textDocumentsAndEditors.acceptDocumentsAndEditorsDelta({
addedDocuments: addedCellDocuments,
removedDocuments: removedCellDocuments
});
if (!initialization) {
this._emitter.emitModelChange({
this._emitter.emitModelChange(deepFreeze({
document: this.notebookDocument,
changes: contentChangeEvents.map(RawContentChangeEvent.asApiEvent)
});
changes: RawContentChangeEvent.asApiEvents(contentChangeEvents)
}));
}
}
private _moveCell(index: number, newIdx: number): void {
const cells = this._cells.splice(index, 1);
this._cells.splice(newIdx, 0, ...cells);
const changes: vscode.NotebookCellsChangeData[] = [{
start: index,
deletedCount: 1,
deletedItems: cells.map(data => data.cell),
items: []
}, {
start: newIdx,
deletedCount: 0,
deletedItems: [],
items: cells.map(data => data.cell)
}];
this._emitter.emitModelChange({
const changes = [
new RawContentChangeEvent(index, 1, cells.map(c => c.cell), []),
new RawContentChangeEvent(newIdx, 0, [], cells)
];
this._emitter.emitModelChange(deepFreeze({
document: this.notebookDocument,
changes
});
changes: RawContentChangeEvent.asApiEvents(changes)
}));
}
private _setCellOutputs(index: number, outputs: IOutputDto[]): void {
const cell = this._cells[index];
cell.setOutputs(outputs);
this._emitter.emitCellOutputsChange({ document: this.notebookDocument, cells: [cell.cell] });
this._emitter.emitCellOutputsChange(deepFreeze({ document: this.notebookDocument, cells: [cell.cell] }));
}
private _setCellOutputItems(index: number, outputId: string, append: boolean, outputItems: IOutputItemDto[]): void {
const cell = this._cells[index];
cell.setOutputItems(outputId, append, outputItems);
this._emitter.emitCellOutputsChange({ document: this.notebookDocument, cells: [cell.cell] });
this._emitter.emitCellOutputsChange(deepFreeze({ document: this.notebookDocument, cells: [cell.cell] }));
}
private _changeCellLanguage(index: number, language: string): void {
private _changeCellLanguage(index: number, newModeId: string): void {
const cell = this._cells[index];
const event: vscode.NotebookCellLanguageChangeEvent = { document: this.notebookDocument, cell: cell.cell, language };
this._emitter.emitCellLanguageChange(event);
if (cell.cell.document.languageId !== newModeId) {
this._textDocuments.$acceptModelModeChanged(cell.uri, newModeId);
}
}
private _changeCellMetadata(index: number, newMetadata: NotebookCellMetadata | undefined): void {
private _changeCellMetadata(index: number, newMetadata: NotebookCellMetadata): void {
const cell = this._cells[index];
cell.setMetadata(newMetadata || {});
const event: vscode.NotebookCellMetadataChangeEvent = { document: this.notebookDocument, cell: cell.cell };
this._emitter.emitCellMetadataChange(event);
const originalInternalMetadata = cell.internalMetadata;
const originalExtMetadata = cell.cell.metadata;
cell.setMetadata(newMetadata);
const newExtMetadata = cell.cell.metadata;
if (!equals(originalExtMetadata, newExtMetadata)) {
this._emitter.emitCellMetadataChange(deepFreeze({ document: this.notebookDocument, cell: cell.cell }));
}
if (originalInternalMetadata.runState !== newMetadata.runState) {
const executionState = newMetadata.runState ?? extHostTypes.NotebookCellExecutionState.Idle;
this._emitter.emitCellExecutionStateChange(deepFreeze({ document: this.notebookDocument, cell: cell.cell, executionState }));
}
}
getCellFromIndex(index: number): ExtHostCell | undefined {

View File

@@ -3,12 +3,11 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { readonly } from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
import { MainThreadNotebookShape } 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, ICellRange, ICellReplaceEdit, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellEditType, ICellEditOperation, ICellReplaceEdit } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import * as vscode from 'vscode';
import { ExtHostNotebookDocument } from './extHostNotebookDocument';
@@ -46,7 +45,7 @@ class NotebookEditorCellEditBuilder implements vscode.NotebookEditorEdit {
this._throwIfFinalized();
this._collectedEdits.push({
editType: CellEditType.DocumentMetadata,
metadata: { ...notebookDocumentMetadataDefaults, ...value }
metadata: value
});
}
@@ -85,28 +84,33 @@ class NotebookEditorCellEditBuilder implements vscode.NotebookEditorEdit {
}
export class ExtHostNotebookEditor {
private _selection?: vscode.NotebookCell;
private _selections: vscode.NotebookCellRange[] = [];
private _visibleRanges: extHostTypes.NotebookCellRange[] = [];
private _selections: vscode.NotebookCellRange[] = [];
private _visibleRanges: vscode.NotebookCellRange[] = [];
private _viewColumn?: vscode.ViewColumn;
private _active: boolean = false;
private _visible: boolean = false;
private _kernel?: vscode.NotebookKernel;
private _onDidDispose = new Emitter<void>();
private readonly _hasDecorationsForKey = new Set<string>();
private readonly _onDidDispose = new Emitter<void>();
readonly onDidDispose: Event<void> = this._onDidDispose.event;
private _hasDecorationsForKey: { [key: string]: boolean; } = Object.create(null);
private _editor: vscode.NotebookEditor | undefined;
private _editor?: vscode.NotebookEditor;
constructor(
readonly id: string,
private readonly _viewType: string,
private readonly _proxy: MainThreadNotebookShape,
readonly notebookData: ExtHostNotebookDocument,
visibleRanges: vscode.NotebookCellRange[],
selections: vscode.NotebookCellRange[],
viewColumn: vscode.ViewColumn | undefined
) {
this._selections = selections;
this._visibleRanges = visibleRanges;
this._viewColumn = viewColumn;
}
dispose() {
@@ -122,7 +126,8 @@ export class ExtHostNotebookEditor {
return that.notebookData.notebookDocument;
},
get selection() {
return that._selection;
const primarySelection = that._selections[0];
return primarySelection && that.notebookData.getCellFromIndex(primarySelection.start)?.cell;
},
get selections() {
return that._selections;
@@ -131,7 +136,11 @@ export class ExtHostNotebookEditor {
return that._visibleRanges;
},
revealRange(range, revealType) {
that._proxy.$tryRevealRange(that.id, extHostConverter.NotebookCellRange.from(range), revealType ?? extHostTypes.NotebookEditorRevealType.Default);
that._proxy.$tryRevealRange(
that.id,
extHostConverter.NotebookCellRange.from(range),
revealType ?? extHostTypes.NotebookEditorRevealType.Default
);
},
get viewColumn() {
return that._viewColumn;
@@ -163,34 +172,16 @@ export class ExtHostNotebookEditor {
return this._visible;
}
set visible(_state: boolean) {
throw readonly('visible');
}
_acceptVisibility(value: boolean) {
this._visible = value;
}
_acceptVisibleRanges(value: extHostTypes.NotebookCellRange[]): void {
_acceptVisibleRanges(value: vscode.NotebookCellRange[]): void {
this._visibleRanges = value;
}
_acceptSelections(selections: ICellRange[]): void {
const primarySelection = selections[0];
this._selection = primarySelection ? this.notebookData.getCellFromIndex(primarySelection.start)?.cell : undefined;
this._selections = selections.map(val => new extHostTypes.NotebookCellRange(val.start, val.end));
}
get active(): boolean {
return this._active;
}
set active(_state: boolean) {
throw readonly('active');
}
_acceptActive(value: boolean) {
this._active = value;
_acceptSelections(selections: vscode.NotebookCellRange[]): void {
this._selections = selections;
}
private _applyEdit(editData: INotebookEditData): Promise<boolean> {
@@ -213,9 +204,9 @@ export class ExtHostNotebookEditor {
const prevIndex = compressedEditsIndex;
const prev = compressedEdits[prevIndex];
if (prev.editType === CellEditType.Replace && editData.cellEdits[i].editType === CellEditType.Replace) {
const edit = editData.cellEdits[i];
if ((edit.editType !== CellEditType.DocumentMetadata) && prev.index === edit.index) {
const edit = editData.cellEdits[i];
if (prev.editType === CellEditType.Replace && edit.editType === CellEditType.Replace) {
if (prev.index === edit.index) {
prev.cells.push(...(editData.cellEdits[i] as ICellReplaceEdit).cells);
prev.count += (editData.cellEdits[i] as ICellReplaceEdit).count;
continue;
@@ -230,15 +221,14 @@ export class ExtHostNotebookEditor {
}
setDecorations(decorationType: vscode.NotebookEditorDecorationType, range: vscode.NotebookCellRange): void {
const willBeEmpty = (range.start === range.end);
if (willBeEmpty && !this._hasDecorationsForKey[decorationType.key]) {
if (range.isEmpty && !this._hasDecorationsForKey.has(decorationType.key)) {
// avoid no-op call to the renderer
return;
}
if (willBeEmpty) {
delete this._hasDecorationsForKey[decorationType.key];
if (range.isEmpty) {
this._hasDecorationsForKey.delete(decorationType.key);
} else {
this._hasDecorationsForKey[decorationType.key] = true;
this._hasDecorationsForKey.add(decorationType.key);
}
return this._proxy.$trySetDecorations(

View File

@@ -25,7 +25,7 @@ export class ExtHostProgress implements ExtHostProgressShape {
withProgress<R>(extension: IExtensionDescription, options: ProgressOptions, task: (progress: Progress<IProgressStep>, token: CancellationToken) => Thenable<R>): Thenable<R> {
const handle = this._handles++;
const { title, location, cancellable } = options;
const source = localize('extensionSource', "{0} (Extension)", extension.displayName || extension.name);
const source = { label: localize('extensionSource', "{0} (Extension)", extension.displayName || extension.name), id: extension.identifier.value };
this._proxy.$startProgress(handle, { location: ProgressLocation.from(location), title, source, cancellable }, extension);
return this._withProgress(handle, task, !!cancellable);

View File

@@ -16,6 +16,7 @@ import { ThemeIcon, QuickInputButtons } from 'vs/workbench/api/common/extHostTyp
import { isPromiseCanceledError } from 'vs/base/common/errors';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { coalesce } from 'vs/base/common/arrays';
import Severity from 'vs/base/common/severity';
export type Item = string | QuickPickItem;
@@ -67,11 +68,12 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx
const instance = ++this._instances;
const quickPickWidget = proxy.$show(instance, {
placeHolder: options && options.placeHolder,
matchOnDescription: options && options.matchOnDescription,
matchOnDetail: options && options.matchOnDetail,
ignoreFocusLost: options && options.ignoreFocusOut,
canPickMany: options && options.canPickMany
title: options?.title,
placeHolder: options?.placeHolder,
matchOnDescription: options?.matchOnDescription,
matchOnDetail: options?.matchOnDetail,
ignoreFocusLost: options?.ignoreFocusOut,
canPickMany: options?.canPickMany,
}, token);
const widgetClosedMarker = {};
@@ -632,7 +634,7 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx
set validationMessage(validationMessage: string | undefined) {
this._validationMessage = validationMessage;
this.update({ validationMessage });
this.update({ validationMessage, severity: validationMessage ? Severity.Error : Severity.Ignore });
}
}

View File

@@ -0,0 +1,31 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Event, Emitter } from 'vs/base/common/event';
import { ExtHostTelemetryShape } from 'vs/workbench/api/common/extHost.protocol';
export class ExtHostTelemetry implements ExtHostTelemetryShape {
private readonly _onDidChangeTelemetryEnabled = new Emitter<boolean>();
readonly onDidChangeTelemetryEnabled: Event<boolean> = this._onDidChangeTelemetryEnabled.event;
private _enabled: boolean = false;
getTelemetryEnabled(): boolean {
return this._enabled;
}
$initializeTelemetryEnabled(enabled: boolean): void {
this._enabled = enabled;
}
$onDidChangeTelemetryEnabled(enabled: boolean): void {
this._enabled = enabled;
this._onDidChangeTelemetryEnabled.fire(enabled);
}
}
export const IExtHostTelemetry = createDecorator<IExtHostTelemetry>('IExtHostTelemetry');
export interface IExtHostTelemetry extends ExtHostTelemetry, ExtHostTelemetryShape { }

View File

@@ -5,10 +5,10 @@
import type * as vscode from 'vscode';
import { Event, Emitter } from 'vs/base/common/event';
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IShellLaunchConfigDto, IShellDefinitionDto, IShellAndArgsDto, ITerminalDimensionsDto, ITerminalLinkDto, TerminalIdentifier } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IShellLaunchConfigDto, 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, UriComponents } from 'vs/base/common/uri';
import { URI } from 'vs/base/common/uri';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { IDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle';
import { Disposable as VSCodeDisposable, EnvironmentVariableMutatorType } from './extHostTypes';
@@ -19,8 +19,9 @@ 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 } from 'vs/platform/terminal/common/terminal';
import { ITerminalChildProcess, ITerminalDimensionsOverride, 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';
export interface IExtHostTerminalService extends ExtHostTerminalServiceShape, IDisposable {
@@ -133,7 +134,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, isExtensionTerminal: true });
await this._proxy.$createTerminal(this._id, { name: this._name, isExtensionCustomPtyTerminal: true });
// At this point, the id has been set via `$acceptTerminalOpened`
if (typeof this._id === 'string') {
throw new Error('Terminal creation failed');
@@ -198,6 +199,9 @@ export class ExtHostPseudoterminal implements ITerminalChildProcess {
public readonly onProcessTitleChanged: Event<string> = this._onProcessTitleChanged.event;
private readonly _onProcessOverrideDimensions = new Emitter<ITerminalDimensionsOverride | undefined>();
public get onProcessOverrideDimensions(): Event<ITerminalDimensionsOverride | undefined> { return this._onProcessOverrideDimensions.event; }
private readonly _onProcessShellTypeChanged = new Emitter<TerminalShellType>();
public readonly onProcessShellTypeChanged = this._onProcessShellTypeChanged.event;
constructor(private readonly _pty: vscode.Pseudoterminal) { }
@@ -324,8 +328,7 @@ 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 $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<ITerminalLaunchError | undefined>;
public abstract $getAvailableShells(): Promise<IShellDefinitionDto[]>;
public abstract $getAvailableProfiles(quickLaunchOnly: boolean): Promise<ITerminalProfile[]>;
public abstract $getDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto>;
public abstract $acceptWorkspacePermissionsChanged(isAllowed: boolean): void;
@@ -776,11 +779,7 @@ export class WorkerExtHostTerminalService extends BaseExtHostTerminalService {
throw new NotSupportedError();
}
public $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<ITerminalLaunchError | undefined> {
throw new NotSupportedError();
}
public $getAvailableShells(): Promise<IShellDefinitionDto[]> {
public $getAvailableProfiles(quickLaunchOnly: boolean): Promise<ITerminalProfile[]> {
throw new NotSupportedError();
}

View File

@@ -5,10 +5,10 @@
import { mapFind } from 'vs/base/common/arrays';
import { disposableTimeout } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { Emitter } from 'vs/base/common/event';
import { once } from 'vs/base/common/functional';
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { deepFreeze } from 'vs/base/common/objects';
import { isDefined } from 'vs/base/common/types';
import { URI, UriComponents } from 'vs/base/common/uri';
@@ -17,11 +17,11 @@ 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 { TestItem, TestResults, TestState } from 'vs/workbench/api/common/extHostTypeConverters';
import { Disposable } from 'vs/workbench/api/common/extHostTypes';
import * as Convert from 'vs/workbench/api/common/extHostTypeConverters';
import { Disposable, TestItem as TestItemImpl, TestItemHookProperty } 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, TestIdWithProvider, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
import { AbstractIncrementalTestCollection, IncrementalChangeCollector, IncrementalTestCollectionItem, InternalTestItem, ISerializedTestResults, 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()}`;
@@ -93,7 +93,7 @@ export class ExtHostTesting implements ExtHostTestingShape {
tests
.map(this.getInternalTestForReference, this)
.filter(isDefined)
.map(t => ({ providerId: t.providerId, testId: t.item.extId }));
.map(t => ({ src: t.src, testId: t.item.extId }));
await this.proxy.$runTests({
exclude: req.exclude ? testListToProviders(req.exclude).map(t => t.testId) : undefined,
@@ -106,7 +106,7 @@ export class ExtHostTesting implements ExtHostTestingShape {
* Implements vscode.test.publishTestResults
*/
public publishExtensionProvidedResults(results: vscode.TestResults, persist: boolean): void {
this.proxy.$publishExtensionProvidedResults(TestResults.from(generateUuid(), results), persist);
this.proxy.$publishExtensionProvidedResults(Convert.TestResults.from(generateUuid(), results), persist);
}
/**
@@ -116,7 +116,7 @@ export class ExtHostTesting implements ExtHostTestingShape {
public $publishTestResults(results: ISerializedTestResults[]): void {
this.results = Object.freeze(
results
.map(r => deepFreeze(TestResults.to(r)))
.map(r => deepFreeze(Convert.TestResults.to(r)))
.concat(this.results)
.sort((a, b) => b.completedAt - a.completedAt)
.slice(0, 32),
@@ -136,7 +136,8 @@ export class ExtHostTesting implements ExtHostTestingShape {
return;
}
let method: undefined | ((p: vscode.TestProvider) => vscode.TestHierarchy<vscode.TestItem> | undefined);
const cancellation = new CancellationTokenSource();
let method: undefined | ((p: vscode.TestProvider) => vscode.ProviderResult<vscode.TestItem>);
if (resource === ExtHostTestingResource.TextDocument) {
let document = this.documents.getDocument(uri);
@@ -155,14 +156,14 @@ export class ExtHostTesting implements ExtHostTestingShape {
if (document) {
const folder = await this.workspace.getWorkspaceFolder2(uri, false);
method = p => p.createDocumentTestHierarchy
? p.createDocumentTestHierarchy(document!.document)
: this.createDefaultDocumentTestHierarchy(p, document!.document, folder);
method = p => p.provideDocumentTestRoot
? p.provideDocumentTestRoot(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.createWorkspaceTestHierarchy?.(folder);
method = p => p.provideWorkspaceTestRoot(folder, cancellation.token);
}
}
@@ -170,33 +171,21 @@ export class ExtHostTesting implements ExtHostTestingShape {
return;
}
const subscribeFn = (id: string, provider: vscode.TestProvider) => {
const subscribeFn = async (id: string, provider: vscode.TestProvider) => {
try {
const hierarchy = method!(provider);
if (!hierarchy) {
return;
const root = await method!(provider);
if (root) {
collection.addRoot(root, id);
}
collection.pushDiff([TestDiffOpType.DeltaDiscoverComplete, 1]);
disposable.add(hierarchy);
collection.addRoot(hierarchy.root, id);
Promise.resolve(hierarchy.discoveredInitialTests).then(() => collection.pushDiff([TestDiffOpType.DeltaDiscoverComplete, -1]));
hierarchy.onDidChangeTest(e => collection.onItemChange(e, id));
hierarchy.onDidInvalidateTest?.(e => {
const internal = collection.getTestByReference(e);
if (!internal) {
console.warn(`Received a TestProvider.onDidInvalidateTest for a test that does not currently exist.`);
} else {
this.proxy.$retireTest(internal.item.extId);
}
});
} catch (e) {
console.error(e);
}
};
const disposable = new DisposableStore();
const collection = disposable.add(this.ownedTests.createForHierarchy(diff => this.proxy.$publishDiff(resource, uriComponents, diff)));
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);
}
@@ -208,6 +197,17 @@ export class ExtHostTesting implements ExtHostTestingShape {
this.testSubscriptions.set(subscriptionKey, { store: disposable, collection, subscribeFn });
}
/**
* Expands the nodes in the test tree. If levels is less than zero, it will
* be treated as infinite.
* @override
*/
public async $expandTest(test: TestIdWithSrc, levels: number) {
const sub = mapFind(this.testSubscriptions.values(), s => s.collection.treeId === test.src.tree ? s : undefined);
await sub?.collection.expand(test.testId, levels < 0 ? Infinity : levels);
this.flushCollectionDiffs();
}
/**
* Disposes of a previous subscription to tests.
* @override
@@ -238,12 +238,16 @@ export class ExtHostTesting implements ExtHostTestingShape {
* @override
*/
public async $runTestsForProvider(req: RunTestForProviderRequest, cancellation: CancellationToken): Promise<void> {
const provider = this.providers.get(req.providerId);
if (!provider || !provider.runTests) {
const provider = this.providers.get(req.tests[0].src.provider);
if (!provider) {
return;
}
const includeTests = req.ids.map(id => this.ownedTests.getTestById(id)?.[1]).filter(isDefined);
const includeTests = req.tests
.map(({ testId, src }) => this.ownedTests.getTestById(testId, src.tree))
.filter(isDefined)
.map(([_tree, test]) => test);
const excludeTests = req.excludeExtIds
.map(id => this.ownedTests.getTestById(id))
.filter(isDefined)
@@ -268,7 +272,7 @@ export class ExtHostTesting implements ExtHostTestingShape {
}
this.flushCollectionDiffs();
this.proxy.$updateTestStateInRun(req.runId, test.id, TestState.from(state));
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)),
@@ -286,13 +290,13 @@ export class ExtHostTesting implements ExtHostTestingShape {
}
}
public $lookupTest(req: TestIdWithProvider): Promise<InternalTestItem | undefined> {
public $lookupTest(req: TestIdWithSrc): Promise<InternalTestItem | undefined> {
const owned = this.ownedTests.getTestById(req.testId);
if (!owned) {
return Promise.resolve(undefined);
}
const { actual, previousChildren, previousEquals, ...item } = owned[1];
const { actual, discoverCts, expandLevels, ...item } = owned[1];
return Promise.resolve(item);
}
@@ -317,95 +321,54 @@ export class ExtHostTesting implements ExtHostTestingShape {
?? mapFind(this.testSubscriptions.values(), c => c.collection.getTestByReference(test))
?? this.textDocumentObservers.getMirroredTestDataByReference(test);
}
private createDefaultDocumentTestHierarchy(provider: vscode.TestProvider, document: vscode.TextDocument, folder: vscode.WorkspaceFolder | undefined): vscode.TestHierarchy<vscode.TestItem> | undefined {
if (!folder) {
return;
}
const workspaceHierarchy = provider.createWorkspaceTestHierarchy?.(folder);
if (!workspaceHierarchy) {
return;
}
const onDidInvalidateTest = new Emitter<vscode.TestItem>();
workspaceHierarchy.onDidInvalidateTest?.(node => {
const wrapper = TestItemFilteredWrapper.getWrapperForTestItem(node, document);
if (wrapper.hasNodeMatchingFilter) {
onDidInvalidateTest.fire(wrapper);
}
});
const onDidChangeTest = new Emitter<vscode.TestItem>();
workspaceHierarchy.onDidChangeTest(node => {
const wrapper = TestItemFilteredWrapper.getWrapperForTestItem(node, document);
const previouslySeen = wrapper.hasNodeMatchingFilter;
if (previouslySeen) {
// reset cache and get whether you can currently see the TestItem.
wrapper.reset();
const currentlySeen = wrapper.hasNodeMatchingFilter;
if (currentlySeen) {
onDidChangeTest.fire(wrapper);
return;
}
// Fire the event to say that the current visible parent has changed.
onDidChangeTest.fire(wrapper.visibleParent);
return;
}
const previousParent = wrapper.visibleParent;
wrapper.reset();
const currentlySeen = wrapper.hasNodeMatchingFilter;
// It wasn't previously seen and isn't currently seen so
// nothing has actually changed.
if (!currentlySeen) {
return;
}
// The test is now visible so we need to refresh the cache
// of the previous visible parent and fire that it has changed.
previousParent.reset();
onDidChangeTest.fire(previousParent);
});
return {
root: TestItemFilteredWrapper.getWrapperForTestItem(workspaceHierarchy.root, document),
dispose: () => {
onDidChangeTest.dispose();
TestItemFilteredWrapper.removeFilter(document);
},
discoveredInitialTests: workspaceHierarchy.discoveredInitialTests,
onDidInvalidateTest: onDidInvalidateTest.event,
onDidChangeTest: onDidChangeTest.event
};
}
}
export const createDefaultDocumentTestRoot = async <T extends vscode.TestItem>(
provider: vscode.TestProvider<T>,
document: vscode.TextDocument,
folder: vscode.WorkspaceFolder | undefined,
token: CancellationToken,
) => {
if (!folder) {
return;
}
const root = await provider.provideWorkspaceTestRoot(folder, token);
if (!root) {
return;
}
token.onCancellationRequested(() => {
TestItemFilteredWrapper.removeFilter(document);
});
return TestItemFilteredWrapper.getWrapperForTestItem(root, document);
};
/*
* 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 implements vscode.TestItem {
export class TestItemFilteredWrapper<T extends vscode.TestItem = vscode.TestItem> extends TestItemImpl {
private static wrapperMap = new WeakMap<vscode.TextDocument, WeakMap<vscode.TestItem, 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(item: vscode.TestItem, filterDocument: vscode.TextDocument, parent?: TestItemFilteredWrapper): TestItemFilteredWrapper {
public static getWrapperForTestItem<T extends vscode.TestItem>(
item: T,
filterDocument: vscode.TextDocument,
parent?: TestItemFilteredWrapper<T>,
): TestItemFilteredWrapper<T> {
let innerMap = this.wrapperMap.get(filterDocument);
if (innerMap?.has(item)) {
return innerMap.get(item)!;
return innerMap.get(item) as TestItemFilteredWrapper<T>;
}
if (!innerMap) {
innerMap = new WeakMap<vscode.TestItem, TestItemFilteredWrapper>();
this.wrapperMap.set(filterDocument, innerMap);
}
const w = new TestItemFilteredWrapper(item, filterDocument, parent);
@@ -413,75 +376,72 @@ export class TestItemFilteredWrapper implements vscode.TestItem {
return w;
}
/**
* 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 get id() {
return this.actual.id;
}
private _cachedMatchesFilter: boolean | undefined;
public get label() {
return this.actual.label;
}
public get debuggable() {
return this.actual.debuggable;
}
public get description() {
return this.actual.description;
}
public get location() {
return this.actual.location;
}
public get runnable() {
return this.actual.runnable;
}
public get children() {
// We only want children that match the filter.
return this.getWrappedChildren().filter(child => child.hasNodeMatchingFilter);
}
public get visibleParent(): TestItemFilteredWrapper {
return this.hasNodeMatchingFilter ? this : this.parent!.visibleParent;
}
private matchesFilter: boolean | undefined;
// Determines if the TestItem matches the filter. This would be true if:
// 1. We don't have a parent (because the root is the workspace root node)
// 2. The URI of the current node matches the filter URI
// 3. Some child of the current node matches the filter URI
/**
* Gets whether this node, or any of its children, match the document filter.
*/
public get hasNodeMatchingFilter(): boolean {
if (this.matchesFilter === undefined) {
this.matchesFilter = !this.parent
|| this.actual.location?.uri.toString() === this.filterDocument.uri.toString()
|| this.getWrappedChildren().some(child => child.hasNodeMatchingFilter);
if (this._cachedMatchesFilter === undefined) {
return this.refreshMatch();
} else {
return this._cachedMatchesFilter;
}
}
private constructor(
public readonly actual: T,
private filterDocument: vscode.TextDocument,
public readonly parent?: TestItemFilteredWrapper<T>,
) {
super(actual.id, actual.label, actual.uri, actual.expandable);
if (!(actual instanceof TestItemImpl)) {
throw new Error(`TestItems provided to the VS Code API must extend \`vscode.TestItem\`, but ${actual.id} did not`);
}
return this.matchesFilter;
(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),
};
}
// Reset the cache of whether or not you can see a node from a particular node
// up to it's visible parent.
public reset(): void {
if (this !== this.visibleParent) {
this.parent?.reset();
/**
* Refreshes the `hasNodeMatchingFilter` state for this item. It matches
* if the test itself has a location that matches, or if any of its
* children do.
*/
private 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) {
const wrapper = TestItemFilteredWrapper.getWrapperForTestItem(rawChild, this.filterDocument, this);
if (wrapper.hasNodeMatchingFilter) {
this.children.add(wrapper);
} else {
this.children.delete(wrapper);
}
}
this.matchesFilter = undefined;
}
const nowMatches = this.children.size > 0 || this.actual.uri.toString() === this.filterDocument.uri.toString();
this._cachedMatchesFilter = nowMatches;
private constructor(public readonly actual: vscode.TestItem, private filterDocument: vscode.TextDocument, private readonly parent?: TestItemFilteredWrapper) {
this.getWrappedChildren();
}
if (nowMatches !== didMatch) {
this.parent?.refreshMatch();
}
private getWrappedChildren() {
return this.actual.children?.map(t => TestItemFilteredWrapper.getWrapperForTestItem(t, this.filterDocument, this)) || [];
return this._cachedMatchesFilter;
}
}
@@ -491,7 +451,6 @@ export class TestItemFilteredWrapper implements vscode.TestItem {
interface MirroredCollectionTestItem extends IncrementalTestCollectionItem {
revived: vscode.TestItem;
depth: number;
wrapped?: vscode.RequiredTestItem;
}
class MirroredChangeCollector extends IncrementalChangeCollector<MirroredCollectionTestItem> {
@@ -505,7 +464,7 @@ class MirroredChangeCollector extends IncrementalChangeCollector<MirroredCollect
return this.added.size === 0 && this.removed.size === 0 && this.updated.size === 0;
}
constructor(private readonly collection: MirroredTestCollection, private readonly emitter: Emitter<vscode.TestChangeEvent>) {
constructor(private readonly emitter: Emitter<vscode.TestChangeEvent>) {
super();
}
@@ -520,7 +479,7 @@ class MirroredChangeCollector extends IncrementalChangeCollector<MirroredCollect
* @override
*/
public update(node: MirroredCollectionTestItem): void {
Object.assign(node.revived, TestItem.toShallow(node.item));
Object.assign(node.revived, Convert.TestItem.toPlain(node.item));
if (!this.added.has(node)) {
this.updated.add(node);
}
@@ -549,79 +508,11 @@ class MirroredChangeCollector extends IncrementalChangeCollector<MirroredCollect
* @override
*/
public getChangeEvent(): vscode.TestChangeEvent {
const { collection, added, updated, removed } = this;
const { added, updated, removed } = this;
return {
get added() { return [...added].map(collection.getPublicTestItem, collection); },
get updated() { return [...updated].map(collection.getPublicTestItem, collection); },
get removed() { return [...removed].map(collection.getPublicTestItem, collection); },
get commonChangeAncestor() {
let ancestorPath: MirroredCollectionTestItem[] | undefined;
const buildAncestorPath = (node: MirroredCollectionTestItem | undefined) => {
if (!node) {
return undefined;
}
// add the node and all its parents to the list of ancestors. If
// the node is detached, do not return a path (its parent will
// also have been passed to remove() and be present)
const path: MirroredCollectionTestItem[] = new Array(node.depth + 1);
for (let i = node.depth; i >= 0; i--) {
if (!node) {
return undefined; // detached child
}
path[node.depth] = node;
node = node.parent ? collection.getMirroredTestDataById(node.parent) : undefined;
}
return path;
};
const addAncestorPath = (node: MirroredCollectionTestItem) => {
// fast path: if the common ancestor is already the root, no more work to do
if (ancestorPath && ancestorPath.length === 0) {
return;
}
const thisPath = buildAncestorPath(node);
if (!thisPath) {
return;
}
if (!ancestorPath) {
ancestorPath = thisPath;
return;
}
// removes node from the path to the ancestor that don't match
// the corresponding node in *this* path.
for (let i = ancestorPath.length - 1; i >= 0; i--) {
if (ancestorPath[i] !== thisPath[i]) {
ancestorPath.pop();
}
}
};
const addParentAncestor = (node: MirroredCollectionTestItem) => {
if (ancestorPath && ancestorPath.length === 0) {
// no-op
} else if (node.parent === null) {
ancestorPath = [];
} else {
const parent = collection.getMirroredTestDataById(node.parent);
if (parent) {
addAncestorPath(parent);
}
}
};
for (const node of added) { addParentAncestor(node); }
for (const node of updated) { addAncestorPath(node); }
for (const node of removed) { addParentAncestor(node); }
const ancestor = ancestorPath && ancestorPath[ancestorPath.length - 1];
return ancestor ? collection.getPublicTestItem(ancestor) : null;
},
get added() { return [...added].map(n => n.revived); },
get updated() { return [...updated].map(n => n.revived); },
get removed() { return [...removed].map(n => n.revived); },
};
}
@@ -654,12 +545,12 @@ export class MirroredTestCollection extends AbstractIncrementalTestCollection<Mi
/**
* Translates the item IDs to TestItems for exposure to extensions.
*/
public getAllAsTestItem(itemIds: Iterable<string>): vscode.RequiredTestItem[] {
let output: vscode.RequiredTestItem[] = [];
public getAllAsTestItem(itemIds: Iterable<string>) {
let output: vscode.TestItem[] = [];
for (const itemId of itemIds) {
const item = this.items.get(itemId);
if (item) {
output.push(this.getPublicTestItem(item));
output.push(item.revived);
}
}
@@ -685,64 +576,23 @@ export class MirroredTestCollection extends AbstractIncrementalTestCollection<Mi
* @override
*/
protected createItem(item: InternalTestItem, parent?: MirroredCollectionTestItem): MirroredCollectionTestItem {
return { ...item, revived: TestItem.toShallow(item.item), depth: parent ? parent.depth + 1 : 0, children: new Set() };
return {
...item,
// todo@connor4312: make this work well again with children
revived: Convert.TestItem.toPlain(item.item) as vscode.TestItem,
depth: parent ? parent.depth + 1 : 0,
children: new Set(),
};
}
/**
* @override
*/
protected createChangeCollector() {
return new MirroredChangeCollector(this, this.changeEmitter);
}
/**
* Gets the public test item instance for the given mirrored record.
*/
public getPublicTestItem(item: MirroredCollectionTestItem): vscode.RequiredTestItem {
if (!item.wrapped) {
item.wrapped = new TestItemFromMirror(item, this);
}
return item.wrapped;
return new MirroredChangeCollector(this.changeEmitter);
}
}
class TestItemFromMirror implements vscode.RequiredTestItem {
readonly #internal: MirroredCollectionTestItem;
readonly #collection: MirroredTestCollection;
public get id() { return this.#internal.revived.id!; }
public get label() { return this.#internal.revived.label; }
public get description() { return this.#internal.revived.description; }
public get location() { return this.#internal.revived.location; }
public get runnable() { return this.#internal.revived.runnable ?? true; }
public get debuggable() { return this.#internal.revived.debuggable ?? false; }
public get children() {
return this.#collection.getAllAsTestItem(this.#internal.children);
}
constructor(internal: MirroredCollectionTestItem, collection: MirroredTestCollection) {
this.#internal = internal;
this.#collection = collection;
}
public toJSON() {
const serialized: vscode.RequiredTestItem & TestIdWithProvider = {
id: this.id,
label: this.label,
description: this.description,
location: this.location,
runnable: this.runnable,
debuggable: this.debuggable,
children: this.children.map(c => (c as TestItemFromMirror).toJSON()),
providerId: this.#internal.providerId,
testId: this.id,
};
return serialized;
}
}
interface IObserverData {
observers: number;

View File

@@ -6,7 +6,7 @@
import { ExtHostTunnelServiceShape } from 'vs/workbench/api/common/extHost.protocol';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import * as vscode from 'vscode';
import { RemoteTunnel, TunnelCreationOptions, TunnelOptions } from 'vs/platform/remote/common/tunnel';
import { ProvidedPortAttributes, RemoteTunnel, TunnelCreationOptions, TunnelOptions } from 'vs/platform/remote/common/tunnel';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Emitter } from 'vs/base/common/event';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
@@ -46,6 +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;
}
export const IExtHostTunnelService = createDecorator<IExtHostTunnelService>('IExtHostTunnelService');
@@ -71,6 +72,14 @@ export class ExtHostTunnelService implements IExtHostTunnelService {
async setTunnelExtensionFunctions(provider: vscode.RemoteAuthorityResolver | undefined): Promise<IDisposable> {
return { dispose: () => { } };
}
registerPortsAttributesProvider(portSelector: { pid?: number, portRange?: [number, number] }, provider: vscode.PortAttributesProvider) {
return { dispose: () => { } };
}
async $providePortAttributes(handles: number[], ports: number[], pid: number | undefined, commandline: string | undefined, cancellationToken: vscode.CancellationToken): Promise<ProvidedPortAttributes[]> {
return [];
}
async $forwardPort(tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise<TunnelDto | undefined> { return undefined; }
async $closeTunnel(remote: { host: string, port: number }): Promise<void> { }
async $onDidTunnelsChange(): Promise<void> { }

View File

@@ -3,36 +3,35 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as modes from 'vs/editor/common/modes';
import * as types from './extHostTypes';
import * as search from 'vs/workbench/contrib/search/common/search';
import { ITextEditorOptions, EditorOverride } from 'vs/platform/editor/common/editor';
import { IDecorationOptions, IThemeDecorationRenderOptions, IDecorationRenderOptions, IContentDecorationRenderOptions } from 'vs/editor/common/editorCommon';
import { EndOfLineSequence, TrackedRangeStickiness } from 'vs/editor/common/model';
import type * as vscode from 'vscode';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ProgressLocation as MainProgressLocation } from 'vs/platform/progress/common/progress';
import { EditorGroupColumn, SaveReason } from 'vs/workbench/common/editor';
import { IPosition } from 'vs/editor/common/core/position';
import * as editorRange from 'vs/editor/common/core/range';
import { ISelection } from 'vs/editor/common/core/selection';
import { coalesce, isNonEmptyArray } from 'vs/base/common/arrays';
import * as htmlContent from 'vs/base/common/htmlContent';
import * as languageSelector from 'vs/editor/common/modes/languageSelector';
import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol';
import { MarkerSeverity, IRelatedInformation, IMarkerData, MarkerTag } from 'vs/platform/markers/common/markers';
import { ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { isString, isNumber, isDefined } from 'vs/base/common/types';
import * as marked from 'vs/base/common/marked/marked';
import { parse } from 'vs/base/common/marshalling';
import { cloneAndChange } from 'vs/base/common/objects';
import { LogLevel as _MainLogLevel } from 'vs/platform/log/common/log';
import { coalesce, isNonEmptyArray } from 'vs/base/common/arrays';
import { isDefined, isNumber, isString } from 'vs/base/common/types';
import { URI, UriComponents } from 'vs/base/common/uri';
import { RenderLineNumbersType } from 'vs/editor/common/config/editorOptions';
import { IPosition } from 'vs/editor/common/core/position';
import * as editorRange from 'vs/editor/common/core/range';
import { ISelection } from 'vs/editor/common/core/selection';
import { IContentDecorationRenderOptions, IDecorationOptions, IDecorationRenderOptions, IThemeDecorationRenderOptions } from 'vs/editor/common/editorCommon';
import { EndOfLineSequence, TrackedRangeStickiness } from 'vs/editor/common/model';
import * as modes from 'vs/editor/common/modes';
import * as languageSelector from 'vs/editor/common/modes/languageSelector';
import { EditorOverride, ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { IMarkerData, IRelatedInformation, MarkerSeverity, MarkerTag } from 'vs/platform/markers/common/markers';
import { ProgressLocation as MainProgressLocation } from 'vs/platform/progress/common/progress';
import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol';
import { CommandsConverter } from 'vs/workbench/api/common/extHostCommands';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
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 { ISerializedTestResults, ITestItem, ITestState, SerializedTestResultItem } from 'vs/workbench/contrib/testing/common/testCollection';
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 { ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import type * as vscode from 'vscode';
import * as types from './extHostTypes';
export interface PositionLike {
line: number;
@@ -568,7 +567,7 @@ export namespace WorkspaceEdit {
metadata: entry.metadata,
resource: entry.uri,
edit: {
editType: notebooks.CellEditType.Metadata,
editType: notebooks.CellEditType.PartialMetadata,
index: entry.index,
metadata: entry.newMetadata
}
@@ -594,7 +593,6 @@ export namespace WorkspaceEdit {
resource: entry.uri,
edit: {
editType: notebooks.CellEditType.OutputItems,
index: entry.index,
outputId: entry.outputId,
items: entry.newOutputItems?.map(NotebookCellOutputItem.from) || [],
append: entry.append
@@ -790,8 +788,8 @@ export namespace location {
};
}
export function to(value: modes.Location): types.Location {
return new types.Location(value.uri, Range.to(value.range));
export function to(value: extHostProtocol.ILocationDto): types.Location {
return new types.Location(URI.revive(value.uri), Range.to(value.range));
}
}
@@ -810,9 +808,9 @@ export namespace DefinitionLink {
: undefined,
};
}
export function to(value: modes.LocationLink): vscode.LocationLink {
export function to(value: extHostProtocol.IDefinitionLinkDto): vscode.LocationLink {
return {
targetUri: value.uri,
targetUri: URI.revive(value.uri),
targetRange: Range.to(value.range),
targetSelectionRange: value.targetSelectionRange
? Range.to(value.targetSelectionRange)
@@ -901,12 +899,13 @@ export namespace InlineValue {
export namespace InlineValueContext {
export function from(inlineValueContext: vscode.InlineValueContext): extHostProtocol.IInlineValueContextDto {
return <extHostProtocol.IInlineValueContextDto>{
frameId: inlineValueContext.frameId,
stoppedLocation: Range.from(inlineValueContext.stoppedLocation)
};
}
export function to(inlineValueContext: extHostProtocol.IInlineValueContextDto): types.InlineValueContext {
return new types.InlineValueContext(Range.to(inlineValueContext.stoppedLocation));
return new types.InlineValueContext(inlineValueContext.frameId, Range.to(inlineValueContext.stoppedLocation));
}
}
@@ -1419,7 +1418,7 @@ export namespace NotebookCellRange {
export namespace NotebookCellMetadata {
export function to(data: notebooks.NotebookCellMetadata): types.NotebookCellMetadata {
return new types.NotebookCellMetadata(data.editable, data.breakpointMargin, data.runnable, data.hasExecutionOrder, data.executionOrder, data.runState, data.runStartTime, data.statusMessage, data.lastRunDuration, data.inputCollapsed, data.outputCollapsed, data.custom);
return new types.NotebookCellMetadata(data.editable, data.breakpointMargin, data.hasExecutionOrder, data.statusMessage, data.inputCollapsed, data.outputCollapsed, data.custom);
}
}
@@ -1430,9 +1429,26 @@ export namespace NotebookDocumentMetadata {
}
export function to(data: notebooks.NotebookDocumentMetadata): types.NotebookDocumentMetadata {
return new types.NotebookDocumentMetadata(data.editable, data.runnable, data.cellEditable, data.cellRunnable, data.cellHasExecutionOrder, data.displayOrder, data.custom, data.runState, data.trusted);
return new types.NotebookDocumentMetadata(data.editable, data.cellEditable, data.cellHasExecutionOrder, data.custom, data.trusted);
}
}
export namespace NotebookCellPreviousExecutionResult {
export function to(data: notebooks.NotebookCellMetadata): vscode.NotebookCellExecutionSummary {
return {
duration: data.lastRunDuration,
executionOrder: data.executionOrder,
success: data.lastRunSuccess
};
}
export function from(data: vscode.NotebookCellExecutionSummary): Partial<notebooks.NotebookCellMetadata> {
return {
lastRunSuccess: data.success,
lastRunDuration: data.duration,
executionOrder: data.executionOrder
};
}
}
export namespace NotebookCellKind {
@@ -1461,21 +1477,26 @@ export namespace NotebookCellData {
export function from(data: vscode.NotebookCellData): notebooks.ICellDto2 {
return {
cellKind: NotebookCellKind.from(data.cellKind),
cellKind: NotebookCellKind.from(data.kind),
language: data.language,
source: data.source,
metadata: data.metadata,
outputs: data.outputs.map(output => ({
outputId: output.id,
metadata: output.metadata,
outputs: (output.outputs || []).map(op => ({
mime: op.mime,
value: op.value,
metadata: op.metadata
}))
}))
metadata: {
...data.metadata,
...NotebookCellPreviousExecutionResult.from(data.latestExecutionSummary ?? {})
},
outputs: data.outputs ? data.outputs.map(NotebookCellOutput.from) : []
};
}
export function to(data: notebooks.ICellDto2): vscode.NotebookCellData {
return new types.NotebookCellData(
NotebookCellKind.to(data.cellKind),
data.source,
data.language,
data.outputs ? data.outputs.map(NotebookCellOutput.to) : undefined,
data.metadata ? NotebookCellMetadata.to(data.metadata) : undefined,
);
}
}
export namespace NotebookCellOutputItem {
@@ -1591,64 +1612,111 @@ export namespace NotebookDecorationRenderOptions {
}
}
export namespace NotebookDocumentContentOptions {
export function from(options: vscode.NotebookDocumentContentOptions | undefined): notebooks.TransientOptions {
return {
transientOutputs: options ? options.transientOutputs : false,
transientMetadata: {
...(options?.transientMetadata ?? {}),
...{
executionOrder: true,
lastRunDuration: true,
runState: true,
runStartTime: true,
lastRunSuccess: true
}
}
};
}
}
export namespace TestState {
export function from(item: vscode.TestState): ITestState {
return {
state: item.state,
duration: item.duration,
messages: item.messages?.map(message => ({
message: MarkdownString.fromStrict(message.message) || '',
severity: message.severity,
expectedOutput: message.expectedOutput,
actualOutput: message.actualOutput,
location: message.location ? location.from(message.location) as any : undefined,
})) ?? [],
messages: item.messages.map(TestMessage.from),
};
}
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 TestMessage {
export function from(message: vscode.TestMessage): ITestMessage {
return {
state: item.state,
messages: item.messages.map(message => ({
message: typeof message.message === 'string' ? message.message : MarkdownString.to(message.message),
severity: message.severity,
expectedOutput: message.expectedOutput,
actualOutput: message.actualOutput,
location: message.location && location.to({
range: message.location.range,
uri: URI.revive(message.location.uri)
}),
})),
duration: item.duration,
message: MarkdownString.fromStrict(message.message) || '',
severity: message.severity,
expectedOutput: message.expectedOutput,
actualOutput: message.actualOutput,
location: message.location ? location.from(message.location) as any : undefined,
};
}
export function to(item: ITestMessage): vscode.TestMessage {
const message = new types.TestMessage(typeof item.message === 'string' ? item.message : MarkdownString.to(item.message));
message.severity = item.severity;
message.actualOutput = item.actualOutput;
message.expectedOutput = item.expectedOutput;
return message;
}
}
export namespace TestItem {
export type Raw = vscode.TestItem;
export function from(item: vscode.TestItem): ITestItem {
return {
extId: item.id,
label: item.label,
location: item.location ? location.from(item.location) as any : undefined,
uri: item.uri,
range: Range.from(item.range),
debuggable: item.debuggable ?? false,
description: item.description,
runnable: item.runnable ?? true,
expandable: item.expandable,
};
}
export function toShallow(item: ITestItem): Omit<vscode.RequiredTestItem, 'children'> {
export function fromResultSnapshot(item: vscode.TestResultSnapshot): ITestItem {
return {
extId: item.id,
label: item.label,
uri: item.uri,
range: Range.from(item.range),
debuggable: false,
description: item.description,
runnable: true,
expandable: true,
};
}
export function toPlain(item: ITestItem): Omit<vscode.TestItem, 'children' | 'invalidate' | 'discoverChildren'> {
return {
id: item.extId,
label: item.label,
location: item.location && location.to({
range: item.location.range,
uri: URI.revive(item.location.uri)
}),
uri: URI.revive(item.uri),
range: Range.to(item.range),
expandable: item.expandable,
debuggable: item.debuggable,
description: item.description,
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);
testItem.debuggable = item.debuggable;
testItem.description = item.description;
testItem.runnable = item.runnable;
return testItem;
}
}
export namespace TestResults {
@@ -1659,7 +1727,7 @@ export namespace TestResults {
items: [],
};
const queue: [parent: SerializedTestResultItem | null, children: Iterable<vscode.TestItemWithResults>][] = [
const queue: [parent: SerializedTestResultItem | null, children: Iterable<vscode.TestResultSnapshot>][] = [
[null, results.results],
];
@@ -1669,11 +1737,12 @@ export namespace TestResults {
const serializedItem: SerializedTestResultItem = {
children: item.children?.map(c => c.id) ?? [],
computedState: item.result.state,
item: TestItem.from(item),
item: TestItem.fromResultSnapshot(item),
state: TestState.from(item.result),
retired: undefined,
expand: TestItemExpandState.Expanded,
parent: parent?.item.extId ?? null,
providerId: '',
src: { provider: '', tree: -1 },
direct: !parent,
};
@@ -1687,8 +1756,8 @@ export namespace TestResults {
return serialized;
}
const convertTestResultItem = (item: SerializedTestResultItem, byInternalId: Map<string, SerializedTestResultItem>): vscode.TestItemWithResults => ({
...TestItem.toShallow(item.item),
const convertTestResultItem = (item: SerializedTestResultItem, byInternalId: Map<string, SerializedTestResultItem>): vscode.TestResultSnapshot => ({
...TestItem.toPlain(item.item),
result: TestState.to(item.state),
children: item.children
.map(c => byInternalId.get(c))
@@ -1712,3 +1781,16 @@ export namespace TestResults {
};
}
}
export namespace CodeActionTriggerKind {
export function to(value: modes.CodeActionTriggerType): types.CodeActionTriggerKind {
switch (value) {
case modes.CodeActionTriggerType.Invoke:
return types.CodeActionTriggerKind.Invoke;
case modes.CodeActionTriggerType.Auto:
return types.CodeActionTriggerKind.Automatic;
}
}
}

View File

@@ -13,7 +13,7 @@ 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 { CellEditType, ICellEditOperation, notebookDocumentMetadataDefaults, NOTEBOOK_DISPLAY_ORDER } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellEditType, ICellEditOperation } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import type * as vscode from 'vscode';
function es5ClassCompat(target: Function): any {
@@ -673,7 +673,7 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit {
// --- notebook
replaceNotebookMetadata(uri: URI, value: vscode.NotebookDocumentMetadata, metadata?: vscode.WorkspaceEditEntryMetadata): void {
this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.DocumentMetadata, metadata: { ...notebookDocumentMetadataDefaults, ...value } }, notebookMetadata: value });
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 {
@@ -707,7 +707,7 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit {
}
replaceNotebookCellMetadata(uri: URI, index: number, cellMetadata: vscode.NotebookCellMetadata, metadata?: vscode.WorkspaceEditEntryMetadata): void {
this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.Metadata, index, metadata: cellMetadata } });
this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.PartialMetadata, index, metadata: cellMetadata } });
}
// --- text
@@ -1178,9 +1178,9 @@ export class DocumentSymbol {
}
export enum CodeActionTrigger {
Automatic = 1,
Manual = 2,
export enum CodeActionTriggerKind {
Invoke = 1,
Automatic = 2,
}
@es5ClassCompat
@@ -2476,9 +2476,11 @@ export class InlineValueEvaluatableExpression implements vscode.InlineValueEvalu
@es5ClassCompat
export class InlineValueContext implements vscode.InlineValueContext {
readonly frameId: number;
readonly stoppedLocation: vscode.Range;
constructor(range: vscode.Range) {
constructor(frameId: number, range: vscode.Range) {
this.frameId = frameId;
this.stoppedLocation = range;
}
}
@@ -2906,14 +2908,17 @@ export class NotebookCellRange {
return this._end;
}
get isEmpty(): boolean {
return this._start === this._end;
}
constructor(start: number, end: number) {
// todo@rebornix
// if (start < 0) {
// throw illegalArgument('start must be positive');
// }
// if (end < start) {
// throw illegalArgument('end cannot be smaller than start');
// }
if (start < 0) {
throw illegalArgument('start must be positive');
}
if (end < start) {
throw illegalArgument('end cannot be smaller than start');
}
this._start = start;
this._end = end;
}
@@ -2924,13 +2929,8 @@ export class NotebookCellMetadata {
constructor(
readonly editable?: boolean,
readonly breakpointMargin?: boolean,
readonly runnable?: boolean,
readonly hasExecutionOrder?: boolean,
readonly executionOrder?: number,
readonly runState?: NotebookCellRunState,
readonly runStartTime?: number,
readonly statusMessage?: string,
readonly lastRunDuration?: number,
readonly inputCollapsed?: boolean,
readonly outputCollapsed?: boolean,
readonly custom?: Record<string, any>,
@@ -2939,19 +2939,14 @@ export class NotebookCellMetadata {
with(change: {
editable?: boolean | null,
breakpointMargin?: boolean | null,
runnable?: boolean | null,
hasExecutionOrder?: boolean | null,
executionOrder?: number | null,
runState?: NotebookCellRunState | null,
runStartTime?: number | null,
statusMessage?: string | null,
lastRunDuration?: number | null,
inputCollapsed?: boolean | null,
outputCollapsed?: boolean | null,
custom?: Record<string, any> | null,
}): NotebookCellMetadata {
let { editable, breakpointMargin, runnable, hasExecutionOrder, executionOrder, runState, runStartTime, statusMessage, lastRunDuration, inputCollapsed, outputCollapsed, custom } = change;
let { editable, breakpointMargin, hasExecutionOrder, statusMessage, inputCollapsed, outputCollapsed, custom } = change;
if (editable === undefined) {
editable = this.editable;
@@ -2963,41 +2958,16 @@ export class NotebookCellMetadata {
} else if (breakpointMargin === null) {
breakpointMargin = undefined;
}
if (runnable === undefined) {
runnable = this.runnable;
} else if (runnable === null) {
runnable = undefined;
}
if (hasExecutionOrder === undefined) {
hasExecutionOrder = this.hasExecutionOrder;
} else if (hasExecutionOrder === null) {
hasExecutionOrder = undefined;
}
if (executionOrder === undefined) {
executionOrder = this.executionOrder;
} else if (executionOrder === null) {
executionOrder = undefined;
}
if (runState === undefined) {
runState = this.runState;
} else if (runState === null) {
runState = undefined;
}
if (runStartTime === undefined) {
runStartTime = this.runStartTime;
} else if (runStartTime === null) {
runStartTime = undefined;
}
if (statusMessage === undefined) {
statusMessage = this.statusMessage;
} else if (statusMessage === null) {
statusMessage = undefined;
}
if (lastRunDuration === undefined) {
lastRunDuration = this.lastRunDuration;
} else if (lastRunDuration === null) {
lastRunDuration = undefined;
}
if (inputCollapsed === undefined) {
inputCollapsed = this.inputCollapsed;
} else if (inputCollapsed === null) {
@@ -3016,13 +2986,8 @@ export class NotebookCellMetadata {
if (editable === this.editable &&
breakpointMargin === this.breakpointMargin &&
runnable === this.runnable &&
hasExecutionOrder === this.hasExecutionOrder &&
executionOrder === this.executionOrder &&
runState === this.runState &&
runStartTime === this.runStartTime &&
statusMessage === this.statusMessage &&
lastRunDuration === this.lastRunDuration &&
inputCollapsed === this.inputCollapsed &&
outputCollapsed === this.outputCollapsed &&
custom === this.custom
@@ -3033,13 +2998,8 @@ export class NotebookCellMetadata {
return new NotebookCellMetadata(
editable,
breakpointMargin,
runnable,
hasExecutionOrder,
executionOrder,
runState,
runStartTime,
statusMessage,
lastRunDuration,
inputCollapsed,
outputCollapsed,
custom,
@@ -3051,70 +3011,42 @@ export class NotebookDocumentMetadata {
constructor(
readonly editable: boolean = true,
readonly runnable: boolean = true,
readonly cellEditable: boolean = true,
readonly cellRunnable: boolean = true,
readonly cellHasExecutionOrder: boolean = true,
readonly displayOrder: vscode.GlobPattern[] = NOTEBOOK_DISPLAY_ORDER,
readonly custom: { [key: string]: any; } = {},
readonly runState: NotebookRunState = NotebookRunState.Idle,
readonly trusted: boolean = true,
) { }
with(change: {
editable?: boolean | null,
runnable?: boolean | null,
cellEditable?: boolean | null,
cellRunnable?: boolean | null,
cellHasExecutionOrder?: boolean | null,
displayOrder?: vscode.GlobPattern[] | null,
custom?: { [key: string]: any; } | null,
runState?: NotebookRunState | null,
trusted?: boolean | null,
}): NotebookDocumentMetadata {
let { editable, runnable, cellEditable, cellRunnable, cellHasExecutionOrder, displayOrder, custom, runState, trusted } = change;
let { editable, cellEditable, cellHasExecutionOrder, custom, trusted } = change;
if (editable === undefined) {
editable = this.editable;
} else if (editable === null) {
editable = undefined;
}
if (runnable === undefined) {
runnable = this.runnable;
} else if (runnable === null) {
runnable = undefined;
}
if (cellEditable === undefined) {
cellEditable = this.cellEditable;
} else if (cellEditable === null) {
cellEditable = undefined;
}
if (cellRunnable === undefined) {
cellRunnable = this.cellRunnable;
} else if (cellRunnable === null) {
cellRunnable = undefined;
}
if (cellHasExecutionOrder === undefined) {
cellHasExecutionOrder = this.cellHasExecutionOrder;
} else if (cellHasExecutionOrder === null) {
cellHasExecutionOrder = undefined;
}
if (displayOrder === undefined) {
displayOrder = this.displayOrder;
} else if (displayOrder === null) {
displayOrder = undefined;
}
if (custom === undefined) {
custom = this.custom;
} else if (custom === null) {
custom = undefined;
}
if (runState === undefined) {
runState = this.runState;
} else if (runState === null) {
runState = undefined;
}
if (trusted === undefined) {
trusted = this.trusted;
} else if (trusted === null) {
@@ -3122,13 +3054,9 @@ export class NotebookDocumentMetadata {
}
if (editable === this.editable &&
runnable === this.runnable &&
cellEditable === this.cellEditable &&
cellRunnable === this.cellRunnable &&
cellHasExecutionOrder === this.cellHasExecutionOrder &&
displayOrder === this.displayOrder &&
custom === this.custom &&
runState === this.runState &&
trusted === this.trusted
) {
return this;
@@ -3137,20 +3065,45 @@ export class NotebookDocumentMetadata {
return new NotebookDocumentMetadata(
editable,
runnable,
cellEditable,
cellRunnable,
cellHasExecutionOrder,
displayOrder,
custom,
runState,
trusted
);
}
}
export class NotebookCellData {
kind: NotebookCellKind;
source: string;
language: string;
outputs?: NotebookCellOutput[];
metadata?: NotebookCellMetadata;
latestExecutionSummary?: vscode.NotebookCellExecutionSummary;
constructor(kind: NotebookCellKind, source: string, language: string, outputs?: NotebookCellOutput[], metadata?: NotebookCellMetadata, latestExecutionSummary?: vscode.NotebookCellExecutionSummary) {
this.kind = kind;
this.source = source;
this.language = language;
this.outputs = outputs ?? [];
this.metadata = metadata;
this.latestExecutionSummary = latestExecutionSummary;
}
}
export class NotebookData {
cells: NotebookCellData[];
metadata: NotebookDocumentMetadata;
constructor(cells: NotebookCellData[], metadata?: NotebookDocumentMetadata) {
this.cells = cells;
this.metadata = metadata ?? new NotebookDocumentMetadata();
}
}
export class NotebookCellOutputItem {
static isNotebookCellOutputItem(obj: unknown): obj is vscode.NotebookCellOutputItem {
@@ -3191,16 +3144,10 @@ export enum NotebookCellKind {
Code = 2
}
export enum NotebookCellRunState {
Running = 1,
Idle = 2,
Success = 3,
Error = 4
}
export enum NotebookRunState {
Running = 1,
Idle = 2
export enum NotebookCellExecutionState {
Idle = 1,
Pending = 2,
Executing = 3,
}
export enum NotebookCellStatusBarAlignment {
@@ -3276,7 +3223,7 @@ export class LinkedEditingRanges {
}
//#region Testing
export enum TestRunState {
export enum TestResult {
Unset = 0,
Queued = 1,
Running = 2,
@@ -3293,9 +3240,155 @@ export enum TestMessageSeverity {
Hint = 3
}
export type RequiredTestItem = vscode.RequiredTestItem;
export const TestItemHookProperty = Symbol('TestItemHookProperty');
export type TestItem = vscode.TestItem;
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;
}
const testItemPropAccessor = <K extends keyof vscode.TestItem>(item: TestItem, key: K, defaultValue: vscode.TestItem[K]) => {
let value = defaultValue;
return {
enumerable: true,
configurable: false,
get() {
return value;
},
set(newValue: vscode.TestItem[K]) {
item[TestItemHookProperty]?.setProp(key, newValue);
value = newValue;
},
};
};
export class TestChildrenCollection implements vscode.TestChildrenCollection<vscode.TestItem> {
#map = new Map<string, vscode.TestItem>();
#hookRef: () => ITestItemHook | undefined;
public get size() {
return this.#map.size;
}
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;
constructor(id: string, public label: string, uri: vscode.Uri, public expandable: boolean) {
Object.defineProperties(this, {
id: {
value: id,
enumerable: true,
writable: false,
},
uri: {
value: uri,
enumerable: true,
writable: false,
},
children: {
value: new TestChildrenCollection(() => this[TestItemHookProperty]),
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),
});
}
public invalidate() {
this[TestItemHookProperty]?.invalidate(this.id);
}
public discoverChildren(progress: vscode.Progress<{ busy: boolean }>, _token: vscode.CancellationToken) {
progress.report({ busy: false });
}
}
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;
public expectedOutput?: string;
public actualOutput?: string;
public static diff(message: string | vscode.MarkdownString, expected: string, actual: string) {
const msg = new TestMessage(message);
msg.expectedOutput = expected;
msg.actualOutput = actual;
return msg;
}
constructor(public message: string | vscode.MarkdownString) { }
}
//#endregion
@@ -3311,3 +3404,11 @@ export enum WorkspaceTrustState {
Trusted = 1,
Unknown = 2
}
export enum PortAutoForwardAction {
Notify = 1,
OpenBrowser = 2,
OpenPreview = 3,
Silent = 4,
Ignore = 5
}

View File

@@ -5,7 +5,6 @@
import { Emitter, Event } from 'vs/base/common/event';
import { URI } from 'vs/base/common/uri';
import * as modes from 'vs/editor/common/modes';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ILogService } from 'vs/platform/log/common/log';
import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
@@ -97,7 +96,7 @@ export class ExtHostWebview implements vscode.Webview {
public set options(newOptions: vscode.WebviewOptions) {
this.assertNotDisposed();
this.#proxy.$setOptions(this.#handle, convertWebviewOptions(this.#extension, this.#workspace, newOptions));
this.#proxy.$setOptions(this.#handle, serializeWebviewOptions(this.#extension, this.#workspace, newOptions));
this.#options = newOptions;
}
@@ -148,7 +147,7 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
this._logService.warn(`${extensionId} created a webview without a content security policy: https://aka.ms/vscode-webview-missing-csp`);
}
public createNewWebview(handle: string, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, extension: IExtensionDescription): ExtHostWebview {
public createNewWebview(handle: string, options: extHostProtocol.IWebviewOptions, extension: IExtensionDescription): ExtHostWebview {
const webview = new ExtHostWebview(handle, this._webviewProxy, reviveOptions(options), this.initData, this.workspace, extension, this._deprecationService);
this._webviews.set(handle, webview);
@@ -170,22 +169,24 @@ export function toExtensionData(extension: IExtensionDescription): extHostProtoc
return { id: extension.identifier, location: extension.extensionLocation };
}
export function convertWebviewOptions(
export function serializeWebviewOptions(
extension: IExtensionDescription,
workspace: IExtHostWorkspace | undefined,
options: vscode.WebviewPanelOptions & vscode.WebviewOptions,
): modes.IWebviewOptions {
options: vscode.WebviewOptions,
): extHostProtocol.IWebviewOptions {
return {
...options,
enableCommandUris: options.enableCommandUris,
enableScripts: options.enableScripts,
portMapping: options.portMapping,
localResourceRoots: options.localResourceRoots || getDefaultLocalResourceRoots(extension, workspace)
};
}
function reviveOptions(
options: modes.IWebviewOptions & modes.IWebviewPanelOptions
): vscode.WebviewOptions {
export function reviveOptions(options: extHostProtocol.IWebviewOptions): vscode.WebviewOptions {
return {
...options,
enableCommandUris: options.enableCommandUris,
enableScripts: options.enableScripts,
portMapping: options.portMapping,
localResourceRoots: options.localResourceRoots?.map(components => URI.from(components)),
};
}

View File

@@ -7,10 +7,9 @@ import { Emitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid';
import * as modes from 'vs/editor/common/modes';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import { convertWebviewOptions, ExtHostWebview, ExtHostWebviews, toExtensionData } from 'vs/workbench/api/common/extHostWebview';
import { serializeWebviewOptions, ExtHostWebview, ExtHostWebviews, toExtensionData } 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';
@@ -48,14 +47,14 @@ class ExtHostWebviewPanel extends Disposable implements vscode.WebviewPanel {
viewType: string,
title: string,
viewColumn: vscode.ViewColumn | undefined,
editorOptions: vscode.WebviewPanelOptions,
panelOptions: vscode.WebviewPanelOptions,
webview: ExtHostWebview
) {
super();
this.#handle = handle;
this.#proxy = proxy;
this.#viewType = viewType;
this.#options = editorOptions;
this.#options = panelOptions;
this.#viewColumn = viewColumn;
this.#title = title;
this.#webview = webview;
@@ -201,7 +200,11 @@ export class ExtHostWebviewPanels implements extHostProtocol.ExtHostWebviewPanel
};
const handle = ExtHostWebviewPanels.newHandle();
this._proxy.$createWebviewPanel(toExtensionData(extension), handle, viewType, title, webviewShowOptions, convertWebviewOptions(extension, this.workspace, options));
this._proxy.$createWebviewPanel(toExtensionData(extension), handle, viewType, {
title,
panelOptions: serializeWebviewPanelOptions(options),
webviewOptions: serializeWebviewOptions(extension, this.workspace, options),
}, webviewShowOptions);
const webview = this.webviews.createNewWebview(handle, options, extension);
const panel = this.createNewWebviewPanel(handle, viewType, title, viewColumn, options, webview);
@@ -271,10 +274,13 @@ export class ExtHostWebviewPanels implements extHostProtocol.ExtHostWebviewPanel
async $deserializeWebviewPanel(
webviewHandle: extHostProtocol.WebviewHandle,
viewType: string,
title: string,
state: any,
position: EditorGroupColumn,
options: modes.IWebviewOptions & modes.IWebviewPanelOptions
initData: {
title: string;
state: any;
webviewOptions: extHostProtocol.IWebviewOptions;
panelOptions: extHostProtocol.IWebviewPanelOptions;
},
position: EditorGroupColumn
): Promise<void> {
const entry = this._serializers.get(viewType);
if (!entry) {
@@ -282,12 +288,12 @@ export class ExtHostWebviewPanels implements extHostProtocol.ExtHostWebviewPanel
}
const { serializer, extension } = entry;
const webview = this.webviews.createNewWebview(webviewHandle, options, extension);
const revivedPanel = this.createNewWebviewPanel(webviewHandle, viewType, title, position, options, webview);
await serializer.deserializeWebviewPanel(revivedPanel, state);
const webview = this.webviews.createNewWebview(webviewHandle, initData.webviewOptions, extension);
const revivedPanel = this.createNewWebviewPanel(webviewHandle, viewType, initData.title, position, initData.panelOptions, webview);
await serializer.deserializeWebviewPanel(revivedPanel, initData.state);
}
public createNewWebviewPanel(webviewHandle: string, viewType: string, title: string, position: vscode.ViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, webview: ExtHostWebview) {
public createNewWebviewPanel(webviewHandle: string, viewType: string, title: string, position: vscode.ViewColumn, options: extHostProtocol.IWebviewPanelOptions, webview: ExtHostWebview) {
const panel = new ExtHostWebviewPanel(webviewHandle, this._proxy, viewType, title, position, options, webview);
this._webviewPanels.set(webviewHandle, panel);
return panel;
@@ -297,3 +303,10 @@ export class ExtHostWebviewPanels implements extHostProtocol.ExtHostWebviewPanel
return this._webviewPanels.get(handle);
}
}
function serializeWebviewPanelOptions(options: vscode.WebviewPanelOptions): extHostProtocol.IWebviewPanelOptions {
return {
enableFindWidget: options.enableFindWidget,
retainContextWhenHidden: options.retainContextWhenHidden,
};
}

View File

@@ -563,8 +563,8 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
return this._workspaceTrustState;
}
requireWorkspaceTrust(modal?: boolean): Promise<WorkspaceTrustState> {
return this._proxy.$requireWorkspaceTrust(modal);
requireWorkspaceTrust(options?: vscode.WorkspaceTrustRequestOptions): Promise<WorkspaceTrustState> {
return this._proxy.$requireWorkspaceTrust(options);
}
$onDidChangeWorkspaceTrustState(state: WorkspaceTrustStateChangeEvent): void {

View File

@@ -53,6 +53,11 @@ const apiMenus: IAPIMenu[] = [
id: MenuId.EditorContext,
description: localize('menus.editorContext', "The editor context menu")
},
{
key: 'editor/context/copy',
id: MenuId.EditorContextCopy,
description: localize('menus.editorContextCopyAs', "'Copy as' submenu in the editor context menu")
},
{
key: 'explorer/context',
id: MenuId.ExplorerContext,
@@ -91,6 +96,11 @@ const apiMenus: IAPIMenu[] = [
proposed: true,
supportsSubmenus: false
},
{
key: 'menuBar/edit/copy',
id: MenuId.MenubarCopy,
description: localize('menus.opy', "'Copy as' submenu in the top level Edit menu")
},
{
key: 'scm/title',
id: MenuId.SCMTitle,
@@ -104,17 +114,17 @@ const apiMenus: IAPIMenu[] = [
{
key: 'scm/resourceState/context',
id: MenuId.SCMResourceContext,
description: localize('menus.resourceGroupContext', "The Source Control resource group context menu")
description: localize('menus.resourceStateContext', "The Source Control resource state context menu")
},
{
key: 'scm/resourceFolder/context',
id: MenuId.SCMResourceFolderContext,
description: localize('menus.resourceStateContext', "The Source Control resource state context menu")
description: localize('menus.resourceFolderContext', "The Source Control resource folder context menu")
},
{
key: 'scm/resourceGroup/context',
id: MenuId.SCMResourceGroupContext,
description: localize('menus.resourceFolderContext', "The Source Control resource folder context menu")
description: localize('menus.resourceGroupContext', "The Source Control resource group context menu")
},
{
key: 'scm/change/title',
@@ -160,6 +170,12 @@ const apiMenus: IAPIMenu[] = [
description: localize('comment.actions', "The contributed comment context menu, rendered as buttons below the comment editor"),
supportsSubmenus: false
},
{
key: 'notebook/toolbar',
id: MenuId.NotebookToolbar,
description: localize('notebook.toolbar', "The contributed notebook toolbar menu"),
proposed: true
},
{
key: 'notebook/cell/title',
id: MenuId.NotebookCellTitle,
@@ -539,7 +555,7 @@ commandsExtensionPoint.setHandler(extensions => {
let absoluteIcon: { dark: URI; light?: URI; } | ThemeIcon | undefined;
if (icon) {
if (typeof icon === 'string') {
absoluteIcon = ThemeIcon.fromString(icon) || { dark: resources.joinPath(extension.description.extensionLocation, icon) };
absoluteIcon = ThemeIcon.fromString(icon) ?? { dark: resources.joinPath(extension.description.extensionLocation, icon), light: resources.joinPath(extension.description.extensionLocation, icon) };
} else {
absoluteIcon = {