mirror of
https://github.com/coder/code-server.git
synced 2026-05-06 12:31:58 +02:00
chore(vscode): update to 1.56.0
This commit is contained in:
20
lib/vscode/src/vs/workbench/api/browser/apiCommands.ts
Normal file
20
lib/vscode/src/vs/workbench/api/browser/apiCommands.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { isWeb } from 'vs/base/common/platform';
|
||||
|
||||
if (isWeb) {
|
||||
CommandsRegistry.registerCommand('_workbench.fetchJSON', async function (accessor: ServicesAccessor, url: string, method: string) {
|
||||
const result = await fetch(url, { method, headers: { Accept: 'application/json' } });
|
||||
|
||||
if (result.ok) {
|
||||
return result.json();
|
||||
} else {
|
||||
throw new Error(result.statusText);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -63,6 +63,8 @@ import './mainThreadWebviewManager';
|
||||
import './mainThreadWorkspace';
|
||||
import './mainThreadComments';
|
||||
import './mainThreadNotebook';
|
||||
import './mainThreadNotebookKernels';
|
||||
import './mainThreadNotebookDocumentsAndEditors';
|
||||
import './mainThreadTask';
|
||||
import './mainThreadLabelService';
|
||||
import './mainThreadTunnelService';
|
||||
@@ -71,6 +73,7 @@ import './mainThreadTimeline';
|
||||
import './mainThreadTesting';
|
||||
import './mainThreadSecretState';
|
||||
import 'vs/workbench/api/common/apiCommands';
|
||||
import 'vs/workbench/api/browser/apiCommands';
|
||||
|
||||
export class ExtensionPoints implements IWorkbenchContribution {
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
|
||||
import { IExtHostContext, IWorkspaceEditDto, MainThreadBulkEditsShape, MainContext } from 'vs/workbench/api/common/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { reviveWorkspaceEditDto2 } from 'vs/workbench/api/browser/mainThreadEditors';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadBulkEdits)
|
||||
export class MainThreadBulkEdits implements MainThreadBulkEditsShape {
|
||||
@@ -13,12 +14,16 @@ export class MainThreadBulkEdits implements MainThreadBulkEditsShape {
|
||||
constructor(
|
||||
_extHostContext: IExtHostContext,
|
||||
@IBulkEditService private readonly _bulkEditService: IBulkEditService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
) { }
|
||||
|
||||
dispose(): void { }
|
||||
|
||||
$tryApplyWorkspaceEdit(dto: IWorkspaceEditDto, undoRedoGroupId?: number): Promise<boolean> {
|
||||
const edits = reviveWorkspaceEditDto2(dto);
|
||||
return this._bulkEditService.apply(edits, { undoRedoGroupId }).then(() => true, _err => false);
|
||||
return this._bulkEditService.apply(edits, { undoRedoGroupId }).then(() => true, err => {
|
||||
this._logService.warn('IGNORING workspace edit', err);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Schemas } from 'vs/base/common/network';
|
||||
import { isString } from 'vs/base/common/types';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { localize } from 'vs/nls';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { CLIOutput, IExtensionGalleryService, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { ExtensionManagementCLIService } from 'vs/platform/extensionManagement/common/extensionManagementCLIService';
|
||||
@@ -17,9 +17,10 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { IOpenWindowOptions, IWindowOpenable } from 'vs/platform/windows/common/windows';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
|
||||
import { ExtensionKindController } from 'vs/workbench/services/extensions/common/extensionsUtil';
|
||||
import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService';
|
||||
import { IExtensionManifest } from 'vs/workbench/workbench.web.api';
|
||||
|
||||
|
||||
@@ -27,7 +28,17 @@ import { IExtensionManifest } from 'vs/workbench/workbench.web.api';
|
||||
|
||||
CommandsRegistry.registerCommand('_remoteCLI.openExternal', function (accessor: ServicesAccessor, uri: UriComponents | string) {
|
||||
const openerService = accessor.get(IOpenerService);
|
||||
openerService.open(isString(uri) ? uri : URI.revive(uri), { openExternal: true, allowTunneling: true });
|
||||
return openerService.open(isString(uri) ? uri : URI.revive(uri), { openExternal: true, allowTunneling: true });
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand('_remoteCLI.windowOpen', function (accessor: ServicesAccessor, toOpen: IWindowOpenable[], options?: IOpenWindowOptions) {
|
||||
const commandService = accessor.get(ICommandService);
|
||||
return commandService.executeCommand('_files.windowOpen', toOpen, options);
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand('_remoteCLI.getSystemStatus', function (accessor: ServicesAccessor) {
|
||||
const commandService = accessor.get(ICommandService);
|
||||
return commandService.executeCommand('_issues.getSystemStatus');
|
||||
});
|
||||
|
||||
interface ManageExtensionsArgs {
|
||||
@@ -77,30 +88,27 @@ class RemoteExtensionCLIManagementService extends ExtensionManagementCLIService
|
||||
|
||||
private _location: string | undefined;
|
||||
|
||||
private readonly _extensionKindController: ExtensionKindController;
|
||||
|
||||
constructor(
|
||||
@IExtensionManagementService extensionManagementService: IExtensionManagementService,
|
||||
@IProductService productService: IProductService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IExtensionGalleryService extensionGalleryService: IExtensionGalleryService,
|
||||
@ILabelService labelService: ILabelService,
|
||||
@IWorkbenchEnvironmentService envService: IWorkbenchEnvironmentService
|
||||
@IWorkbenchEnvironmentService envService: IWorkbenchEnvironmentService,
|
||||
@IExtensionManifestPropertiesService private readonly _extensionManifestPropertiesService: IExtensionManifestPropertiesService,
|
||||
) {
|
||||
super(extensionManagementService, extensionGalleryService);
|
||||
|
||||
const remoteAuthority = envService.remoteAuthority;
|
||||
this._location = remoteAuthority ? labelService.getHostLabel(Schemas.vscodeRemote, remoteAuthority) : undefined;
|
||||
|
||||
this._extensionKindController = new ExtensionKindController(productService, configurationService);
|
||||
}
|
||||
|
||||
protected get location(): string | undefined {
|
||||
protected override get location(): string | undefined {
|
||||
return this._location;
|
||||
}
|
||||
|
||||
protected validateExtensionKind(manifest: IExtensionManifest, output: CLIOutput): boolean {
|
||||
if (!this._extensionKindController.canExecuteOnWorkspace(manifest)) {
|
||||
protected override validateExtensionKind(manifest: IExtensionManifest, output: CLIOutput): boolean {
|
||||
if (!this._extensionManifestPropertiesService.canExecuteOnWorkspace(manifest)) {
|
||||
output.log(localize('cannot be installed', "Cannot install the '{0}' extension because it is declared to not run in this setup.", getExtensionId(manifest.publisher, manifest.name)));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape {
|
||||
disposables.add(editor.onDidDispose(remove));
|
||||
disposables.add(webviewZone);
|
||||
disposables.add(webview);
|
||||
disposables.add(webview.onMessage(msg => this._proxy.$onDidReceiveMessage(handle, msg)));
|
||||
disposables.add(webview.onMessage(msg => this._proxy.$onDidReceiveMessage(handle, msg.message)));
|
||||
|
||||
this._insets.set(handle, webviewZone);
|
||||
}
|
||||
|
||||
@@ -535,7 +535,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments
|
||||
this._commentService.updateComments(providerId, event);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
override dispose(): void {
|
||||
super.dispose();
|
||||
this._workspaceProviders.forEach(value => dispose(value));
|
||||
this._workspaceProviders.clear();
|
||||
|
||||
@@ -31,13 +31,13 @@ import { CustomTextEditorModel } from 'vs/workbench/contrib/customEditor/common/
|
||||
import { WebviewExtensionDescription } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { WebviewInput } from 'vs/workbench/contrib/webviewPanel/browser/webviewEditorInput';
|
||||
import { IWebviewWorkbenchService } from 'vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService';
|
||||
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IPathService } from 'vs/workbench/services/path/common/pathService';
|
||||
import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService';
|
||||
import { IWorkingCopy, IWorkingCopyBackup, IWorkingCopyService, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
import { IWorkingCopy, IWorkingCopyBackup, NO_TYPE_ID, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopy';
|
||||
|
||||
const enum CustomEditorModelType {
|
||||
Custom,
|
||||
@@ -60,8 +60,7 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc
|
||||
@ICustomEditorService private readonly _customEditorService: ICustomEditorService,
|
||||
@IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService,
|
||||
@IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IBackupFileService private readonly _backupService: IBackupFileService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -92,19 +91,19 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc
|
||||
}));
|
||||
}
|
||||
|
||||
dispose() {
|
||||
override dispose() {
|
||||
super.dispose();
|
||||
|
||||
dispose(this._editorProviders.values());
|
||||
this._editorProviders.clear();
|
||||
}
|
||||
|
||||
public $registerTextEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: extHostProtocol.IWebviewPanelOptions, capabilities: extHostProtocol.CustomTextEditorCapabilities): void {
|
||||
this.registerEditorProvider(CustomEditorModelType.Text, reviveWebviewExtension(extensionData), viewType, options, capabilities, true);
|
||||
public $registerTextEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: extHostProtocol.IWebviewPanelOptions, capabilities: extHostProtocol.CustomTextEditorCapabilities, serializeBuffersForPostMessage: boolean): void {
|
||||
this.registerEditorProvider(CustomEditorModelType.Text, reviveWebviewExtension(extensionData), viewType, options, capabilities, true, serializeBuffersForPostMessage);
|
||||
}
|
||||
|
||||
public $registerCustomEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: extHostProtocol.IWebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean): void {
|
||||
this.registerEditorProvider(CustomEditorModelType.Custom, reviveWebviewExtension(extensionData), viewType, options, {}, supportsMultipleEditorsPerDocument);
|
||||
public $registerCustomEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: extHostProtocol.IWebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean, serializeBuffersForPostMessage: boolean): void {
|
||||
this.registerEditorProvider(CustomEditorModelType.Custom, reviveWebviewExtension(extensionData), viewType, options, {}, supportsMultipleEditorsPerDocument, serializeBuffersForPostMessage);
|
||||
}
|
||||
|
||||
private registerEditorProvider(
|
||||
@@ -114,6 +113,7 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc
|
||||
options: extHostProtocol.IWebviewPanelOptions,
|
||||
capabilities: extHostProtocol.CustomTextEditorCapabilities,
|
||||
supportsMultipleEditorsPerDocument: boolean,
|
||||
serializeBuffersForPostMessage: boolean,
|
||||
): void {
|
||||
if (this._editorProviders.has(viewType)) {
|
||||
throw new Error(`Provider for ${viewType} already registered`);
|
||||
@@ -133,7 +133,7 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc
|
||||
const handle = webviewInput.id;
|
||||
const resource = webviewInput.resource;
|
||||
|
||||
this.mainThreadWebviewPanels.addWebviewInput(handle, webviewInput);
|
||||
this.mainThreadWebviewPanels.addWebviewInput(handle, webviewInput, { serializeBuffersForPostMessage });
|
||||
webviewInput.webview.options = options;
|
||||
webviewInput.webview.extension = extension;
|
||||
|
||||
@@ -228,7 +228,7 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc
|
||||
const model = MainThreadCustomEditorModel.create(this._instantiationService, this._proxyCustomEditors, viewType, resource, options, () => {
|
||||
return Array.from(this.mainThreadWebviewPanels.webviewInputs)
|
||||
.filter(editor => editor instanceof CustomEditorInput && isEqual(editor.resource, resource)) as CustomEditorInput[];
|
||||
}, cancellation, this._backupService);
|
||||
}, cancellation);
|
||||
return this._customEditorService.models.add(resource, viewType, model);
|
||||
}
|
||||
}
|
||||
@@ -295,6 +295,18 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
private readonly _onDidChangeOrphaned = this._register(new Emitter<void>());
|
||||
public readonly onDidChangeOrphaned = this._onDidChangeOrphaned.event;
|
||||
|
||||
// TODO@mjbvz consider to enable a `typeId` that is specific for custom
|
||||
// editors. Using a distinct `typeId` allows the working copy to have
|
||||
// any resource (including file based resources) even if other working
|
||||
// copies exist with the same resource.
|
||||
//
|
||||
// IMPORTANT: changing the `typeId` has an impact on backups for this
|
||||
// working copy. Any value that is not the empty string will be used
|
||||
// as seed to the backup. Only change the `typeId` if you have implemented
|
||||
// a fallback solution to resolve any existing backups that do not have
|
||||
// this seed.
|
||||
readonly typeId = NO_TYPE_ID;
|
||||
|
||||
public static async create(
|
||||
instantiationService: IInstantiationService,
|
||||
proxy: extHostProtocol.ExtHostCustomEditorsShape,
|
||||
@@ -303,7 +315,6 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
options: { backupId?: string },
|
||||
getEditors: () => CustomEditorInput[],
|
||||
cancellation: CancellationToken,
|
||||
_backupFileService: IBackupFileService,
|
||||
): Promise<MainThreadCustomEditorModel> {
|
||||
const editors = getEditors();
|
||||
let untitledDocumentData: VSBuffer | undefined;
|
||||
@@ -344,7 +355,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
return this._editorResource;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
override dispose() {
|
||||
this.#isDisposed = true;
|
||||
|
||||
if (this._editable) {
|
||||
@@ -651,23 +662,25 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
}
|
||||
const primaryEditor = editors[0];
|
||||
|
||||
const backupData: IWorkingCopyBackup<CustomDocumentBackupData> = {
|
||||
meta: {
|
||||
viewType: this.viewType,
|
||||
editorResource: this._editorResource,
|
||||
backupId: '',
|
||||
extension: primaryEditor.extension ? {
|
||||
id: primaryEditor.extension.id.value,
|
||||
location: primaryEditor.extension.location,
|
||||
} : undefined,
|
||||
webview: {
|
||||
id: primaryEditor.id,
|
||||
options: primaryEditor.webview.options,
|
||||
state: primaryEditor.webview.state,
|
||||
}
|
||||
const backupMeta: CustomDocumentBackupData = {
|
||||
viewType: this.viewType,
|
||||
editorResource: this._editorResource,
|
||||
backupId: '',
|
||||
extension: primaryEditor.extension ? {
|
||||
id: primaryEditor.extension.id.value,
|
||||
location: primaryEditor.extension.location,
|
||||
} : undefined,
|
||||
webview: {
|
||||
id: primaryEditor.id,
|
||||
options: primaryEditor.webview.options,
|
||||
state: primaryEditor.webview.state,
|
||||
}
|
||||
};
|
||||
|
||||
const backupData: IWorkingCopyBackup = {
|
||||
meta: backupMeta
|
||||
};
|
||||
|
||||
if (!this._editable) {
|
||||
return backupData;
|
||||
}
|
||||
|
||||
@@ -157,10 +157,12 @@ export class MainThreadDocuments extends Disposable implements MainThreadDocumen
|
||||
}));
|
||||
|
||||
this._register(workingCopyFileService.onDidRunWorkingCopyFileOperation(e => {
|
||||
if (e.operation === FileOperation.MOVE || e.operation === FileOperation.DELETE) {
|
||||
for (const { source } of e.files) {
|
||||
if (source) {
|
||||
this._modelReferenceCollection.remove(source);
|
||||
const isMove = e.operation === FileOperation.MOVE;
|
||||
if (isMove || e.operation === FileOperation.DELETE) {
|
||||
for (const pair of e.files) {
|
||||
const removed = isMove ? pair.source : pair.target;
|
||||
if (removed) {
|
||||
this._modelReferenceCollection.remove(removed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -169,7 +171,7 @@ export class MainThreadDocuments extends Disposable implements MainThreadDocumen
|
||||
this._modelTrackers = Object.create(null);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
public override dispose(): void {
|
||||
Object.keys(this._modelTrackers).forEach((modelUrl) => {
|
||||
this._modelTrackers[modelUrl].dispose();
|
||||
});
|
||||
|
||||
@@ -518,7 +518,7 @@ export class MainThreadTextEditor {
|
||||
|
||||
const snippetController = SnippetController2.get(this._codeEditor);
|
||||
|
||||
// // cancel previous snippet mode
|
||||
// cancel previous snippet mode
|
||||
// snippetController.leaveSnippet();
|
||||
|
||||
// set selection, focus editor
|
||||
|
||||
@@ -7,7 +7,7 @@ import { SerializedError } from 'vs/base/common/errors';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { IExtHostContext, MainContext, MainThreadExtensionServiceShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { IExtensionService, ExtensionActivationError, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IExtensionService, ExtensionHostKind, MissingExtensionDependency } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { localize } from 'vs/nls';
|
||||
@@ -20,6 +20,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator';
|
||||
import { ITimerService } from 'vs/workbench/services/timer/browser/timerService';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadExtensionService)
|
||||
export class MainThreadExtensionService implements MainThreadExtensionServiceShape {
|
||||
@@ -34,6 +35,7 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha
|
||||
@IHostService private readonly _hostService: IHostService,
|
||||
@IWorkbenchExtensionEnablementService private readonly _extensionEnablementService: IWorkbenchExtensionEnablementService,
|
||||
@ITimerService private readonly _timerService: ITimerService,
|
||||
@IWorkbenchEnvironmentService protected readonly _environmentService: IWorkbenchEnvironmentService,
|
||||
) {
|
||||
this._extensionHostKind = extHostContext.extensionHostKind;
|
||||
}
|
||||
@@ -59,25 +61,36 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha
|
||||
console.error(`[${extensionId}]${error.message}`);
|
||||
console.error(error.stack);
|
||||
}
|
||||
async $onExtensionActivationError(extensionId: ExtensionIdentifier, activationError: ExtensionActivationError): Promise<void> {
|
||||
if (typeof activationError === 'string') {
|
||||
this._extensionService._logOrShowMessage(Severity.Error, activationError);
|
||||
} else {
|
||||
this._handleMissingDependency(extensionId, activationError.dependency);
|
||||
}
|
||||
}
|
||||
async $onExtensionActivationError(extensionId: ExtensionIdentifier, data: SerializedError, missingExtensionDependency: MissingExtensionDependency | null): Promise<void> {
|
||||
const error = new Error();
|
||||
error.name = data.name;
|
||||
error.message = data.message;
|
||||
error.stack = data.stack;
|
||||
|
||||
private async _handleMissingDependency(extensionId: ExtensionIdentifier, missingDependency: string): Promise<void> {
|
||||
const extension = await this._extensionService.getExtension(extensionId.value);
|
||||
if (extension) {
|
||||
const local = await this._extensionsWorkbenchService.queryLocal();
|
||||
const installedDependency = local.filter(i => areSameExtensions(i.identifier, { id: missingDependency }))[0];
|
||||
if (installedDependency) {
|
||||
await this._handleMissingInstalledDependency(extension, installedDependency.local!);
|
||||
} else {
|
||||
await this._handleMissingNotInstalledDependency(extension, missingDependency);
|
||||
this._extensionService._onDidActivateExtensionError(extensionId, error);
|
||||
|
||||
if (missingExtensionDependency) {
|
||||
const extension = await this._extensionService.getExtension(extensionId.value);
|
||||
if (extension) {
|
||||
const local = await this._extensionsWorkbenchService.queryLocal();
|
||||
const installedDependency = local.filter(i => areSameExtensions(i.identifier, { id: missingExtensionDependency.dependency }))[0];
|
||||
if (installedDependency) {
|
||||
await this._handleMissingInstalledDependency(extension, installedDependency.local!);
|
||||
return;
|
||||
} else {
|
||||
await this._handleMissingNotInstalledDependency(extension, missingExtensionDependency.dependency);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const isDev = !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment;
|
||||
if (isDev) {
|
||||
this._notificationService.error(error);
|
||||
return;
|
||||
}
|
||||
|
||||
console.error(error.message);
|
||||
}
|
||||
|
||||
private async _handleMissingInstalledDependency(extension: IExtensionDescription, missingInstalledDependency: ILocalExtension): Promise<void> {
|
||||
|
||||
@@ -121,7 +121,7 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape {
|
||||
cancelId = buttons.length - 1;
|
||||
}
|
||||
|
||||
const { choice } = await this._dialogService.show(severity, message, buttons, { cancelId, useCustom });
|
||||
const { choice } = await this._dialogService.show(severity, message, buttons, { cancelId, custom: useCustom });
|
||||
return choice === commands.length ? undefined : commands[choice].handle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,100 +5,16 @@
|
||||
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { diffMaps, diffSets } from 'vs/base/common/collections';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { IRelativePattern } from 'vs/base/common/glob';
|
||||
import { DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ResourceMap } from 'vs/base/common/map';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { EditorActivation, EditorOverride } from 'vs/platform/editor/common/editor';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { BoundModelReferenceCollection } from 'vs/workbench/api/browser/mainThreadDocuments';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { getNotebookEditorFromEditorPane, IActiveNotebookEditor, INotebookEditor, NotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/browser/notebookEditorInput';
|
||||
import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/notebookEditorService';
|
||||
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService';
|
||||
import { ICellEditOperation, ICellRange, IImmediateCellEditOperation, IMainCellDto, INotebookDecorationRenderOptions, INotebookDocumentFilter, INotebookExclusiveDocumentFilter, INotebookKernel, NotebookCellsChangeType, NotebookDataDto, TransientMetadata, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService';
|
||||
import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
|
||||
import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookCellStatusBarEntryDto, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookEditorAddData, INotebookModelAddedData, MainContext, MainThreadNotebookShape, NotebookEditorRevealType, NotebookExtensionDescription } from '../common/extHost.protocol';
|
||||
|
||||
class NotebookAndEditorState {
|
||||
static compute(before: NotebookAndEditorState | undefined, after: NotebookAndEditorState): INotebookDocumentsAndEditorsDelta {
|
||||
if (!before) {
|
||||
return {
|
||||
addedDocuments: [...after.documents].map(NotebookAndEditorState._asModelAddData),
|
||||
addedEditors: [...after.textEditors.values()].map(NotebookAndEditorState._asEditorAddData),
|
||||
visibleEditors: [...after.visibleEditors].map(editor => editor[0])
|
||||
};
|
||||
}
|
||||
const documentDelta = diffSets(before.documents, after.documents);
|
||||
const editorDelta = diffMaps(before.textEditors, after.textEditors);
|
||||
const addedAPIEditors = editorDelta.added.map(NotebookAndEditorState._asEditorAddData);
|
||||
|
||||
const removedAPIEditors = editorDelta.removed.map(removed => removed.getId());
|
||||
const newActiveEditor = before.activeEditor !== after.activeEditor ? after.activeEditor : undefined;
|
||||
const visibleEditorDelta = diffMaps(before.visibleEditors, after.visibleEditors);
|
||||
|
||||
return {
|
||||
addedDocuments: documentDelta.added.map(NotebookAndEditorState._asModelAddData),
|
||||
removedDocuments: documentDelta.removed.map(e => e.uri),
|
||||
addedEditors: addedAPIEditors,
|
||||
removedEditors: removedAPIEditors,
|
||||
newActiveEditor: newActiveEditor,
|
||||
visibleEditors: visibleEditorDelta.added.length === 0 && visibleEditorDelta.removed.length === 0
|
||||
? undefined
|
||||
: [...after.visibleEditors].map(editor => editor[0])
|
||||
};
|
||||
}
|
||||
|
||||
constructor(
|
||||
readonly documents: Set<NotebookTextModel>,
|
||||
readonly textEditors: Map<string, IActiveNotebookEditor>,
|
||||
readonly activeEditor: string | null | undefined,
|
||||
readonly visibleEditors: Map<string, IActiveNotebookEditor>
|
||||
) {
|
||||
//
|
||||
}
|
||||
|
||||
private static _asModelAddData(e: NotebookTextModel): INotebookModelAddedData {
|
||||
return {
|
||||
viewType: e.viewType,
|
||||
uri: e.uri,
|
||||
metadata: e.metadata,
|
||||
versionId: e.versionId,
|
||||
cells: e.cells.map(cell => ({
|
||||
handle: cell.handle,
|
||||
uri: cell.uri,
|
||||
source: cell.textBuffer.getLinesContent(),
|
||||
eol: cell.textBuffer.getEOL(),
|
||||
language: cell.language,
|
||||
cellKind: cell.cellKind,
|
||||
outputs: cell.outputs,
|
||||
metadata: cell.metadata
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
private static _asEditorAddData(add: IActiveNotebookEditor): INotebookEditorAddData {
|
||||
return {
|
||||
id: add.getId(),
|
||||
documentUri: add.viewModel.uri,
|
||||
selections: add.getSelections(),
|
||||
visibleRanges: add.visibleRanges,
|
||||
viewColumn: undefined
|
||||
};
|
||||
}
|
||||
}
|
||||
import { NotebookSelector } from 'vs/workbench/contrib/notebook/common/notebookSelector';
|
||||
import { INotebookCellStatusBarItemProvider, INotebookExclusiveDocumentFilter, NotebookDataDto, TransientCellMetadata, TransientDocumentMetadata, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookContentProvider, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, MainContext, MainThreadNotebookShape, NotebookExtensionDescription } from '../common/extHost.protocol';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadNotebook)
|
||||
export class MainThreadNotebooks implements MainThreadNotebookShape {
|
||||
@@ -106,311 +22,42 @@ export class MainThreadNotebooks implements MainThreadNotebookShape {
|
||||
private readonly _disposables = new DisposableStore();
|
||||
|
||||
private readonly _proxy: ExtHostNotebookShape;
|
||||
private readonly _notebookProviders = new Map<string, { controller: IMainNotebookController, disposable: IDisposable }>();
|
||||
private readonly _notebookProviders = new Map<string, { controller: INotebookContentProvider, disposable: IDisposable }>();
|
||||
private readonly _notebookSerializer = new Map<number, IDisposable>();
|
||||
private readonly _notebookKernelProviders = new Map<number, { extension: NotebookExtensionDescription, emitter: Emitter<URI | undefined>, provider: IDisposable }>();
|
||||
private readonly _editorEventListenersMapping = new Map<string, DisposableStore>();
|
||||
private readonly _documentEventListenersMapping = new ResourceMap<DisposableStore>();
|
||||
private readonly _cellStatusBarEntries = new Map<number, IDisposable>();
|
||||
private readonly _modelReferenceCollection: BoundModelReferenceCollection;
|
||||
|
||||
private _currentState?: NotebookAndEditorState;
|
||||
private readonly _notebookCellStatusBarRegistrations = new Map<number, IDisposable>();
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IWorkingCopyService private readonly _workingCopyService: IWorkingCopyService,
|
||||
@INotebookService private readonly _notebookService: INotebookService,
|
||||
@INotebookEditorService private readonly _notebookEditorService: INotebookEditorService,
|
||||
@IEditorService private readonly _editorService: IEditorService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
@INotebookCellStatusBarService private readonly _cellStatusBarService: INotebookCellStatusBarService,
|
||||
@INotebookEditorModelResolverService private readonly _notebookEditorModelResolverService: INotebookEditorModelResolverService,
|
||||
@IUriIdentityService private readonly _uriIdentityService: IUriIdentityService
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostNotebook);
|
||||
this._modelReferenceCollection = new BoundModelReferenceCollection(this._uriIdentityService.extUri);
|
||||
this._registerListeners();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._disposables.dispose();
|
||||
|
||||
this._modelReferenceCollection.dispose();
|
||||
|
||||
// remove all notebook providers
|
||||
for (const item of this._notebookProviders.values()) {
|
||||
item.disposable.dispose();
|
||||
}
|
||||
|
||||
// remove all kernel providers
|
||||
for (const item of this._notebookKernelProviders.values()) {
|
||||
item.emitter.dispose();
|
||||
item.provider.dispose();
|
||||
}
|
||||
dispose(this._notebookSerializer.values());
|
||||
dispose(this._editorEventListenersMapping.values());
|
||||
dispose(this._documentEventListenersMapping.values());
|
||||
dispose(this._cellStatusBarEntries.values());
|
||||
}
|
||||
|
||||
async $tryApplyEdits(_viewType: string, resource: UriComponents, modelVersionId: number, cellEdits: ICellEditOperation[]): Promise<boolean> {
|
||||
const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));
|
||||
if (!textModel) {
|
||||
return false;
|
||||
}
|
||||
if (textModel.versionId !== modelVersionId) {
|
||||
return false;
|
||||
}
|
||||
return textModel.applyEdits(cellEdits, true, undefined, () => undefined, undefined);
|
||||
}
|
||||
|
||||
async $applyEdits(resource: UriComponents, cellEdits: IImmediateCellEditOperation[], computeUndoRedo = true): Promise<void> {
|
||||
const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));
|
||||
if (!textModel) {
|
||||
throw new Error(`Can't apply edits to unknown notebook model: ${resource}`);
|
||||
}
|
||||
|
||||
textModel.applyEdits(cellEdits, true, undefined, () => undefined, undefined, computeUndoRedo);
|
||||
}
|
||||
|
||||
private _registerListeners(): void {
|
||||
|
||||
// forward changes to dirty state
|
||||
// todo@rebornix todo@mjbvz this seem way too complicated... is there an easy way to
|
||||
// the actual resource from a working copy?
|
||||
this._disposables.add(this._workingCopyService.onDidChangeDirty(e => {
|
||||
if (e.resource.scheme !== Schemas.vscodeNotebook) {
|
||||
return;
|
||||
}
|
||||
for (const notebook of this._notebookService.getNotebookTextModels()) {
|
||||
if (isEqual(notebook.uri.with({ scheme: Schemas.vscodeNotebook }), e.resource)) {
|
||||
this._proxy.$acceptDirtyStateChanged(notebook.uri, e.isDirty());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
this._disposables.add(this._editorService.onDidActiveEditorChange(e => {
|
||||
this._updateState();
|
||||
}));
|
||||
|
||||
this._disposables.add(this._editorService.onDidVisibleEditorsChange(e => {
|
||||
if (this._notebookProviders.size > 0) { // TODO@rebornix propably wrong, what about providers from another host
|
||||
if (!this._currentState) {
|
||||
// no current state means we didn't even create editors in ext host yet.
|
||||
return;
|
||||
}
|
||||
|
||||
// we can't simply update visibleEditors as we need to check if we should create editors first.
|
||||
this._updateState();
|
||||
}
|
||||
}));
|
||||
|
||||
const handleNotebookEditorAdded = (editor: INotebookEditor) => {
|
||||
if (this._editorEventListenersMapping.has(editor.getId())) {
|
||||
//todo@jrieken a bug when this happens?
|
||||
return;
|
||||
}
|
||||
const disposableStore = new DisposableStore();
|
||||
disposableStore.add(editor.onDidChangeVisibleRanges(() => {
|
||||
this._proxy.$acceptEditorPropertiesChanged(editor.getId(), { visibleRanges: { ranges: editor.visibleRanges } });
|
||||
}));
|
||||
|
||||
disposableStore.add(editor.onDidChangeSelection(() => {
|
||||
this._proxy.$acceptEditorPropertiesChanged(editor.getId(), { selections: { selections: editor.getSelections() } });
|
||||
}));
|
||||
|
||||
disposableStore.add(editor.onDidChangeKernel(() => {
|
||||
if (!editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
this._proxy.$acceptNotebookActiveKernelChange({
|
||||
uri: editor.viewModel.uri,
|
||||
providerHandle: editor.activeKernel?.providerHandle,
|
||||
kernelFriendlyId: editor.activeKernel?.friendlyId
|
||||
});
|
||||
}));
|
||||
|
||||
disposableStore.add(editor.onDidChangeModel(() => this._updateState()));
|
||||
disposableStore.add(editor.onDidFocusEditorWidget(() => this._updateState(editor)));
|
||||
|
||||
this._editorEventListenersMapping.set(editor.getId(), disposableStore);
|
||||
|
||||
const activeNotebookEditor = getNotebookEditorFromEditorPane(this._editorService.activeEditorPane);
|
||||
this._updateState(activeNotebookEditor);
|
||||
};
|
||||
|
||||
this._notebookEditorService.listNotebookEditors().forEach(handleNotebookEditorAdded);
|
||||
this._disposables.add(this._notebookEditorService.onDidAddNotebookEditor(handleNotebookEditorAdded));
|
||||
|
||||
this._disposables.add(this._notebookEditorService.onDidRemoveNotebookEditor(editor => {
|
||||
this._editorEventListenersMapping.get(editor.getId())?.dispose();
|
||||
this._editorEventListenersMapping.delete(editor.getId());
|
||||
this._updateState();
|
||||
}));
|
||||
|
||||
|
||||
const cellToDto = (cell: NotebookCellTextModel): IMainCellDto => {
|
||||
return {
|
||||
handle: cell.handle,
|
||||
uri: cell.uri,
|
||||
source: cell.textBuffer.getLinesContent(),
|
||||
eol: cell.textBuffer.getEOL(),
|
||||
language: cell.language,
|
||||
cellKind: cell.cellKind,
|
||||
outputs: cell.outputs,
|
||||
metadata: cell.metadata
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
const handleNotebookDocumentAdded = (textModel: NotebookTextModel) => {
|
||||
if (this._documentEventListenersMapping.has(textModel.uri)) {
|
||||
//todo@jrieken a bug when this happens?
|
||||
return;
|
||||
}
|
||||
const disposableStore = new DisposableStore();
|
||||
disposableStore.add(textModel!.onDidChangeContent(event => {
|
||||
const dto = event.rawEvents.map(e => {
|
||||
const data =
|
||||
e.kind === NotebookCellsChangeType.ModelChange || e.kind === NotebookCellsChangeType.Initialize
|
||||
? {
|
||||
kind: e.kind,
|
||||
versionId: event.versionId,
|
||||
changes: e.changes.map(diff => [diff[0], diff[1], diff[2].map(cell => cellToDto(cell as NotebookCellTextModel))] as [number, number, IMainCellDto[]])
|
||||
}
|
||||
: (
|
||||
e.kind === NotebookCellsChangeType.Move
|
||||
? {
|
||||
kind: e.kind,
|
||||
index: e.index,
|
||||
length: e.length,
|
||||
newIdx: e.newIdx,
|
||||
versionId: event.versionId,
|
||||
cells: e.cells.map(cell => cellToDto(cell as NotebookCellTextModel))
|
||||
}
|
||||
: e
|
||||
);
|
||||
|
||||
return data;
|
||||
});
|
||||
|
||||
/**
|
||||
* TODO@rebornix, @jrieken
|
||||
* When a document is modified, it will trigger onDidChangeContent events.
|
||||
* The first event listener is this one, which doesn't know if the text model is dirty or not. It can ask `workingCopyService` but get the wrong result
|
||||
* The second event listener is `NotebookEditorModel`, which will then set `isDirty` to `true`.
|
||||
* Since `e.transient` decides if the model should be dirty or not, we will use the same logic here.
|
||||
*/
|
||||
const hasNonTransientEvent = event.rawEvents.find(e => !e.transient);
|
||||
this._proxy.$acceptModelChanged(textModel.uri, {
|
||||
rawEvents: dto,
|
||||
versionId: event.versionId
|
||||
}, !!hasNonTransientEvent);
|
||||
|
||||
const hasDocumentMetadataChangeEvent = event.rawEvents.find(e => e.kind === NotebookCellsChangeType.ChangeDocumentMetadata);
|
||||
if (!!hasDocumentMetadataChangeEvent) {
|
||||
this._proxy.$acceptDocumentPropertiesChanged(textModel.uri, { metadata: textModel.metadata });
|
||||
}
|
||||
}));
|
||||
this._documentEventListenersMapping.set(textModel!.uri, disposableStore);
|
||||
};
|
||||
|
||||
this._notebookService.listNotebookDocuments().forEach(handleNotebookDocumentAdded);
|
||||
this._disposables.add(this._notebookService.onDidAddNotebookDocument(document => {
|
||||
handleNotebookDocumentAdded(document);
|
||||
this._updateState();
|
||||
}));
|
||||
|
||||
this._disposables.add(this._notebookService.onDidRemoveNotebookDocument(uri => {
|
||||
this._documentEventListenersMapping.get(uri)?.dispose();
|
||||
this._documentEventListenersMapping.delete(uri);
|
||||
this._updateState();
|
||||
}));
|
||||
|
||||
this._disposables.add(this._notebookService.onDidChangeNotebookActiveKernel(e => {
|
||||
this._proxy.$acceptNotebookActiveKernelChange(e);
|
||||
}));
|
||||
|
||||
this._disposables.add(this._notebookEditorModelResolverService.onDidSaveNotebook(e => {
|
||||
this._proxy.$acceptModelSaved(e);
|
||||
}));
|
||||
|
||||
const notebookEditor = getNotebookEditorFromEditorPane(this._editorService.activeEditorPane);
|
||||
this._updateState(notebookEditor);
|
||||
}
|
||||
|
||||
private _updateState(focusedNotebookEditor?: INotebookEditor): void {
|
||||
|
||||
const activeNotebookEditor = getNotebookEditorFromEditorPane(this._editorService.activeEditorPane);
|
||||
let activeEditor = activeNotebookEditor?.hasModel() ? activeNotebookEditor.getId() : null;
|
||||
|
||||
const editors = new Map<string, IActiveNotebookEditor>();
|
||||
const visibleEditorsMap = new Map<string, IActiveNotebookEditor>();
|
||||
|
||||
for (const editor of this._notebookEditorService.listNotebookEditors()) {
|
||||
if (editor.hasModel()) {
|
||||
editors.set(editor.getId(), editor);
|
||||
}
|
||||
}
|
||||
|
||||
this._editorService.visibleEditorPanes.forEach(editorPane => {
|
||||
const notebookEditor = getNotebookEditorFromEditorPane(editorPane);
|
||||
if (notebookEditor?.hasModel() && editors.has(notebookEditor.getId())) {
|
||||
visibleEditorsMap.set(notebookEditor.getId(), notebookEditor);
|
||||
}
|
||||
});
|
||||
|
||||
if (!activeEditor && focusedNotebookEditor?.textModel) {
|
||||
activeEditor = focusedNotebookEditor.getId();
|
||||
}
|
||||
|
||||
const newState = new NotebookAndEditorState(new Set(this._notebookService.listNotebookDocuments()), editors, activeEditor, visibleEditorsMap);
|
||||
const delta = NotebookAndEditorState.compute(this._currentState, newState);
|
||||
|
||||
this._currentState = newState;
|
||||
if (!this._isDeltaEmpty(delta)) {
|
||||
return this._proxy.$acceptDocumentAndEditorsDelta(delta);
|
||||
}
|
||||
}
|
||||
|
||||
private _isDeltaEmpty(delta: INotebookDocumentsAndEditorsDelta): boolean {
|
||||
if (delta.addedDocuments !== undefined && delta.addedDocuments.length > 0) {
|
||||
return false;
|
||||
}
|
||||
if (delta.removedDocuments !== undefined && delta.removedDocuments.length > 0) {
|
||||
return false;
|
||||
}
|
||||
if (delta.addedEditors !== undefined && delta.addedEditors.length > 0) {
|
||||
return false;
|
||||
}
|
||||
if (delta.removedEditors !== undefined && delta.removedEditors.length > 0) {
|
||||
return false;
|
||||
}
|
||||
if (delta.visibleEditors !== undefined && delta.visibleEditors.length > 0) {
|
||||
return false;
|
||||
}
|
||||
if (delta.newActiveEditor !== undefined) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async $registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, options: {
|
||||
transientOutputs: boolean;
|
||||
transientMetadata: TransientMetadata;
|
||||
transientCellMetadata: TransientCellMetadata;
|
||||
transientDocumentMetadata: TransientDocumentMetadata;
|
||||
viewOptions?: { displayName: string; filenamePattern: (string | IRelativePattern | INotebookExclusiveDocumentFilter)[]; exclusive: boolean; };
|
||||
}): Promise<void> {
|
||||
let contentOptions = { transientOutputs: options.transientOutputs, transientMetadata: options.transientMetadata };
|
||||
let contentOptions = { transientOutputs: options.transientOutputs, transientCellMetadata: options.transientCellMetadata, transientDocumentMetadata: options.transientDocumentMetadata };
|
||||
|
||||
const controller: IMainNotebookController = {
|
||||
const controller: INotebookContentProvider = {
|
||||
get options() {
|
||||
return contentOptions;
|
||||
},
|
||||
set options(newOptions) {
|
||||
contentOptions.transientMetadata = newOptions.transientMetadata;
|
||||
contentOptions.transientCellMetadata = newOptions.transientCellMetadata;
|
||||
contentOptions.transientDocumentMetadata = newOptions.transientDocumentMetadata;
|
||||
contentOptions.transientOutputs = newOptions.transientOutputs;
|
||||
},
|
||||
viewOptions: options.viewOptions,
|
||||
@@ -421,12 +68,6 @@ export class MainThreadNotebooks implements MainThreadNotebookShape {
|
||||
transientOptions: contentOptions
|
||||
};
|
||||
},
|
||||
resolveNotebookEditor: async (viewType: string, uri: URI, editorId: string) => {
|
||||
await this._proxy.$resolveNotebookEditor(viewType, uri, editorId);
|
||||
},
|
||||
onDidReceiveMessage: (editorId: string, rendererType: string | undefined, message: unknown) => {
|
||||
this._proxy.$onDidReceiveMessage(editorId, rendererType, message);
|
||||
},
|
||||
save: async (uri: URI, token: CancellationToken) => {
|
||||
return this._proxy.$saveNotebook(viewType, uri, token);
|
||||
},
|
||||
@@ -442,7 +83,7 @@ export class MainThreadNotebooks implements MainThreadNotebookShape {
|
||||
this._notebookProviders.set(viewType, { controller, disposable });
|
||||
}
|
||||
|
||||
async $updateNotebookProviderOptions(viewType: string, options?: { transientOutputs: boolean; transientMetadata: TransientMetadata; }): Promise<void> {
|
||||
async $updateNotebookProviderOptions(viewType: string, options?: { transientOutputs: boolean; transientCellMetadata: TransientCellMetadata; transientDocumentMetadata: TransientDocumentMetadata; }): Promise<void> {
|
||||
const provider = this._notebookProviders.get(viewType);
|
||||
|
||||
if (provider && options) {
|
||||
@@ -467,10 +108,10 @@ export class MainThreadNotebooks implements MainThreadNotebookShape {
|
||||
const registration = this._notebookService.registerNotebookSerializer(viewType, extension, {
|
||||
options,
|
||||
dataToNotebook: (data: VSBuffer): Promise<NotebookDataDto> => {
|
||||
return this._proxy.$dataToNotebook(handle, data);
|
||||
return this._proxy.$dataToNotebook(handle, data, CancellationToken.None);
|
||||
},
|
||||
notebookToData: (data: NotebookDataDto): Promise<VSBuffer> => {
|
||||
return this._proxy.$notebookToData(handle, data);
|
||||
return this._proxy.$notebookToData(handle, data, CancellationToken.None);
|
||||
}
|
||||
});
|
||||
this._notebookSerializer.set(handle, registration);
|
||||
@@ -481,174 +122,51 @@ export class MainThreadNotebooks implements MainThreadNotebookShape {
|
||||
this._notebookSerializer.delete(handle);
|
||||
}
|
||||
|
||||
async $registerNotebookKernelProvider(extension: NotebookExtensionDescription, handle: number, documentFilter: INotebookDocumentFilter): Promise<void> {
|
||||
const emitter = new Emitter<URI | undefined>();
|
||||
$emitCellStatusBarEvent(eventHandle: number): void {
|
||||
const emitter = this._notebookCellStatusBarRegistrations.get(eventHandle);
|
||||
if (emitter instanceof Emitter) {
|
||||
emitter.fire(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
async $registerNotebookCellStatusBarItemProvider(handle: number, eventHandle: number | undefined, selector: NotebookSelector): Promise<void> {
|
||||
const that = this;
|
||||
|
||||
const provider = this._notebookService.registerNotebookKernelProvider({
|
||||
providerExtensionId: extension.id.value,
|
||||
providerDescription: extension.description,
|
||||
onDidChangeKernels: emitter.event,
|
||||
selector: documentFilter,
|
||||
provideKernels: async (uri: URI, token: CancellationToken): Promise<INotebookKernel[]> => {
|
||||
const result: INotebookKernel[] = [];
|
||||
const kernelsDto = await that._proxy.$provideNotebookKernels(handle, uri, token);
|
||||
for (const dto of kernelsDto) {
|
||||
result.push({
|
||||
id: dto.id,
|
||||
friendlyId: dto.friendlyId,
|
||||
label: dto.label,
|
||||
extension: dto.extension,
|
||||
extensionLocation: URI.revive(dto.extensionLocation),
|
||||
providerHandle: dto.providerHandle,
|
||||
description: dto.description,
|
||||
detail: dto.detail,
|
||||
isPreferred: dto.isPreferred,
|
||||
preloads: dto.preloads?.map(u => URI.revive(u)),
|
||||
supportedLanguages: dto.supportedLanguages,
|
||||
implementsInterrupt: dto.implementsInterrupt,
|
||||
resolve: (uri: URI, editorId: string, token: CancellationToken): Promise<void> => {
|
||||
this._logService.debug('MainthreadNotebooks.resolveNotebookKernel', uri.path, dto.friendlyId);
|
||||
return this._proxy.$resolveNotebookKernel(handle, editorId, uri, dto.friendlyId, token);
|
||||
},
|
||||
executeNotebookCellsRequest: (uri: URI, cellRanges: ICellRange[]): Promise<void> => {
|
||||
this._logService.debug('MainthreadNotebooks.executeNotebookCell', uri.path, dto.friendlyId, cellRanges);
|
||||
return this._proxy.$executeNotebookKernelFromProvider(handle, uri, dto.friendlyId, cellRanges);
|
||||
},
|
||||
cancelNotebookCellExecution: (uri: URI, cellRanges: ICellRange[]): Promise<void> => {
|
||||
this._logService.debug('MainthreadNotebooks.cancelNotebookCellExecution', uri.path, dto.friendlyId, cellRanges);
|
||||
return this._proxy.$cancelNotebookCellExecution(handle, uri, dto.friendlyId, cellRanges);
|
||||
const provider: INotebookCellStatusBarItemProvider = {
|
||||
async provideCellStatusBarItems(uri: URI, index: number, token: CancellationToken) {
|
||||
const result = await that._proxy.$provideNotebookCellStatusBarItems(handle, uri, index, token);
|
||||
return {
|
||||
items: result?.items ?? [],
|
||||
dispose() {
|
||||
if (result) {
|
||||
that._proxy.$releaseNotebookCellStatusBarItems(result.cacheId);
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
this._notebookKernelProviders.set(handle, { extension, emitter, provider });
|
||||
return;
|
||||
}
|
||||
|
||||
async $unregisterNotebookKernelProvider(handle: number): Promise<void> {
|
||||
const entry = this._notebookKernelProviders.get(handle);
|
||||
|
||||
if (entry) {
|
||||
entry.emitter.dispose();
|
||||
entry.provider.dispose();
|
||||
this._notebookKernelProviders.delete(handle);
|
||||
}
|
||||
}
|
||||
|
||||
$onNotebookKernelChange(handle: number, uriComponents: UriComponents): void {
|
||||
const entry = this._notebookKernelProviders.get(handle);
|
||||
|
||||
entry?.emitter.fire(uriComponents ? URI.revive(uriComponents) : undefined);
|
||||
}
|
||||
|
||||
async $postMessage(id: string, forRendererId: string | undefined, value: any): Promise<boolean> {
|
||||
const editor = this._notebookEditorService.getNotebookEditor(id);
|
||||
if (!editor) {
|
||||
return false;
|
||||
}
|
||||
editor.postMessage(forRendererId, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
async $tryRevealRange(id: string, range: ICellRange, revealType: NotebookEditorRevealType): Promise<void> {
|
||||
const editor = this._notebookEditorService.getNotebookEditor(id);
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
const notebookEditor = editor as INotebookEditor;
|
||||
if (!notebookEditor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
const viewModel = notebookEditor.viewModel;
|
||||
const cell = viewModel.viewCells[range.start];
|
||||
if (!cell) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (revealType) {
|
||||
case NotebookEditorRevealType.Default:
|
||||
return notebookEditor.revealCellRangeInView(range);
|
||||
case NotebookEditorRevealType.InCenter:
|
||||
return notebookEditor.revealInCenter(cell);
|
||||
case NotebookEditorRevealType.InCenterIfOutsideViewport:
|
||||
return notebookEditor.revealInCenterIfOutsideViewport(cell);
|
||||
case NotebookEditorRevealType.AtTop:
|
||||
return notebookEditor.revealInViewAtTop(cell);
|
||||
}
|
||||
}
|
||||
|
||||
$registerNotebookEditorDecorationType(key: string, options: INotebookDecorationRenderOptions): void {
|
||||
this._notebookEditorService.registerEditorDecorationType(key, options);
|
||||
}
|
||||
|
||||
$removeNotebookEditorDecorationType(key: string): void {
|
||||
this._notebookEditorService.removeEditorDecorationType(key);
|
||||
}
|
||||
|
||||
$trySetDecorations(id: string, range: ICellRange, key: string): void {
|
||||
const editor = this._notebookEditorService.getNotebookEditor(id);
|
||||
if (editor) {
|
||||
const notebookEditor = editor as INotebookEditor;
|
||||
notebookEditor.setEditorDecorations(key, range);
|
||||
}
|
||||
}
|
||||
|
||||
async $setStatusBarEntry(id: number, rawStatusBarEntry: INotebookCellStatusBarEntryDto): Promise<void> {
|
||||
const statusBarEntry = {
|
||||
...rawStatusBarEntry,
|
||||
...{ cellResource: URI.revive(rawStatusBarEntry.cellResource) }
|
||||
}
|
||||
};
|
||||
},
|
||||
selector: selector
|
||||
};
|
||||
|
||||
const existingEntry = this._cellStatusBarEntries.get(id);
|
||||
if (existingEntry) {
|
||||
existingEntry.dispose();
|
||||
if (typeof eventHandle === 'number') {
|
||||
const emitter = new Emitter<void>();
|
||||
this._notebookCellStatusBarRegistrations.set(eventHandle, emitter);
|
||||
provider.onDidChangeStatusBarItems = emitter.event;
|
||||
}
|
||||
|
||||
if (statusBarEntry.visible) {
|
||||
this._cellStatusBarEntries.set(id, this._cellStatusBarService.addEntry(statusBarEntry));
|
||||
}
|
||||
const disposable = this._cellStatusBarService.registerCellStatusBarItemProvider(provider);
|
||||
this._notebookCellStatusBarRegistrations.set(handle, disposable);
|
||||
}
|
||||
|
||||
|
||||
async $tryOpenDocument(uriComponents: UriComponents): Promise<URI> {
|
||||
const uri = URI.revive(uriComponents);
|
||||
const ref = await this._notebookEditorModelResolverService.resolve(uri, undefined);
|
||||
this._modelReferenceCollection.add(uri, ref);
|
||||
return uri;
|
||||
}
|
||||
|
||||
async $trySaveDocument(uriComponents: UriComponents) {
|
||||
const uri = URI.revive(uriComponents);
|
||||
|
||||
const ref = await this._notebookEditorModelResolverService.resolve(uri);
|
||||
const saveResult = await ref.object.save();
|
||||
ref.dispose();
|
||||
return saveResult;
|
||||
}
|
||||
|
||||
async $tryShowNotebookDocument(resource: UriComponents, viewType: string, options: INotebookDocumentShowOptions): Promise<string> {
|
||||
const editorOptions = new NotebookEditorOptions({
|
||||
cellSelections: options.selection && [options.selection],
|
||||
preserveFocus: options.preserveFocus,
|
||||
pinned: options.pinned,
|
||||
// selection: options.selection,
|
||||
// preserve pre 1.38 behaviour to not make group active when preserveFocus: true
|
||||
// but make sure to restore the editor to fix https://github.com/microsoft/vscode/issues/79633
|
||||
activation: options.preserveFocus ? EditorActivation.RESTORE : undefined,
|
||||
override: EditorOverride.DISABLED,
|
||||
});
|
||||
|
||||
const input = NotebookEditorInput.create(this._instantiationService, URI.revive(resource), viewType);
|
||||
const editorPane = await this._editorService.openEditor(input, editorOptions, options.position);
|
||||
const notebookEditor = getNotebookEditorFromEditorPane(editorPane);
|
||||
|
||||
if (notebookEditor) {
|
||||
return notebookEditor.getId();
|
||||
} else {
|
||||
throw new Error(`Notebook Editor creation failure for documenet ${resource}`);
|
||||
async $unregisterNotebookCellStatusBarItemProvider(handle: number, eventHandle: number | undefined): Promise<void> {
|
||||
const unregisterThing = (handle: number) => {
|
||||
const entry = this._notebookCellStatusBarRegistrations.get(handle);
|
||||
if (entry) {
|
||||
this._notebookCellStatusBarRegistrations.get(handle)?.dispose();
|
||||
this._notebookCellStatusBarRegistrations.delete(handle);
|
||||
}
|
||||
};
|
||||
unregisterThing(handle);
|
||||
if (typeof eventHandle === 'number') {
|
||||
unregisterThing(eventHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { DisposableStore, dispose } from 'vs/base/common/lifecycle';
|
||||
import { ResourceMap } from 'vs/base/common/map';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { BoundModelReferenceCollection } from 'vs/workbench/api/browser/mainThreadDocuments';
|
||||
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { IImmediateCellEditOperation, IMainCellDto, NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService';
|
||||
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
|
||||
import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, MainThreadNotebookDocumentsShape } from '../common/extHost.protocol';
|
||||
import { MainThreadNotebooksAndEditors } from 'vs/workbench/api/browser/mainThreadNotebookDocumentsAndEditors';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
|
||||
export class MainThreadNotebookDocuments implements MainThreadNotebookDocumentsShape {
|
||||
|
||||
private readonly _disposables = new DisposableStore();
|
||||
|
||||
private readonly _proxy: ExtHostNotebookShape;
|
||||
private readonly _documentEventListenersMapping = new ResourceMap<DisposableStore>();
|
||||
private readonly _modelReferenceCollection: BoundModelReferenceCollection;
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
notebooksAndEditors: MainThreadNotebooksAndEditors,
|
||||
@INotebookService private readonly _notebookService: INotebookService,
|
||||
@INotebookEditorModelResolverService private readonly _notebookEditorModelResolverService: INotebookEditorModelResolverService,
|
||||
@IUriIdentityService private readonly _uriIdentityService: IUriIdentityService
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostNotebook);
|
||||
this._modelReferenceCollection = new BoundModelReferenceCollection(this._uriIdentityService.extUri);
|
||||
|
||||
notebooksAndEditors.onDidAddNotebooks(this._handleNotebooksAdded, this, this._disposables);
|
||||
notebooksAndEditors.onDidRemoveNotebooks(this._handleNotebooksRemoved, this, this._disposables);
|
||||
|
||||
// forward dirty and save events
|
||||
this._disposables.add(this._notebookEditorModelResolverService.onDidChangeDirty(model => this._proxy.$acceptDirtyStateChanged(model.resource, model.isDirty())));
|
||||
this._disposables.add(this._notebookEditorModelResolverService.onDidSaveNotebook(e => this._proxy.$acceptModelSaved(e)));
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._disposables.dispose();
|
||||
this._modelReferenceCollection.dispose();
|
||||
dispose(this._documentEventListenersMapping.values());
|
||||
|
||||
}
|
||||
|
||||
private _handleNotebooksAdded(notebooks: readonly NotebookTextModel[]): void {
|
||||
|
||||
for (const textModel of notebooks) {
|
||||
const disposableStore = new DisposableStore();
|
||||
disposableStore.add(textModel.onDidChangeContent(event => {
|
||||
const dto = event.rawEvents.map(e => {
|
||||
const data =
|
||||
e.kind === NotebookCellsChangeType.ModelChange || e.kind === NotebookCellsChangeType.Initialize
|
||||
? {
|
||||
kind: e.kind,
|
||||
versionId: event.versionId,
|
||||
changes: e.changes.map(diff => [diff[0], diff[1], diff[2].map(cell => MainThreadNotebookDocuments._cellToDto(cell as NotebookCellTextModel))] as [number, number, IMainCellDto[]])
|
||||
}
|
||||
: (
|
||||
e.kind === NotebookCellsChangeType.Move
|
||||
? {
|
||||
kind: e.kind,
|
||||
index: e.index,
|
||||
length: e.length,
|
||||
newIdx: e.newIdx,
|
||||
versionId: event.versionId,
|
||||
cells: e.cells.map(cell => MainThreadNotebookDocuments._cellToDto(cell as NotebookCellTextModel))
|
||||
}
|
||||
: e
|
||||
);
|
||||
|
||||
return data;
|
||||
});
|
||||
|
||||
// using the model resolver service to know if the model is dirty or not.
|
||||
// assuming this is the first listener it can mean that at first the model
|
||||
// is marked as dirty and that another event is fired
|
||||
this._proxy.$acceptModelChanged(
|
||||
textModel.uri,
|
||||
{ rawEvents: dto, versionId: event.versionId },
|
||||
this._notebookEditorModelResolverService.isDirty(textModel.uri)
|
||||
);
|
||||
|
||||
const hasDocumentMetadataChangeEvent = event.rawEvents.find(e => e.kind === NotebookCellsChangeType.ChangeDocumentMetadata);
|
||||
if (hasDocumentMetadataChangeEvent) {
|
||||
this._proxy.$acceptDocumentPropertiesChanged(textModel.uri, { metadata: textModel.metadata });
|
||||
}
|
||||
}));
|
||||
|
||||
this._documentEventListenersMapping.set(textModel.uri, disposableStore);
|
||||
}
|
||||
}
|
||||
|
||||
private _handleNotebooksRemoved(uris: URI[]): void {
|
||||
for (const uri of uris) {
|
||||
this._documentEventListenersMapping.get(uri)?.dispose();
|
||||
this._documentEventListenersMapping.delete(uri);
|
||||
}
|
||||
}
|
||||
|
||||
private static _cellToDto(cell: NotebookCellTextModel): IMainCellDto {
|
||||
return {
|
||||
handle: cell.handle,
|
||||
uri: cell.uri,
|
||||
source: cell.textBuffer.getLinesContent(),
|
||||
eol: cell.textBuffer.getEOL(),
|
||||
language: cell.language,
|
||||
cellKind: cell.cellKind,
|
||||
outputs: cell.outputs,
|
||||
metadata: cell.metadata
|
||||
};
|
||||
}
|
||||
|
||||
async $tryOpenDocument(uriComponents: UriComponents): Promise<URI> {
|
||||
const uri = URI.revive(uriComponents);
|
||||
const ref = await this._notebookEditorModelResolverService.resolve(uri, undefined);
|
||||
this._modelReferenceCollection.add(uri, ref);
|
||||
return uri;
|
||||
}
|
||||
|
||||
async $trySaveDocument(uriComponents: UriComponents) {
|
||||
const uri = URI.revive(uriComponents);
|
||||
|
||||
const ref = await this._notebookEditorModelResolverService.resolve(uri);
|
||||
const saveResult = await ref.object.save();
|
||||
ref.dispose();
|
||||
return saveResult;
|
||||
}
|
||||
|
||||
async $applyEdits(resource: UriComponents, cellEdits: IImmediateCellEditOperation[], computeUndoRedo = true): Promise<void> {
|
||||
const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));
|
||||
if (!textModel) {
|
||||
throw new Error(`Can't apply edits to unknown notebook model: ${URI.revive(resource).toString()}`);
|
||||
}
|
||||
|
||||
try {
|
||||
textModel.applyEdits(cellEdits, true, undefined, () => undefined, undefined, computeUndoRedo);
|
||||
} catch (e) {
|
||||
// Clearing outputs at the same time as the EH calling append/replaceOutputItems is an expected race, and it should be a no-op.
|
||||
// And any other failure should not throw back to the extension.
|
||||
onUnexpectedError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { diffMaps, diffSets } from 'vs/base/common/collections';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { combinedDisposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { MainThreadNotebookDocuments } from 'vs/workbench/api/browser/mainThreadNotebookDocuments';
|
||||
import { MainThreadNotebookEditors } from 'vs/workbench/api/browser/mainThreadNotebookEditors';
|
||||
import { extHostCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { editorGroupToViewColumn } from 'vs/workbench/common/editor';
|
||||
import { getNotebookEditorFromEditorPane, IActiveNotebookEditor, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/notebookEditorService';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookDocumentsAndEditorsDelta, INotebookEditorAddData, INotebookModelAddedData, MainContext } from '../common/extHost.protocol';
|
||||
|
||||
interface INotebookAndEditorDelta {
|
||||
removedDocuments: URI[];
|
||||
addedDocuments: NotebookTextModel[];
|
||||
removedEditors: string[];
|
||||
addedEditors: IActiveNotebookEditor[];
|
||||
newActiveEditor?: string | null;
|
||||
visibleEditors?: string[];
|
||||
}
|
||||
|
||||
class NotebookAndEditorState {
|
||||
static compute(before: NotebookAndEditorState | undefined, after: NotebookAndEditorState): INotebookAndEditorDelta {
|
||||
if (!before) {
|
||||
return {
|
||||
addedDocuments: [...after.documents],
|
||||
removedDocuments: [],
|
||||
addedEditors: [...after.textEditors.values()],
|
||||
removedEditors: [],
|
||||
visibleEditors: [...after.visibleEditors].map(editor => editor[0])
|
||||
};
|
||||
}
|
||||
const documentDelta = diffSets(before.documents, after.documents);
|
||||
const editorDelta = diffMaps(before.textEditors, after.textEditors);
|
||||
|
||||
const newActiveEditor = before.activeEditor !== after.activeEditor ? after.activeEditor : undefined;
|
||||
const visibleEditorDelta = diffMaps(before.visibleEditors, after.visibleEditors);
|
||||
|
||||
return {
|
||||
addedDocuments: documentDelta.added,
|
||||
removedDocuments: documentDelta.removed.map(e => e.uri),
|
||||
addedEditors: editorDelta.added,
|
||||
removedEditors: editorDelta.removed.map(removed => removed.getId()),
|
||||
newActiveEditor: newActiveEditor,
|
||||
visibleEditors: visibleEditorDelta.added.length === 0 && visibleEditorDelta.removed.length === 0
|
||||
? undefined
|
||||
: [...after.visibleEditors].map(editor => editor[0])
|
||||
};
|
||||
}
|
||||
|
||||
constructor(
|
||||
readonly documents: Set<NotebookTextModel>,
|
||||
readonly textEditors: Map<string, IActiveNotebookEditor>,
|
||||
readonly activeEditor: string | null | undefined,
|
||||
readonly visibleEditors: Map<string, IActiveNotebookEditor>
|
||||
) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
@extHostCustomer
|
||||
export class MainThreadNotebooksAndEditors {
|
||||
|
||||
private readonly _onDidAddNotebooks = new Emitter<NotebookTextModel[]>();
|
||||
private readonly _onDidRemoveNotebooks = new Emitter<URI[]>();
|
||||
private readonly _onDidAddEditors = new Emitter<IActiveNotebookEditor[]>();
|
||||
private readonly _onDidRemoveEditors = new Emitter<string[]>();
|
||||
|
||||
readonly onDidAddNotebooks: Event<NotebookTextModel[]> = this._onDidAddNotebooks.event;
|
||||
readonly onDidRemoveNotebooks: Event<URI[]> = this._onDidRemoveNotebooks.event;
|
||||
readonly onDidAddEditors: Event<IActiveNotebookEditor[]> = this._onDidAddEditors.event;
|
||||
readonly onDidRemoveEditors: Event<string[]> = this._onDidRemoveEditors.event;
|
||||
|
||||
private readonly _proxy: Pick<ExtHostNotebookShape, '$acceptDocumentAndEditorsDelta'>;
|
||||
private readonly _disposables = new DisposableStore();
|
||||
|
||||
private readonly _editorListeners = new Map<string, IDisposable>();
|
||||
|
||||
private _currentState?: NotebookAndEditorState;
|
||||
|
||||
private readonly _mainThreadNotebooks: MainThreadNotebookDocuments;
|
||||
private readonly _mainThreadEditors: MainThreadNotebookEditors;
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@INotebookService private readonly _notebookService: INotebookService,
|
||||
@INotebookEditorService private readonly _notebookEditorService: INotebookEditorService,
|
||||
@IEditorService private readonly _editorService: IEditorService,
|
||||
@IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService,
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostNotebook);
|
||||
|
||||
this._mainThreadNotebooks = instantiationService.createInstance(MainThreadNotebookDocuments, extHostContext, this);
|
||||
this._mainThreadEditors = instantiationService.createInstance(MainThreadNotebookEditors, extHostContext, this);
|
||||
|
||||
extHostContext.set(MainContext.MainThreadNotebookDocuments, this._mainThreadNotebooks);
|
||||
extHostContext.set(MainContext.MainThreadNotebookEditors, this._mainThreadEditors);
|
||||
|
||||
this._notebookService.onDidCreateNotebookDocument(() => this._updateState(), this, this._disposables);
|
||||
this._notebookService.onDidRemoveNotebookDocument(() => this._updateState(), this, this._disposables);
|
||||
this._editorService.onDidActiveEditorChange(() => this._updateState(), this, this._disposables);
|
||||
this._editorService.onDidVisibleEditorsChange(() => this._updateState(), this, this._disposables);
|
||||
this._notebookEditorService.onDidAddNotebookEditor(this._handleEditorAdd, this, this._disposables);
|
||||
this._notebookEditorService.onDidRemoveNotebookEditor(this._handleEditorRemove, this, this._disposables);
|
||||
this._updateState();
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this._mainThreadNotebooks.dispose();
|
||||
this._mainThreadEditors.dispose();
|
||||
this._onDidAddEditors.dispose();
|
||||
this._onDidRemoveEditors.dispose();
|
||||
this._onDidAddNotebooks.dispose();
|
||||
this._onDidRemoveNotebooks.dispose();
|
||||
this._disposables.dispose();
|
||||
}
|
||||
|
||||
private _handleEditorAdd(editor: INotebookEditor): void {
|
||||
this._editorListeners.set(editor.getId(), combinedDisposable(
|
||||
editor.onDidChangeModel(() => this._updateState()),
|
||||
editor.onDidFocusEditorWidget(() => this._updateState(editor)),
|
||||
));
|
||||
this._updateState();
|
||||
}
|
||||
|
||||
private _handleEditorRemove(editor: INotebookEditor): void {
|
||||
this._editorListeners.get(editor.getId())?.dispose();
|
||||
this._editorListeners.delete(editor.getId());
|
||||
this._updateState();
|
||||
}
|
||||
|
||||
private _updateState(focusedEditor?: INotebookEditor): void {
|
||||
|
||||
const editors = new Map<string, IActiveNotebookEditor>();
|
||||
const visibleEditorsMap = new Map<string, IActiveNotebookEditor>();
|
||||
|
||||
for (const editor of this._notebookEditorService.listNotebookEditors()) {
|
||||
if (editor.hasModel()) {
|
||||
editors.set(editor.getId(), editor);
|
||||
}
|
||||
}
|
||||
|
||||
const activeNotebookEditor = getNotebookEditorFromEditorPane(this._editorService.activeEditorPane);
|
||||
let activeEditor: string | null = null;
|
||||
if (activeNotebookEditor) {
|
||||
activeEditor = activeNotebookEditor.getId();
|
||||
} else if (focusedEditor?.textModel) {
|
||||
activeEditor = focusedEditor.getId();
|
||||
}
|
||||
if (activeEditor && !editors.has(activeEditor)) {
|
||||
activeEditor = null;
|
||||
}
|
||||
|
||||
for (const editorPane of this._editorService.visibleEditorPanes) {
|
||||
const notebookEditor = getNotebookEditorFromEditorPane(editorPane);
|
||||
if (notebookEditor?.hasModel() && editors.has(notebookEditor.getId())) {
|
||||
visibleEditorsMap.set(notebookEditor.getId(), notebookEditor);
|
||||
}
|
||||
}
|
||||
|
||||
const newState = new NotebookAndEditorState(new Set(this._notebookService.listNotebookDocuments()), editors, activeEditor, visibleEditorsMap);
|
||||
this._onDelta(NotebookAndEditorState.compute(this._currentState, newState));
|
||||
this._currentState = newState;
|
||||
}
|
||||
|
||||
private _onDelta(delta: INotebookAndEditorDelta): void {
|
||||
if (MainThreadNotebooksAndEditors._isDeltaEmpty(delta)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dto: INotebookDocumentsAndEditorsDelta = {
|
||||
removedDocuments: delta.removedDocuments,
|
||||
removedEditors: delta.removedEditors,
|
||||
newActiveEditor: delta.newActiveEditor,
|
||||
visibleEditors: delta.visibleEditors,
|
||||
addedDocuments: delta.addedDocuments.map(MainThreadNotebooksAndEditors._asModelAddData),
|
||||
addedEditors: delta.addedEditors.map(this._asEditorAddData, this),
|
||||
};
|
||||
|
||||
// send to extension FIRST
|
||||
this._proxy.$acceptDocumentAndEditorsDelta(dto);
|
||||
|
||||
// handle internally
|
||||
this._onDidRemoveEditors.fire(delta.removedEditors);
|
||||
this._onDidRemoveNotebooks.fire(delta.removedDocuments);
|
||||
this._onDidAddNotebooks.fire(delta.addedDocuments);
|
||||
this._onDidAddEditors.fire(delta.addedEditors);
|
||||
}
|
||||
|
||||
private static _isDeltaEmpty(delta: INotebookAndEditorDelta): boolean {
|
||||
if (delta.addedDocuments !== undefined && delta.addedDocuments.length > 0) {
|
||||
return false;
|
||||
}
|
||||
if (delta.removedDocuments !== undefined && delta.removedDocuments.length > 0) {
|
||||
return false;
|
||||
}
|
||||
if (delta.addedEditors !== undefined && delta.addedEditors.length > 0) {
|
||||
return false;
|
||||
}
|
||||
if (delta.removedEditors !== undefined && delta.removedEditors.length > 0) {
|
||||
return false;
|
||||
}
|
||||
if (delta.visibleEditors !== undefined && delta.visibleEditors.length > 0) {
|
||||
return false;
|
||||
}
|
||||
if (delta.newActiveEditor !== undefined) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static _asModelAddData(e: NotebookTextModel): INotebookModelAddedData {
|
||||
return {
|
||||
viewType: e.viewType,
|
||||
uri: e.uri,
|
||||
metadata: e.metadata,
|
||||
versionId: e.versionId,
|
||||
cells: e.cells.map(cell => ({
|
||||
handle: cell.handle,
|
||||
uri: cell.uri,
|
||||
source: cell.textBuffer.getLinesContent(),
|
||||
eol: cell.textBuffer.getEOL(),
|
||||
language: cell.language,
|
||||
cellKind: cell.cellKind,
|
||||
outputs: cell.outputs,
|
||||
metadata: cell.metadata
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
private _asEditorAddData(add: IActiveNotebookEditor): INotebookEditorAddData {
|
||||
|
||||
const pane = this._editorService.visibleEditorPanes.find(pane => getNotebookEditorFromEditorPane(pane) === add);
|
||||
|
||||
return {
|
||||
id: add.getId(),
|
||||
documentUri: add.viewModel.uri,
|
||||
selections: add.getSelections(),
|
||||
visibleRanges: add.visibleRanges,
|
||||
viewColumn: pane && editorGroupToViewColumn(this._editorGroupService, pane.group)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { DisposableStore, dispose } from 'vs/base/common/lifecycle';
|
||||
import { getNotebookEditorFromEditorPane, INotebookEditor, NotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/notebookEditorService';
|
||||
import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookDocumentShowOptions, INotebookEditorViewColumnInfo, MainThreadNotebookEditorsShape, NotebookEditorRevealType } from '../common/extHost.protocol';
|
||||
import { MainThreadNotebooksAndEditors } from 'vs/workbench/api/browser/mainThreadNotebookDocumentsAndEditors';
|
||||
import { ICellEditOperation, INotebookDecorationRenderOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { EditorActivation, EditorOverride } from 'vs/platform/editor/common/editor';
|
||||
import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebookEditorInput';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { editorGroupToViewColumn } from 'vs/workbench/common/editor';
|
||||
import { equals } from 'vs/base/common/objects';
|
||||
|
||||
class MainThreadNotebook {
|
||||
|
||||
constructor(
|
||||
readonly editor: INotebookEditor,
|
||||
readonly disposables: DisposableStore
|
||||
) { }
|
||||
|
||||
dispose() {
|
||||
this.disposables.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export class MainThreadNotebookEditors implements MainThreadNotebookEditorsShape {
|
||||
|
||||
private readonly _disposables = new DisposableStore();
|
||||
|
||||
private readonly _proxy: ExtHostNotebookShape;
|
||||
private readonly _mainThreadEditors = new Map<string, MainThreadNotebook>();
|
||||
|
||||
private _currentViewColumnInfo?: INotebookEditorViewColumnInfo;
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
notebooksAndEditors: MainThreadNotebooksAndEditors,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IEditorService private readonly _editorService: IEditorService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
@INotebookEditorService private readonly _notebookEditorService: INotebookEditorService,
|
||||
@IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostNotebook);
|
||||
|
||||
notebooksAndEditors.onDidAddEditors(this._handleEditorsAdded, this, this._disposables);
|
||||
notebooksAndEditors.onDidRemoveEditors(this._handleEditorsRemoved, this, this._disposables);
|
||||
|
||||
this._editorService.onDidActiveEditorChange(() => this._updateEditorViewColumns(), this, this._disposables);
|
||||
this._editorGroupService.onDidRemoveGroup(() => this._updateEditorViewColumns(), this, this._disposables);
|
||||
this._editorGroupService.onDidMoveGroup(() => this._updateEditorViewColumns(), this, this._disposables);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._disposables.dispose();
|
||||
dispose(this._mainThreadEditors.values());
|
||||
}
|
||||
|
||||
private _handleEditorsAdded(editors: readonly INotebookEditor[]): void {
|
||||
|
||||
for (const editor of editors) {
|
||||
|
||||
const editorDisposables = new DisposableStore();
|
||||
editorDisposables.add(editor.onDidChangeVisibleRanges(() => {
|
||||
this._proxy.$acceptEditorPropertiesChanged(editor.getId(), { visibleRanges: { ranges: editor.visibleRanges } });
|
||||
}));
|
||||
|
||||
editorDisposables.add(editor.onDidChangeSelection(() => {
|
||||
this._proxy.$acceptEditorPropertiesChanged(editor.getId(), { selections: { selections: editor.getSelections() } });
|
||||
}));
|
||||
|
||||
const wrapper = new MainThreadNotebook(editor, editorDisposables);
|
||||
this._mainThreadEditors.set(editor.getId(), wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
private _handleEditorsRemoved(editorIds: readonly string[]): void {
|
||||
for (const id of editorIds) {
|
||||
this._mainThreadEditors.get(id)?.dispose();
|
||||
this._mainThreadEditors.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
private _updateEditorViewColumns(): void {
|
||||
const result: INotebookEditorViewColumnInfo = Object.create(null);
|
||||
for (let editorPane of this._editorService.visibleEditorPanes) {
|
||||
const candidate = getNotebookEditorFromEditorPane(editorPane);
|
||||
if (candidate && this._mainThreadEditors.has(candidate.getId())) {
|
||||
result[candidate.getId()] = editorGroupToViewColumn(this._editorGroupService, editorPane.group);
|
||||
}
|
||||
}
|
||||
if (!equals(result, this._currentViewColumnInfo)) {
|
||||
this._currentViewColumnInfo = result;
|
||||
this._proxy.$acceptEditorViewColumns(result);
|
||||
}
|
||||
}
|
||||
|
||||
async $tryApplyEdits(editorId: string, modelVersionId: number, cellEdits: ICellEditOperation[]): Promise<boolean> {
|
||||
const wrapper = this._mainThreadEditors.get(editorId);
|
||||
if (!wrapper) {
|
||||
return false;
|
||||
}
|
||||
const { editor } = wrapper;
|
||||
if (!editor.textModel) {
|
||||
this._logService.warn('Notebook editor has NO model', editorId);
|
||||
return false;
|
||||
}
|
||||
if (editor.textModel.versionId !== modelVersionId) {
|
||||
return false;
|
||||
}
|
||||
//todo@jrieken use proper selection logic!
|
||||
return editor.textModel.applyEdits(cellEdits, true, undefined, () => undefined, undefined);
|
||||
}
|
||||
|
||||
|
||||
|
||||
async $tryShowNotebookDocument(resource: UriComponents, viewType: string, options: INotebookDocumentShowOptions): Promise<string> {
|
||||
const editorOptions = new NotebookEditorOptions({
|
||||
cellSelections: options.selections,
|
||||
preserveFocus: options.preserveFocus,
|
||||
pinned: options.pinned,
|
||||
// selection: options.selection,
|
||||
// preserve pre 1.38 behaviour to not make group active when preserveFocus: true
|
||||
// but make sure to restore the editor to fix https://github.com/microsoft/vscode/issues/79633
|
||||
activation: options.preserveFocus ? EditorActivation.RESTORE : undefined,
|
||||
override: EditorOverride.DISABLED,
|
||||
});
|
||||
|
||||
const input = NotebookEditorInput.create(this._instantiationService, URI.revive(resource), viewType);
|
||||
const editorPane = await this._editorService.openEditor(input, editorOptions, options.position);
|
||||
const notebookEditor = getNotebookEditorFromEditorPane(editorPane);
|
||||
|
||||
if (notebookEditor) {
|
||||
return notebookEditor.getId();
|
||||
} else {
|
||||
throw new Error(`Notebook Editor creation failure for documenet ${resource}`);
|
||||
}
|
||||
}
|
||||
|
||||
async $tryRevealRange(id: string, range: ICellRange, revealType: NotebookEditorRevealType): Promise<void> {
|
||||
const editor = this._notebookEditorService.getNotebookEditor(id);
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
const notebookEditor = editor as INotebookEditor;
|
||||
if (!notebookEditor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
const viewModel = notebookEditor.viewModel;
|
||||
const cell = viewModel.cellAt(range.start);
|
||||
if (!cell) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (revealType) {
|
||||
case NotebookEditorRevealType.Default:
|
||||
return notebookEditor.revealCellRangeInView(range);
|
||||
case NotebookEditorRevealType.InCenter:
|
||||
return notebookEditor.revealInCenter(cell);
|
||||
case NotebookEditorRevealType.InCenterIfOutsideViewport:
|
||||
return notebookEditor.revealInCenterIfOutsideViewport(cell);
|
||||
case NotebookEditorRevealType.AtTop:
|
||||
return notebookEditor.revealInViewAtTop(cell);
|
||||
}
|
||||
}
|
||||
|
||||
$registerNotebookEditorDecorationType(key: string, options: INotebookDecorationRenderOptions): void {
|
||||
this._notebookEditorService.registerEditorDecorationType(key, options);
|
||||
}
|
||||
|
||||
$removeNotebookEditorDecorationType(key: string): void {
|
||||
this._notebookEditorService.removeEditorDecorationType(key);
|
||||
}
|
||||
|
||||
$trySetDecorations(id: string, range: ICellRange, key: string): void {
|
||||
const editor = this._notebookEditorService.getNotebookEditor(id);
|
||||
if (editor) {
|
||||
const notebookEditor = editor as INotebookEditor;
|
||||
notebookEditor.setEditorDecorations(key, range);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { flatten, isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { combinedDisposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/notebookEditorService';
|
||||
import { INotebookKernel, INotebookKernelChangeEvent } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService';
|
||||
import { ExtHostContext, ExtHostNotebookKernelsShape, IExtHostContext, INotebookKernelDto2, MainContext, MainThreadNotebookKernelsShape } from '../common/extHost.protocol';
|
||||
|
||||
abstract class MainThreadKernel implements INotebookKernel {
|
||||
|
||||
private readonly _onDidChange = new Emitter<INotebookKernelChangeEvent>();
|
||||
private readonly preloads: { uri: URI, provides: string[] }[];
|
||||
readonly onDidChange: Event<INotebookKernelChangeEvent> = this._onDidChange.event;
|
||||
|
||||
readonly id: string;
|
||||
readonly viewType: string;
|
||||
readonly extension: ExtensionIdentifier;
|
||||
|
||||
implementsInterrupt: boolean;
|
||||
label: string;
|
||||
description?: string;
|
||||
detail?: string;
|
||||
supportedLanguages: string[];
|
||||
implementsExecutionOrder: boolean;
|
||||
localResourceRoot: URI;
|
||||
|
||||
public get preloadUris() {
|
||||
return this.preloads.map(p => p.uri);
|
||||
}
|
||||
|
||||
public get preloadProvides() {
|
||||
return flatten(this.preloads.map(p => p.provides));
|
||||
}
|
||||
|
||||
constructor(data: INotebookKernelDto2, private _modeService: IModeService) {
|
||||
this.id = data.id;
|
||||
this.viewType = data.viewType;
|
||||
this.extension = data.extensionId;
|
||||
|
||||
this.implementsInterrupt = data.supportsInterrupt ?? false;
|
||||
this.label = data.label;
|
||||
this.description = data.description;
|
||||
this.detail = data.detail;
|
||||
this.supportedLanguages = isNonEmptyArray(data.supportedLanguages) ? data.supportedLanguages : _modeService.getRegisteredModes();
|
||||
this.implementsExecutionOrder = data.hasExecutionOrder ?? false;
|
||||
this.localResourceRoot = URI.revive(data.extensionLocation);
|
||||
this.preloads = data.preloads?.map(u => ({ uri: URI.revive(u.uri), provides: u.provides })) ?? [];
|
||||
}
|
||||
|
||||
|
||||
update(data: Partial<INotebookKernelDto2>) {
|
||||
|
||||
const event: INotebookKernelChangeEvent = Object.create(null);
|
||||
if (data.label !== undefined) {
|
||||
this.label = data.label;
|
||||
event.label = true;
|
||||
}
|
||||
if (data.description !== undefined) {
|
||||
this.description = data.description;
|
||||
event.description = true;
|
||||
}
|
||||
if (data.detail !== undefined) {
|
||||
this.detail = data.detail;
|
||||
event.detail = true;
|
||||
}
|
||||
if (data.supportedLanguages !== undefined) {
|
||||
this.supportedLanguages = isNonEmptyArray(data.supportedLanguages) ? data.supportedLanguages : this._modeService.getRegisteredModes();
|
||||
event.supportedLanguages = true;
|
||||
}
|
||||
if (data.hasExecutionOrder !== undefined) {
|
||||
this.implementsExecutionOrder = data.hasExecutionOrder;
|
||||
event.hasExecutionOrder = true;
|
||||
}
|
||||
this._onDidChange.fire(event);
|
||||
}
|
||||
|
||||
abstract executeNotebookCellsRequest(uri: URI, cellHandles: number[]): Promise<void>;
|
||||
abstract cancelNotebookCellExecution(uri: URI, cellHandles: number[]): Promise<void>;
|
||||
}
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadNotebookKernels)
|
||||
export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape {
|
||||
|
||||
private readonly _editors = new Map<INotebookEditor, IDisposable>();
|
||||
private readonly _disposables = new DisposableStore();
|
||||
|
||||
private readonly _kernels = new Map<number, [kernel: MainThreadKernel, registraion: IDisposable]>();
|
||||
private readonly _proxy: ExtHostNotebookKernelsShape;
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@IModeService private readonly _modeService: IModeService,
|
||||
@INotebookKernelService private readonly _notebookKernelService: INotebookKernelService,
|
||||
@INotebookEditorService notebookEditorService: INotebookEditorService
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostNotebookKernels);
|
||||
|
||||
notebookEditorService.listNotebookEditors().forEach(this._onEditorAdd, this);
|
||||
notebookEditorService.onDidAddNotebookEditor(this._onEditorAdd, this, this._disposables);
|
||||
notebookEditorService.onDidRemoveNotebookEditor(this._onEditorRemove, this, this._disposables);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._disposables.dispose();
|
||||
for (let [, registration] of this._kernels.values()) {
|
||||
registration.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// --- kernel ipc
|
||||
|
||||
private _onEditorAdd(editor: INotebookEditor) {
|
||||
|
||||
const ipcListener = editor.onDidReceiveMessage(e => {
|
||||
if (e.forRenderer) {
|
||||
return;
|
||||
}
|
||||
if (!editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
const { selected } = this._notebookKernelService.getMatchingKernel(editor.viewModel.notebookDocument);
|
||||
if (!selected) {
|
||||
return;
|
||||
}
|
||||
for (let [handle, candidate] of this._kernels) {
|
||||
if (candidate[0] === selected) {
|
||||
this._proxy.$acceptRendererMessage(handle, editor.getId(), e.message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
this._editors.set(editor, ipcListener);
|
||||
}
|
||||
|
||||
private _onEditorRemove(editor: INotebookEditor) {
|
||||
this._editors.get(editor)?.dispose();
|
||||
this._editors.delete(editor);
|
||||
}
|
||||
|
||||
async $postMessage(handle: number, editorId: string | undefined, message: any): Promise<boolean> {
|
||||
const tuple = this._kernels.get(handle);
|
||||
if (!tuple) {
|
||||
throw new Error('kernel already disposed');
|
||||
}
|
||||
const [kernel] = tuple;
|
||||
let didSend = false;
|
||||
for (const [editor] of this._editors) {
|
||||
if (!editor.hasModel()) {
|
||||
continue;
|
||||
}
|
||||
if (this._notebookKernelService.getMatchingKernel(editor.viewModel.notebookDocument).selected !== kernel) {
|
||||
// different kernel
|
||||
continue;
|
||||
}
|
||||
if (editorId === undefined) {
|
||||
// all editors
|
||||
editor.postMessage(undefined, message);
|
||||
didSend = true;
|
||||
} else if (editor.getId() === editorId) {
|
||||
// selected editors
|
||||
editor.postMessage(undefined, message);
|
||||
didSend = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return didSend;
|
||||
}
|
||||
|
||||
// --- kernel adding/updating/removal
|
||||
|
||||
async $addKernel(handle: number, data: INotebookKernelDto2): Promise<void> {
|
||||
const that = this;
|
||||
const kernel = new class extends MainThreadKernel {
|
||||
async executeNotebookCellsRequest(uri: URI, handles: number[]): Promise<void> {
|
||||
await that._proxy.$executeCells(handle, uri, handles);
|
||||
}
|
||||
async cancelNotebookCellExecution(uri: URI, handles: number[]): Promise<void> {
|
||||
await that._proxy.$cancelCells(handle, uri, handles);
|
||||
}
|
||||
}(data, this._modeService);
|
||||
const registration = this._notebookKernelService.registerKernel(kernel);
|
||||
|
||||
const listener = this._notebookKernelService.onDidChangeNotebookKernelBinding(e => {
|
||||
if (e.oldKernel === kernel.id) {
|
||||
this._proxy.$acceptSelection(handle, e.notebook, false);
|
||||
} else if (e.newKernel === kernel.id) {
|
||||
this._proxy.$acceptSelection(handle, e.notebook, true);
|
||||
}
|
||||
});
|
||||
|
||||
this._kernels.set(handle, [kernel, combinedDisposable(listener, registration)]);
|
||||
}
|
||||
|
||||
$updateKernel(handle: number, data: Partial<INotebookKernelDto2>): void {
|
||||
const tuple = this._kernels.get(handle);
|
||||
if (tuple) {
|
||||
tuple[0].update(data);
|
||||
}
|
||||
}
|
||||
|
||||
$removeKernel(handle: number): void {
|
||||
const tuple = this._kernels.get(handle);
|
||||
if (tuple) {
|
||||
tuple[1].dispose();
|
||||
this._kernels.delete(handle);
|
||||
}
|
||||
}
|
||||
|
||||
$updateNotebookPriority(handle: number, notebook: UriComponents, value: number | undefined): void {
|
||||
const tuple = this._kernels.get(handle);
|
||||
if (tuple) {
|
||||
this._notebookKernelService.updateKernelNotebookAffinity(tuple[0], URI.revive(notebook), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IDisposable, DisposableStore, combinedDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ISCMService, ISCMRepository, ISCMProvider, ISCMResource, ISCMResourceGroup, ISCMResourceDecorations, IInputValidation, ISCMViewService } from 'vs/workbench/contrib/scm/common/scm';
|
||||
import { ISCMService, ISCMRepository, ISCMProvider, ISCMResource, ISCMResourceGroup, ISCMResourceDecorations, IInputValidation, ISCMViewService, InputValidationType } from 'vs/workbench/contrib/scm/common/scm';
|
||||
import { ExtHostContext, MainThreadSCMShape, ExtHostSCMShape, SCMProviderFeatures, SCMRawResourceSplices, SCMGroupFeatures, MainContext, IExtHostContext } from '../common/extHost.protocol';
|
||||
import { Command } from 'vs/editor/common/modes';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
@@ -424,6 +424,24 @@ export class MainThreadSCM implements MainThreadSCMShape {
|
||||
repository.input.visible = visible;
|
||||
}
|
||||
|
||||
$setInputBoxFocus(sourceControlHandle: number): void {
|
||||
const repository = this._repositories.get(sourceControlHandle);
|
||||
if (!repository) {
|
||||
return;
|
||||
}
|
||||
|
||||
repository.input.setFocus();
|
||||
}
|
||||
|
||||
$showValidationMessage(sourceControlHandle: number, message: string, type: InputValidationType) {
|
||||
const repository = this._repositories.get(sourceControlHandle);
|
||||
if (!repository) {
|
||||
return;
|
||||
}
|
||||
|
||||
repository.input.showValidationMessage(message, type);
|
||||
}
|
||||
|
||||
$setValidationProviderIsEnabled(sourceControlHandle: number, enabled: boolean): void {
|
||||
const repository = this._repositories.get(sourceControlHandle);
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@ class RemoteSearchProvider implements ISearchResultProvider, IDisposable {
|
||||
|
||||
return Promise.resolve(searchP).then((result: ISearchCompleteStats) => {
|
||||
this._searches.delete(search.id);
|
||||
return { results: Array.from(search.matches.values()), stats: result.stats, limitHit: result.limitHit };
|
||||
return { results: Array.from(search.matches.values()), stats: result.stats, limitHit: result.limitHit, messages: result.messages };
|
||||
}, err => {
|
||||
this._searches.delete(search.id);
|
||||
return Promise.reject(err);
|
||||
|
||||
@@ -10,12 +10,12 @@ import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { dispose } from 'vs/base/common/lifecycle';
|
||||
import { Command } from 'vs/editor/common/modes';
|
||||
import { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { getCodiconAriaLabel } from 'vs/base/common/codicons';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadStatusBar)
|
||||
export class MainThreadStatusBar implements MainThreadStatusBarShape {
|
||||
|
||||
private readonly entries: Map<number, { accessor: IStatusbarEntryAccessor, alignment: MainThreadStatusBarAlignment, priority: number }> = new Map();
|
||||
private static readonly CODICON_REGEXP = /\$\((.*?)\)/g;
|
||||
|
||||
constructor(
|
||||
_extHostContext: IExtHostContext,
|
||||
@@ -35,7 +35,7 @@ export class MainThreadStatusBar implements MainThreadStatusBarShape {
|
||||
ariaLabel = accessibilityInformation.label;
|
||||
role = accessibilityInformation.role;
|
||||
} else {
|
||||
ariaLabel = text ? text.replace(MainThreadStatusBar.CODICON_REGEXP, (_match, codiconName) => codiconName) : '';
|
||||
ariaLabel = getCodiconAriaLabel(text);
|
||||
}
|
||||
const entry: IStatusbarEntry = { text, tooltip, command, color, backgroundColor, ariaLabel, role };
|
||||
|
||||
|
||||
@@ -422,7 +422,7 @@ export class MainThreadTask implements MainThreadTaskShape {
|
||||
if (execution.task?.execution && CustomExecutionDTO.is(execution.task.execution) && event.resolvedVariables) {
|
||||
const dictionary: IStringDictionary<string> = {};
|
||||
Array.from(event.resolvedVariables.entries()).forEach(entry => dictionary[entry[0]] = entry[1]);
|
||||
resolvedDefinition = await this._configurationResolverService.resolveAny(task.getWorkspaceFolder(),
|
||||
resolvedDefinition = await this._configurationResolverService.resolveAnyAsync(task.getWorkspaceFolder(),
|
||||
execution.task.definition, dictionary);
|
||||
}
|
||||
this._proxy.$onDidStartTask(execution, event.terminalId!, resolvedDefinition);
|
||||
|
||||
@@ -8,11 +8,12 @@ import { StopWatch } from 'vs/base/common/stopwatch';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IShellLaunchConfig, ITerminalDimensions } from 'vs/platform/terminal/common/terminal';
|
||||
import { IShellLaunchConfig, IShellLaunchConfigDto, ITerminalDimensions } from 'vs/platform/terminal/common/terminal';
|
||||
import { TerminalDataBufferer } from 'vs/platform/terminal/common/terminalDataBuffering';
|
||||
import { ExtHostContext, ExtHostTerminalServiceShape, IExtHostContext, IShellLaunchConfigDto, ITerminalDimensionsDto, MainContext, MainThreadTerminalServiceShape, TerminalIdentifier, TerminalLaunchConfig } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostContext, ExtHostTerminalServiceShape, IExtHostContext, ITerminalDimensionsDto, MainContext, MainThreadTerminalServiceShape, TerminalIdentifier, TerminalLaunchConfig } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { ITerminalExternalLinkProvider, ITerminalInstance, ITerminalInstanceService, ITerminalLink, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { TerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy';
|
||||
import { IEnvironmentVariableService, ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
import { deserializeEnvironmentVariableCollection, serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableShared';
|
||||
import { IAvailableProfilesRequest as IAvailableProfilesRequest, IDefaultShellAndArgsRequest, IStartExtensionTerminalRequest, ITerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
@@ -69,7 +70,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
this._toDispose.add(_terminalService.onInstanceRequestStartExtensionTerminal(e => this._onRequestStartExtensionTerminal(e)));
|
||||
this._toDispose.add(_terminalService.onActiveInstanceChanged(instance => this._onActiveTerminalChanged(instance ? instance.instanceId : null)));
|
||||
this._toDispose.add(_terminalService.onInstanceTitleChanged(instance => instance && this._onTitleChanged(instance.instanceId, instance.title)));
|
||||
this._toDispose.add(_terminalService.configHelper.onWorkspacePermissionsChanged(isAllowed => this._onWorkspacePermissionsChanged(isAllowed)));
|
||||
this._toDispose.add(_terminalService.onRequestAvailableProfiles(e => this._onRequestAvailableProfiles(e)));
|
||||
|
||||
// ITerminalInstanceService listeners
|
||||
@@ -100,9 +100,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
public dispose(): void {
|
||||
this._toDispose.dispose();
|
||||
this._linkProvider?.dispose();
|
||||
|
||||
// TODO@Daniel: Should all the previously created terminals be disposed
|
||||
// when the extension host process goes down ?
|
||||
}
|
||||
|
||||
private _getTerminalId(id: TerminalIdentifier): number | undefined {
|
||||
@@ -126,12 +123,16 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
executable: launchConfig.shellPath,
|
||||
args: launchConfig.shellArgs,
|
||||
cwd: typeof launchConfig.cwd === 'string' ? launchConfig.cwd : URI.revive(launchConfig.cwd),
|
||||
icon: launchConfig.icon,
|
||||
initialText: launchConfig.initialText,
|
||||
waitOnExit: launchConfig.waitOnExit,
|
||||
ignoreConfigurationCwd: true,
|
||||
env: launchConfig.env,
|
||||
strictEnv: launchConfig.strictEnv,
|
||||
hideFromUser: launchConfig.hideFromUser,
|
||||
isExtensionCustomPtyTerminal: launchConfig.isExtensionCustomPtyTerminal,
|
||||
customPtyImplementation: launchConfig.isExtensionCustomPtyTerminal
|
||||
? (id, cols, rows) => new TerminalProcessExtHostProxy(id, cols, rows, this._terminalService)
|
||||
: undefined,
|
||||
extHostTerminalId: extHostTerminalId,
|
||||
isFeatureTerminal: launchConfig.isFeatureTerminal,
|
||||
isExtensionOwnedTerminal: launchConfig.isExtensionOwnedTerminal
|
||||
@@ -157,17 +158,11 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
}
|
||||
|
||||
public $dispose(id: TerminalIdentifier): void {
|
||||
const terminalInstance = this._getTerminalInstance(id);
|
||||
if (terminalInstance) {
|
||||
terminalInstance.dispose();
|
||||
}
|
||||
this._getTerminalInstance(id)?.dispose();
|
||||
}
|
||||
|
||||
public $sendText(id: TerminalIdentifier, text: string, addNewLine: boolean): void {
|
||||
const terminalInstance = this._getTerminalInstance(id);
|
||||
if (terminalInstance) {
|
||||
terminalInstance.sendText(text, addNewLine);
|
||||
}
|
||||
this._getTerminalInstance(id)?.sendText(text, addNewLine);
|
||||
}
|
||||
|
||||
public $startSendingDataEvents(): void {
|
||||
@@ -183,10 +178,8 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
}
|
||||
|
||||
public $stopSendingDataEvents(): void {
|
||||
if (this._dataEventTracker) {
|
||||
this._dataEventTracker.dispose();
|
||||
this._dataEventTracker = undefined;
|
||||
}
|
||||
this._dataEventTracker?.dispose();
|
||||
this._dataEventTracker = undefined;
|
||||
}
|
||||
|
||||
public $startLinkProvider(): void {
|
||||
@@ -215,10 +208,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
this._proxy.$acceptTerminalTitleChange(terminalId, name);
|
||||
}
|
||||
|
||||
private _onWorkspacePermissionsChanged(isAllowed: boolean): void {
|
||||
this._proxy.$acceptWorkspacePermissionsChanged(isAllowed);
|
||||
}
|
||||
|
||||
private _onTerminalDisposed(terminalInstance: ITerminalInstance): void {
|
||||
this._proxy.$acceptTerminalClosed(terminalInstance.instanceId, terminalInstance.exitCode);
|
||||
}
|
||||
@@ -276,60 +265,35 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
}
|
||||
|
||||
public $sendProcessTitle(terminalId: number, title: string): void {
|
||||
const terminalProcess = this._terminalProcessProxies.get(terminalId);
|
||||
if (terminalProcess) {
|
||||
terminalProcess.emitTitle(title);
|
||||
}
|
||||
this._terminalProcessProxies.get(terminalId)?.emitTitle(title);
|
||||
}
|
||||
|
||||
public $sendProcessData(terminalId: number, data: string): void {
|
||||
const terminalProcess = this._terminalProcessProxies.get(terminalId);
|
||||
if (terminalProcess) {
|
||||
terminalProcess.emitData(data);
|
||||
}
|
||||
this._terminalProcessProxies.get(terminalId)?.emitData(data);
|
||||
}
|
||||
|
||||
public $sendProcessReady(terminalId: number, pid: number, cwd: string): void {
|
||||
const terminalProcess = this._terminalProcessProxies.get(terminalId);
|
||||
if (terminalProcess) {
|
||||
terminalProcess.emitReady(pid, cwd);
|
||||
}
|
||||
this._terminalProcessProxies.get(terminalId)?.emitReady(pid, cwd);
|
||||
}
|
||||
|
||||
public $sendProcessExit(terminalId: number, exitCode: number | undefined): void {
|
||||
const terminalProcess = this._terminalProcessProxies.get(terminalId);
|
||||
if (terminalProcess) {
|
||||
terminalProcess.emitExit(exitCode);
|
||||
this._terminalProcessProxies.delete(terminalId);
|
||||
}
|
||||
this._terminalProcessProxies.get(terminalId)?.emitExit(exitCode);
|
||||
}
|
||||
|
||||
public $sendOverrideDimensions(terminalId: number, dimensions: ITerminalDimensions | undefined): void {
|
||||
const terminalProcess = this._terminalProcessProxies.get(terminalId);
|
||||
if (terminalProcess) {
|
||||
terminalProcess.emitOverrideDimensions(dimensions);
|
||||
}
|
||||
this._terminalProcessProxies.get(terminalId)?.emitOverrideDimensions(dimensions);
|
||||
}
|
||||
|
||||
public $sendProcessInitialCwd(terminalId: number, initialCwd: string): void {
|
||||
const terminalProcess = this._terminalProcessProxies.get(terminalId);
|
||||
if (terminalProcess) {
|
||||
terminalProcess.emitInitialCwd(initialCwd);
|
||||
}
|
||||
this._terminalProcessProxies.get(terminalId)?.emitInitialCwd(initialCwd);
|
||||
}
|
||||
|
||||
public $sendProcessCwd(terminalId: number, cwd: string): void {
|
||||
const terminalProcess = this._terminalProcessProxies.get(terminalId);
|
||||
if (terminalProcess) {
|
||||
terminalProcess.emitCwd(cwd);
|
||||
}
|
||||
this._terminalProcessProxies.get(terminalId)?.emitCwd(cwd);
|
||||
}
|
||||
|
||||
public $sendResolvedLaunchConfig(terminalId: number, shellLaunchConfig: IShellLaunchConfig): void {
|
||||
const instance = this._terminalService.getInstanceFromId(terminalId);
|
||||
if (instance) {
|
||||
this._getTerminalProcess(terminalId)?.emitResolvedShellLaunchConfig(shellLaunchConfig);
|
||||
}
|
||||
this._getTerminalProcess(terminalId)?.emitResolvedShellLaunchConfig(shellLaunchConfig);
|
||||
}
|
||||
|
||||
private async _onRequestLatency(terminalId: number): Promise<void> {
|
||||
@@ -350,12 +314,12 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
if (conn) {
|
||||
return this._remoteAuthority === conn.remoteAuthority;
|
||||
}
|
||||
return true;
|
||||
return this._extHostKind !== ExtensionHostKind.LocalWebWorker;
|
||||
}
|
||||
|
||||
private async _onRequestAvailableProfiles(req: IAvailableProfilesRequest): Promise<void> {
|
||||
if (this._isPrimaryExtHost() && this._extHostKind !== ExtensionHostKind.LocalWebWorker) {
|
||||
req.callback(await this._proxy.$getAvailableProfiles(req.quickLaunchOnly));
|
||||
if (this._isPrimaryExtHost()) {
|
||||
req.callback(await this._proxy.$getAvailableProfiles(req.configuredProfilesOnly));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,14 +3,17 @@
|
||||
* 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 { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { isDefined } from 'vs/base/common/types';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { getTestSubscriptionKey, ISerializedTestResults, ITestState, RunTestsRequest, TestDiffOpType, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { HydratedTestResult, ITestResultService, LiveTestResult } from 'vs/workbench/contrib/testing/common/testResultService';
|
||||
import { TestResultState } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { ExtensionRunTestsRequest, getTestSubscriptionKey, ITestItem, ITestMessage, ITestRunTask, RunTestsRequest, TestDiffOpType, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { LiveTestResult } from 'vs/workbench/contrib/testing/common/testResult';
|
||||
import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService';
|
||||
import { ITestRootProvider, ITestService } from 'vs/workbench/contrib/testing/common/testService';
|
||||
import { ExtHostContext, ExtHostTestingResource, ExtHostTestingShape, IExtHostContext, MainContext, MainThreadTestingShape } from '../common/extHost.protocol';
|
||||
|
||||
@@ -44,7 +47,6 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh
|
||||
this._register(this.testService.onShouldSubscribe(args => this.proxy.$subscribeToTests(args.resource, args.uri)));
|
||||
this._register(this.testService.onShouldUnsubscribe(args => this.proxy.$unsubscribeFromTests(args.resource, args.uri)));
|
||||
|
||||
|
||||
const prevResults = resultService.results.map(r => r.toJSON()).filter(isDefined);
|
||||
if (prevResults.length) {
|
||||
this.proxy.$publishTestResults(prevResults);
|
||||
@@ -68,31 +70,79 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $publishExtensionProvidedResults(results: ISerializedTestResults, persist: boolean): void {
|
||||
this.resultService.push(new HydratedTestResult(results, persist));
|
||||
$addTestsToRun(runId: string, tests: ITestItem[]): void {
|
||||
for (const test of tests) {
|
||||
test.uri = URI.revive(test.uri);
|
||||
if (test.range) {
|
||||
test.range = Range.lift(test.range);
|
||||
}
|
||||
}
|
||||
|
||||
this.withLiveRun(runId, r => r.addTestChainToRun(tests));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $updateTestStateInRun(runId: string, testId: string, state: ITestState): void {
|
||||
$startedExtensionTestRun(req: ExtensionRunTestsRequest): void {
|
||||
this.resultService.createLiveResult(req);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
$startedTestRunTask(runId: string, task: ITestRunTask): void {
|
||||
this.withLiveRun(runId, r => r.addTask(task));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
$finishedTestRunTask(runId: string, taskId: string): void {
|
||||
this.withLiveRun(runId, r => r.markTaskComplete(taskId));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
$finishedExtensionTestRun(runId: string): void {
|
||||
this.withLiveRun(runId, r => r.markComplete());
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $updateTestStateInRun(runId: string, taskId: string, testId: string, state: TestResultState, duration?: number): void {
|
||||
this.withLiveRun(runId, r => r.updateState(testId, taskId, state, duration));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $appendOutputToRun(runId: string, _taskId: string, output: VSBuffer): void {
|
||||
this.withLiveRun(runId, r => r.output.append(output));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $appendTestMessageInRun(runId: string, taskId: string, testId: string, message: ITestMessage): void {
|
||||
const r = this.resultService.getResult(runId);
|
||||
if (r && r instanceof LiveTestResult) {
|
||||
for (const message of state.messages) {
|
||||
if (message.location) {
|
||||
message.location.uri = URI.revive(message.location.uri);
|
||||
message.location.range = Range.lift(message.location.range);
|
||||
}
|
||||
if (message.location) {
|
||||
message.location.uri = URI.revive(message.location.uri);
|
||||
message.location.range = Range.lift(message.location.range);
|
||||
}
|
||||
|
||||
r.updateState(testId, state);
|
||||
r.appendMessage(testId, taskId, message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $registerTestProvider(id: string) {
|
||||
public $registerTestController(id: string) {
|
||||
const disposable = this.testService.registerTestController(id, {
|
||||
runTests: (req, token) => this.proxy.$runTestsForProvider(req, token),
|
||||
lookupTest: test => this.proxy.$lookupTest(test),
|
||||
@@ -105,7 +155,7 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public $unregisterTestProvider(id: string) {
|
||||
public $unregisterTestController(id: string) {
|
||||
this.testProviderRegistrations.get(id)?.dispose();
|
||||
this.testProviderRegistrations.delete(id);
|
||||
}
|
||||
@@ -142,11 +192,16 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh
|
||||
return result.id;
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
public override dispose() {
|
||||
super.dispose();
|
||||
for (const subscription of this.testSubscriptions.values()) {
|
||||
subscription.dispose();
|
||||
}
|
||||
this.testSubscriptions.clear();
|
||||
}
|
||||
|
||||
private withLiveRun<T>(runId: string, fn: (run: LiveTestResult) => T): T | undefined {
|
||||
const r = this.resultService.getResult(runId);
|
||||
return r && r instanceof LiveTestResult ? fn(r) : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
|
||||
return viewDescriptor ? viewDescriptor.treeView : null;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
override dispose(): void {
|
||||
this._dataProviders.forEach((dataProvider, treeViewId) => {
|
||||
const treeView = this.getTreeView(treeViewId);
|
||||
if (treeView) {
|
||||
|
||||
@@ -12,10 +12,12 @@ import { ITunnelProvider, ITunnelService, TunnelCreationOptions, TunnelProviderF
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import type { TunnelDescription } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadTunnelService)
|
||||
export class MainThreadTunnelService extends Disposable implements MainThreadTunnelServiceShape, PortAttributesProvider {
|
||||
@@ -80,7 +82,8 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun
|
||||
const portRange = selector.portRange;
|
||||
const portInRange = portRange ? ports.some(port => portRange[0] <= port && port < portRange[1]) : true;
|
||||
const pidMatches = !selector.pid || (selector.pid === pid);
|
||||
return portInRange || pidMatches;
|
||||
const commandMatches = !selector.commandMatcher || (commandLine && (commandLine.match(selector.commandMatcher)));
|
||||
return portInRange && pidMatches && commandMatches;
|
||||
}).map(entry => entry[0]);
|
||||
|
||||
if (appropriateHandles.length === 0) {
|
||||
@@ -174,19 +177,13 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun
|
||||
this.remoteAgentService.getEnvironment().then(() => {
|
||||
switch (source) {
|
||||
case CandidatePortSource.None: {
|
||||
const autoDetectionEnablement = this.configurationService.inspect(PORT_AUTO_FORWARD_SETTING);
|
||||
if (autoDetectionEnablement.userRemote === undefined) {
|
||||
// Only update the remote setting if the user hasn't already set it.
|
||||
this.configurationService.updateValue(PORT_AUTO_FORWARD_SETTING, false, ConfigurationTarget.USER_REMOTE);
|
||||
}
|
||||
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
|
||||
.registerDefaultConfigurations([{ 'remote.autoForwardPorts': false }]);
|
||||
break;
|
||||
}
|
||||
case CandidatePortSource.Output: {
|
||||
const candidatePortSourceSetting = this.configurationService.inspect(PORT_AUTO_SOURCE_SETTING);
|
||||
if (candidatePortSourceSetting.userRemote === undefined) {
|
||||
// Only update the remote setting if the user hasn't already set it.
|
||||
this.configurationService.updateValue(PORT_AUTO_SOURCE_SETTING, PORT_AUTO_SOURCE_SETTING_OUTPUT, ConfigurationTarget.USER_REMOTE);
|
||||
}
|
||||
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
|
||||
.registerDefaultConfigurations([{ 'remote.autoForwardPortsSource': PORT_AUTO_SOURCE_SETTING_OUTPUT }]);
|
||||
break;
|
||||
}
|
||||
default: // Do nothing, the defaults for these settings should be used.
|
||||
@@ -195,8 +192,4 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun
|
||||
// The remote failed to get setup. Errors from that area will already be surfaced to the user.
|
||||
});
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,8 +77,8 @@ export class MainThreadUriOpeners extends Disposable implements MainThreadUriOpe
|
||||
await this.proxy.$openUri(id, { resolvedUri: uri, sourceUri: ctx.sourceUri }, token);
|
||||
} catch (e) {
|
||||
if (!isPromiseCanceledError(e)) {
|
||||
const openDefaultAction = new Action('default', localize('openerFailedUseDefault', "Open using default opener"), undefined, undefined, () => {
|
||||
return this.openerService.open(uri, {
|
||||
const openDefaultAction = new Action('default', localize('openerFailedUseDefault', "Open using default opener"), undefined, undefined, async () => {
|
||||
await this.openerService.open(uri, {
|
||||
allowTunneling: false,
|
||||
allowContributedOpeners: defaultExternalUriOpenerId,
|
||||
});
|
||||
@@ -128,7 +128,7 @@ export class MainThreadUriOpeners extends Disposable implements MainThreadUriOpe
|
||||
this._contributedExternalUriOpenersStore.delete(id);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
override dispose(): void {
|
||||
super.dispose();
|
||||
this._registeredOpeners.clear();
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { Disposable, DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { MainThreadWebviews, reviveWebviewContentOptions, reviveWebviewExtension } from 'vs/workbench/api/browser/mainThreadWebviews';
|
||||
@@ -81,7 +81,6 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
||||
private readonly _webviewInputs = new WebviewInputStore();
|
||||
|
||||
private readonly _editorProviders = new Map<string, IDisposable>();
|
||||
private readonly _webviewFromDiffEditorHandles = new Set<string>();
|
||||
|
||||
private readonly _revivers = new Map<string, IDisposable>();
|
||||
|
||||
@@ -99,18 +98,17 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
||||
this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewPanels);
|
||||
|
||||
this._register(_editorService.onDidActiveEditorChange(() => {
|
||||
const activeInput = this._editorService.activeEditor;
|
||||
if (activeInput instanceof DiffEditorInput && activeInput.primary instanceof WebviewInput && activeInput.secondary instanceof WebviewInput) {
|
||||
this.registerWebviewFromDiffEditorListeners(activeInput);
|
||||
}
|
||||
|
||||
this.updateWebviewViewStates(activeInput);
|
||||
this.updateWebviewViewStates(this._editorService.activeEditor);
|
||||
}));
|
||||
|
||||
this._register(_editorService.onDidVisibleEditorsChange(() => {
|
||||
this.updateWebviewViewStates(this._editorService.activeEditor);
|
||||
}));
|
||||
|
||||
this._register(_webviewWorkbenchService.onDidChangeActiveWebviewEditor(input => {
|
||||
this.updateWebviewViewStates(input);
|
||||
}));
|
||||
|
||||
// This reviver's only job is to activate extensions.
|
||||
// This should trigger the real reviver to be registered from the extension host side.
|
||||
this._register(_webviewWorkbenchService.registerResolver({
|
||||
@@ -125,7 +123,7 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
||||
}));
|
||||
}
|
||||
|
||||
dispose() {
|
||||
override dispose() {
|
||||
super.dispose();
|
||||
|
||||
dispose(this._editorProviders.values());
|
||||
@@ -137,9 +135,9 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
||||
|
||||
public get webviewInputs(): Iterable<WebviewInput> { return this._webviewInputs; }
|
||||
|
||||
public addWebviewInput(handle: extHostProtocol.WebviewHandle, input: WebviewInput): void {
|
||||
public addWebviewInput(handle: extHostProtocol.WebviewHandle, input: WebviewInput, options: { serializeBuffersForPostMessage: boolean }): void {
|
||||
this._webviewInputs.add(handle, input);
|
||||
this._mainThreadWebviews.addWebview(handle, input.webview);
|
||||
this._mainThreadWebviews.addWebview(handle, input.webview, options);
|
||||
|
||||
input.webview.onDidDispose(() => {
|
||||
this._proxy.$onDidDisposeWebviewPanel(handle).finally(() => {
|
||||
@@ -156,6 +154,7 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
||||
title: string;
|
||||
webviewOptions: extHostProtocol.IWebviewOptions;
|
||||
panelOptions: extHostProtocol.IWebviewPanelOptions;
|
||||
serializeBuffersForPostMessage: boolean;
|
||||
},
|
||||
showOptions: { viewColumn?: EditorGroupColumn, preserveFocus?: boolean; },
|
||||
): void {
|
||||
@@ -168,7 +167,7 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
||||
const extension = reviveWebviewExtension(extensionData);
|
||||
|
||||
const webview = this._webviewWorkbenchService.createWebview(handle, this.webviewPanelViewType.fromExternal(viewType), initData.title, mainThreadShowOptions, reviveWebviewOptions(initData.panelOptions), reviveWebviewContentOptions(initData.webviewOptions), extension);
|
||||
this.addWebviewInput(handle, webview);
|
||||
this.addWebviewInput(handle, webview, { serializeBuffersForPostMessage: initData.serializeBuffersForPostMessage });
|
||||
|
||||
/* __GDPR__
|
||||
"webviews:createWebviewPanel" : {
|
||||
@@ -205,7 +204,7 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
||||
}
|
||||
}
|
||||
|
||||
public $registerSerializer(viewType: string): void {
|
||||
public $registerSerializer(viewType: string, options: { serializeBuffersForPostMessage: boolean }): void {
|
||||
if (this._revivers.has(viewType)) {
|
||||
throw new Error(`Reviver for ${viewType} already registered`);
|
||||
}
|
||||
@@ -223,7 +222,7 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
||||
|
||||
const handle = webviewInput.id;
|
||||
|
||||
this.addWebviewInput(handle, webviewInput);
|
||||
this.addWebviewInput(handle, webviewInput, options);
|
||||
|
||||
let state = undefined;
|
||||
if (webviewInput.webview.state) {
|
||||
@@ -259,27 +258,6 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
||||
this._revivers.delete(viewType);
|
||||
}
|
||||
|
||||
private registerWebviewFromDiffEditorListeners(diffEditorInput: DiffEditorInput): void {
|
||||
const primary = diffEditorInput.primary as WebviewInput;
|
||||
const secondary = diffEditorInput.secondary as WebviewInput;
|
||||
|
||||
if (this._webviewFromDiffEditorHandles.has(primary.id) || this._webviewFromDiffEditorHandles.has(secondary.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._webviewFromDiffEditorHandles.add(primary.id);
|
||||
this._webviewFromDiffEditorHandles.add(secondary.id);
|
||||
|
||||
const disposables = new DisposableStore();
|
||||
disposables.add(primary.webview.onDidFocus(() => this.updateWebviewViewStates(primary)));
|
||||
disposables.add(secondary.webview.onDidFocus(() => this.updateWebviewViewStates(secondary)));
|
||||
disposables.add(diffEditorInput.onDispose(() => {
|
||||
this._webviewFromDiffEditorHandles.delete(primary.id);
|
||||
this._webviewFromDiffEditorHandles.delete(secondary.id);
|
||||
dispose(disposables);
|
||||
}));
|
||||
}
|
||||
|
||||
private updateWebviewViewStates(activeEditorInput: IEditorInput | undefined) {
|
||||
if (!this._webviewInputs.size) {
|
||||
return;
|
||||
|
||||
@@ -28,7 +28,7 @@ export class MainThreadWebviewsViews extends Disposable implements extHostProtoc
|
||||
this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewViews);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
override dispose() {
|
||||
super.dispose();
|
||||
|
||||
dispose(this._webviewViewProviders.values());
|
||||
@@ -55,7 +55,7 @@ export class MainThreadWebviewsViews extends Disposable implements extHostProtoc
|
||||
public $registerWebviewViewProvider(
|
||||
extensionData: extHostProtocol.WebviewExtensionDescription,
|
||||
viewType: string,
|
||||
options?: { retainContextWhenHidden?: boolean }
|
||||
options: { retainContextWhenHidden?: boolean, serializeBuffersForPostMessage: boolean }
|
||||
): void {
|
||||
if (this._webviewViewProviders.has(viewType)) {
|
||||
throw new Error(`View provider for ${viewType} already registered`);
|
||||
@@ -68,7 +68,7 @@ export class MainThreadWebviewsViews extends Disposable implements extHostProtoc
|
||||
const handle = webviewView.webview.id;
|
||||
|
||||
this._webviewViews.set(handle, webviewView);
|
||||
this.mainThreadWebviews.addWebview(handle, webviewView.webview);
|
||||
this.mainThreadWebviews.addWebview(handle, webviewView.webview, { serializeBuffersForPostMessage: options.serializeBuffersForPostMessage });
|
||||
|
||||
let state = undefined;
|
||||
if (webviewView.webview.state) {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { isWeb } from 'vs/base/common/platform';
|
||||
@@ -13,6 +14,8 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { serializeMessage } from 'vs/workbench/api/common/extHostWebview';
|
||||
import { deserializeWebviewMessage } from 'vs/workbench/api/common/extHostWebviewMessaging';
|
||||
import { Webview, WebviewContentOptions, WebviewExtensionDescription, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
|
||||
export class MainThreadWebviews extends Disposable implements extHostProtocol.MainThreadWebviewsShape {
|
||||
@@ -39,13 +42,13 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviews);
|
||||
}
|
||||
|
||||
public addWebview(handle: extHostProtocol.WebviewHandle, webview: WebviewOverlay): void {
|
||||
public addWebview(handle: extHostProtocol.WebviewHandle, webview: WebviewOverlay, options: { serializeBuffersForPostMessage: boolean }): void {
|
||||
if (this._webviews.has(handle)) {
|
||||
throw new Error('Webview already registered');
|
||||
}
|
||||
|
||||
this._webviews.set(handle, webview);
|
||||
this.hookupWebviewEventDelegate(handle, webview);
|
||||
this.hookupWebviewEventDelegate(handle, webview, options);
|
||||
}
|
||||
|
||||
public $setHtml(handle: extHostProtocol.WebviewHandle, value: string): void {
|
||||
@@ -58,17 +61,23 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
webview.contentOptions = reviveWebviewContentOptions(options);
|
||||
}
|
||||
|
||||
public async $postMessage(handle: extHostProtocol.WebviewHandle, message: any): Promise<boolean> {
|
||||
public async $postMessage(handle: extHostProtocol.WebviewHandle, jsonMessage: string, ...buffers: VSBuffer[]): Promise<boolean> {
|
||||
const webview = this.getWebview(handle);
|
||||
webview.postMessage(message);
|
||||
const { message, arrayBuffers } = deserializeWebviewMessage(jsonMessage, buffers);
|
||||
webview.postMessage(message, arrayBuffers);
|
||||
return true;
|
||||
}
|
||||
|
||||
private hookupWebviewEventDelegate(handle: extHostProtocol.WebviewHandle, webview: WebviewOverlay) {
|
||||
private hookupWebviewEventDelegate(handle: extHostProtocol.WebviewHandle, webview: WebviewOverlay, options: { serializeBuffersForPostMessage: boolean }) {
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
disposables.add(webview.onDidClickLink((uri) => this.onDidClickLink(handle, uri)));
|
||||
disposables.add(webview.onMessage((message: any) => { this._proxy.$onMessage(handle, message); }));
|
||||
|
||||
disposables.add(webview.onMessage((message) => {
|
||||
const serialized = serializeMessage(message.message, options);
|
||||
this._proxy.$onMessage(handle, serialized.message, ...serialized.buffers);
|
||||
}));
|
||||
|
||||
disposables.add(webview.onMissingCsp((extension: ExtensionIdentifier) => this._proxy.$onMissingCsp(handle, extension.value)));
|
||||
|
||||
disposables.add(webview.onDidDispose(() => {
|
||||
@@ -80,7 +89,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
private onDidClickLink(handle: extHostProtocol.WebviewHandle, link: string): void {
|
||||
const webview = this.getWebview(handle);
|
||||
if (this.isSupportedLink(webview, URI.parse(link))) {
|
||||
this._openerService.open(link, { fromUserGesture: true, allowContributedOpeners: true });
|
||||
this._openerService.open(link, { fromUserGesture: true, allowContributedOpeners: true, allowCommands: true });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IRequestService } from 'vs/platform/request/common/request';
|
||||
import { WorkspaceTrustStateChangeEvent, IWorkspaceTrustService, WorkspaceTrustRequestOptions, WorkspaceTrustState } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { WorkspaceTrustRequestOptions, IWorkspaceTrustManagementService, IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { IWorkspace, IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { isUntitledWorkspace } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
@@ -47,20 +47,21 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||
@ILabelService private readonly _labelService: ILabelService,
|
||||
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
|
||||
@IFileService fileService: IFileService,
|
||||
@IWorkspaceTrustService private readonly _workspaceTrustService: IWorkspaceTrustService
|
||||
@IWorkspaceTrustManagementService private readonly _workspaceTrustManagementService: IWorkspaceTrustManagementService,
|
||||
@IWorkspaceTrustRequestService private readonly _workspaceTrustRequestService: IWorkspaceTrustRequestService
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostWorkspace);
|
||||
const workspace = this._contextService.getWorkspace();
|
||||
// The workspace file is provided be a unknown file system provider. It might come
|
||||
// from the extension host. So initialize now knowing that `rootPath` is undefined.
|
||||
if (workspace.configuration && !isNative && !fileService.canHandleResource(workspace.configuration)) {
|
||||
this._proxy.$initializeWorkspace(this.getWorkspaceData(workspace), this.getWorkspaceTrustState());
|
||||
this._proxy.$initializeWorkspace(this.getWorkspaceData(workspace), this.isWorkspaceTrusted());
|
||||
} else {
|
||||
this._contextService.getCompleteWorkspace().then(workspace => this._proxy.$initializeWorkspace(this.getWorkspaceData(workspace), this.getWorkspaceTrustState()));
|
||||
this._contextService.getCompleteWorkspace().then(workspace => this._proxy.$initializeWorkspace(this.getWorkspaceData(workspace), this.isWorkspaceTrusted()));
|
||||
}
|
||||
this._contextService.onDidChangeWorkspaceFolders(this._onDidChangeWorkspace, this, this._toDispose);
|
||||
this._contextService.onDidChangeWorkbenchState(this._onDidChangeWorkspace, this, this._toDispose);
|
||||
this._workspaceTrustService.onDidChangeTrustState(this._onDidChangeWorkspaceTrustState, this, this._toDispose);
|
||||
this._workspaceTrustManagementService.onDidChangeTrust(this._onDidGrantWorkspaceTrust, this, this._toDispose);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@@ -208,15 +209,15 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||
|
||||
// --- trust ---
|
||||
|
||||
$requireWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Promise<WorkspaceTrustState> {
|
||||
return this._workspaceTrustService.requireWorkspaceTrust(options);
|
||||
$requestWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Promise<boolean | undefined> {
|
||||
return this._workspaceTrustRequestService.requestWorkspaceTrust(options);
|
||||
}
|
||||
|
||||
private getWorkspaceTrustState(): WorkspaceTrustState {
|
||||
return this._workspaceTrustService.getWorkspaceTrustState();
|
||||
private isWorkspaceTrusted(): boolean {
|
||||
return this._workspaceTrustManagementService.isWorkpaceTrusted();
|
||||
}
|
||||
|
||||
private _onDidChangeWorkspaceTrustState(state: WorkspaceTrustStateChangeEvent): void {
|
||||
this._proxy.$onDidChangeWorkspaceTrustState(state);
|
||||
private _onDidGrantWorkspaceTrust(): void {
|
||||
this._proxy.$onDidGrantWorkspaceTrust();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ const viewDescriptor: IJSONSchema = {
|
||||
type: 'string'
|
||||
},
|
||||
contextualTitle: {
|
||||
description: localize('vscode.extension.contributes.view.contextualTitle', "Human-readable context for when the view is moved out of its original location. By default, the view's container name will be used. Will be shown"),
|
||||
description: localize('vscode.extension.contributes.view.contextualTitle', "Human-readable context for when the view is moved out of its original location. By default, the view's container name will be used."),
|
||||
type: 'string'
|
||||
},
|
||||
visibility: {
|
||||
@@ -304,8 +304,8 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
|
||||
const removedExtensions: Set<string> = extensionPoints.reduce((result, e) => { result.add(ExtensionIdentifier.toKey(e.description.identifier)); return result; }, new Set<string>());
|
||||
for (const viewContainer of viewContainersRegistry.all) {
|
||||
if (viewContainer.extensionId && removedExtensions.has(ExtensionIdentifier.toKey(viewContainer.extensionId))) {
|
||||
// move only those views that do not belong to the removed extension
|
||||
const views = this.viewsRegistry.getViews(viewContainer).filter(view => !removedExtensions.has(ExtensionIdentifier.toKey((view as ICustomViewDescriptor).extensionId)));
|
||||
// move all views in this container into default view container
|
||||
const views = this.viewsRegistry.getViews(viewContainer);
|
||||
if (views.length) {
|
||||
this.viewsRegistry.moveViews(views, this.getDefaultViewContainer());
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ configurationExtPoint.setHandler((extensions, { added, removed }) => {
|
||||
validateProperties(configuration, extension);
|
||||
|
||||
configuration.id = node.id || extension.description.identifier.value;
|
||||
configuration.extensionInfo = { id: extension.description.identifier.value };
|
||||
configuration.extensionInfo = { id: extension.description.identifier.value, restrictedConfigurations: extension.description.capabilities?.untrustedWorkspaces?.supported === 'limited' ? extension.description.capabilities?.untrustedWorkspaces.restrictedConfigurations : undefined };
|
||||
configuration.title = configuration.title || extension.description.displayName || extension.description.identifier.value;
|
||||
configurations.push(configuration);
|
||||
return configurations;
|
||||
@@ -181,10 +181,10 @@ configurationExtPoint.setHandler((extensions, { added, removed }) => {
|
||||
for (let extension of added) {
|
||||
const configurations: IConfigurationNode[] = [];
|
||||
const value = <IConfigurationNode | IConfigurationNode[]>extension.value;
|
||||
if (!Array.isArray(value)) {
|
||||
configurations.push(...handleConfiguration(value, extension));
|
||||
} else {
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach(v => configurations.push(...handleConfiguration(v, extension)));
|
||||
} else {
|
||||
configurations.push(...handleConfiguration(value, extension));
|
||||
}
|
||||
extensionConfigurations.set(ExtensionIdentifier.toKey(extension.description.identifier), configurations);
|
||||
addedConfigurations.push(...configurations);
|
||||
@@ -213,7 +213,7 @@ function validateProperties(configuration: IConfigurationNode, extension: IExten
|
||||
const propertyConfiguration = properties[key];
|
||||
if (!isObject(propertyConfiguration)) {
|
||||
delete properties[key];
|
||||
extension.collector.error(nls.localize('invalid.property', "'configuration.property' must be an object"));
|
||||
extension.collector.error(nls.localize('invalid.property', "configuration.properties property '{0}' must be an object", key));
|
||||
continue;
|
||||
}
|
||||
if (propertyConfiguration.scope) {
|
||||
@@ -320,7 +320,7 @@ jsonRegistry.registerSchema('vscode://schemas/workspaceConfig', {
|
||||
'remoteAuthority': {
|
||||
type: 'string',
|
||||
doNotSuggest: true,
|
||||
description: nls.localize('workspaceConfig.remoteAuthority', "The remote server where the workspace is located. Only used by unsaved remote workspaces."),
|
||||
description: nls.localize('workspaceConfig.remoteAuthority', "The remote server where the workspace is located."),
|
||||
}
|
||||
},
|
||||
errorMessage: nls.localize('unknownWorkspaceProperty', "Unknown workspace configuration property")
|
||||
|
||||
@@ -47,7 +47,7 @@ import { ExtHostUrls } from 'vs/workbench/api/common/extHostUrls';
|
||||
import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview';
|
||||
import { IExtHostWindow } from 'vs/workbench/api/common/extHostWindow';
|
||||
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { throwProposedApiError, checkProposedApiEnabled, checkRequiresWorkspaceTrust } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { throwProposedApiError, checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier';
|
||||
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
|
||||
import type * as vscode from 'vscode';
|
||||
@@ -84,6 +84,9 @@ import { ExtHostUriOpeners } from 'vs/workbench/api/common/extHostUriOpener';
|
||||
import { IExtHostSecretState } from 'vs/workbench/api/common/exHostSecretState';
|
||||
import { ExtHostEditorTabs } from 'vs/workbench/api/common/extHostEditorTabs';
|
||||
import { IExtHostTelemetry } from 'vs/workbench/api/common/extHostTelemetry';
|
||||
import { ExtHostNotebookKernels } from 'vs/workbench/api/common/extHostNotebookKernels';
|
||||
import { RemoteTrustOption } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { TextSearchCompleteMessageType } from 'vs/workbench/services/search/common/searchExtTypes';
|
||||
|
||||
export interface IExtensionApiFactory {
|
||||
(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode;
|
||||
@@ -140,7 +143,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
const extHostDocuments = rpcProtocol.set(ExtHostContext.ExtHostDocuments, new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors));
|
||||
const extHostDocumentContentProviders = rpcProtocol.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(rpcProtocol, extHostDocumentsAndEditors, extHostLogService));
|
||||
const extHostDocumentSaveParticipant = rpcProtocol.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(extHostLogService, extHostDocuments, rpcProtocol.getProxy(MainContext.MainThreadBulkEdits)));
|
||||
const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, extHostDocuments, initData.environment, extHostLogService, extensionStoragePaths));
|
||||
const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, extHostDocuments, extHostLogService, extensionStoragePaths));
|
||||
const extHostNotebookKernels = rpcProtocol.set(ExtHostContext.ExtHostNotebookKernels, new ExtHostNotebookKernels(rpcProtocol, initData, extHostNotebook));
|
||||
const extHostEditors = rpcProtocol.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(rpcProtocol, extHostDocumentsAndEditors));
|
||||
const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands, extHostLogService));
|
||||
const extHostEditorInsets = rpcProtocol.set(ExtHostContext.ExtHostEditorInsets, new ExtHostEditorInsets(rpcProtocol.getProxy(MainContext.MainThreadEditorInsets), extHostEditors, initData.environment));
|
||||
@@ -333,9 +337,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
: extHostTypes.ExtensionKind.UI;
|
||||
|
||||
const test: typeof vscode.test = {
|
||||
registerTestProvider(provider) {
|
||||
registerTestController(provider) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTesting.registerTestProvider(provider);
|
||||
return extHostTesting.registerTestController(extension.identifier.value, provider);
|
||||
},
|
||||
createDocumentTestObserver(document) {
|
||||
checkProposedApiEnabled(extension);
|
||||
@@ -349,9 +353,12 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTesting.runTests(provider);
|
||||
},
|
||||
publishTestResult(results, persist = true) {
|
||||
createTestItem<T>(options: vscode.TestItemOptions, data?: T) {
|
||||
return new extHostTypes.TestItemImpl(options.id, options.label, options.uri, data);
|
||||
},
|
||||
createTestRun(request, name, persist) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTesting.publishExtensionProvidedResults(results, persist);
|
||||
return extHostTesting.createTestRun(extension.identifier.value, request, name, persist);
|
||||
},
|
||||
get onDidChangeTestResults() {
|
||||
checkProposedApiEnabled(extension);
|
||||
@@ -363,6 +370,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
},
|
||||
};
|
||||
|
||||
// todo@connor4312: backwards compatibility for a short period
|
||||
(test as any).createTestRunTask = test.createTestRun;
|
||||
|
||||
// namespace: extensions
|
||||
const extensions: typeof vscode.extensions = {
|
||||
getExtension(extensionId: string): vscode.Extension<any> | undefined {
|
||||
@@ -633,6 +643,12 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
if ('pty' in nameOrOptions) {
|
||||
return extHostTerminalService.createExtensionTerminal(nameOrOptions);
|
||||
}
|
||||
if (nameOrOptions.message) {
|
||||
checkProposedApiEnabled(extension);
|
||||
}
|
||||
if (nameOrOptions.icon) {
|
||||
checkProposedApiEnabled(extension);
|
||||
}
|
||||
return extHostTerminalService.createTerminalFromOptions(nameOrOptions);
|
||||
}
|
||||
return extHostTerminalService.createTerminal(nameOrOptions, shellPath, shellArgs);
|
||||
@@ -900,7 +916,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTunnelService.onDidChangeTunnels(listener, thisArg, disposables);
|
||||
},
|
||||
registerPortAttributesProvider: (portSelector: { pid?: number, portRange?: [number, number] }, provider: vscode.PortAttributesProvider) => {
|
||||
registerPortAttributesProvider: (portSelector: { pid?: number, portRange?: [number, number], commandMatcher?: RegExp }, provider: vscode.PortAttributesProvider) => {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTunnelService.registerPortsAttributesProvider(portSelector, provider);
|
||||
},
|
||||
@@ -908,18 +924,15 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTimeline.registerTimelineProvider(scheme, provider, extension.identifier, extHostCommands.converter);
|
||||
},
|
||||
get trustState() {
|
||||
checkProposedApiEnabled(extension);
|
||||
checkRequiresWorkspaceTrust(extension);
|
||||
return extHostWorkspace.trustState;
|
||||
get isTrusted() {
|
||||
return extHostWorkspace.trusted;
|
||||
},
|
||||
requireWorkspaceTrust: (options?: vscode.WorkspaceTrustRequestOptions) => {
|
||||
requestWorkspaceTrust: (options?: vscode.WorkspaceTrustRequestOptions) => {
|
||||
checkProposedApiEnabled(extension);
|
||||
checkRequiresWorkspaceTrust(extension);
|
||||
return extHostWorkspace.requireWorkspaceTrust(options);
|
||||
return extHostWorkspace.requestWorkspaceTrust(options);
|
||||
},
|
||||
onDidChangeWorkspaceTrustState: (listener, thisArgs?, disposables?) => {
|
||||
return extHostWorkspace.onDidChangeWorkspaceTrustState(listener, thisArgs, disposables);
|
||||
onDidGrantWorkspaceTrust: (listener, thisArgs?, disposables?) => {
|
||||
return extHostWorkspace.onDidGrantWorkspaceTrust(listener, thisArgs, disposables);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1045,26 +1058,19 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
},
|
||||
get notebookDocuments(): vscode.NotebookDocument[] {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.notebookDocuments.map(d => d.notebookDocument);
|
||||
},
|
||||
get onDidChangeActiveNotebookKernel() {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeActiveNotebookKernel;
|
||||
return extHostNotebook.notebookDocuments.map(d => d.apiNotebook);
|
||||
},
|
||||
registerNotebookSerializer(viewType, serializer, options) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.registerNotebookSerializer(extension, viewType, serializer, options);
|
||||
},
|
||||
registerNotebookContentProvider: (viewType: string, provider: vscode.NotebookContentProvider, options?: {
|
||||
transientOutputs: boolean;
|
||||
transientMetadata: { [K in keyof vscode.NotebookCellMetadata]?: boolean }
|
||||
}) => {
|
||||
registerNotebookContentProvider: (viewType, provider, options) => {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.registerNotebookContentProvider(extension, viewType, provider, options);
|
||||
},
|
||||
registerNotebookKernelProvider: (selector: vscode.NotebookDocumentFilter, provider: vscode.NotebookKernelProvider) => {
|
||||
registerNotebookCellStatusBarItemProvider: (selector: vscode.NotebookSelector, provider: vscode.NotebookCellStatusBarItemProvider) => {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.registerNotebookKernelProvider(extension, selector, provider);
|
||||
return extHostNotebook.registerNotebookCellStatusBarItemProvider(extension, selector, provider);
|
||||
},
|
||||
createNotebookEditorDecorationType(options: vscode.NotebookDecorationRenderOptions): vscode.NotebookEditorDecorationType {
|
||||
checkProposedApiEnabled(extension);
|
||||
@@ -1094,13 +1100,13 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
checkProposedApiEnabled(extension);
|
||||
return new ExtHostNotebookConcatDocument(extHostNotebook, extHostDocuments, notebook, selector);
|
||||
},
|
||||
createCellStatusBarItem(cell: vscode.NotebookCell, alignment?: vscode.NotebookCellStatusBarAlignment, priority?: number): vscode.NotebookCellStatusBarItem {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.createNotebookCellStatusBarItemInternal(cell, alignment, priority);
|
||||
},
|
||||
createNotebookCellExecutionTask(uri: vscode.Uri, index: number, kernelId: string): vscode.NotebookCellExecutionTask | undefined {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.createNotebookCellExecution(uri, index, kernelId);
|
||||
},
|
||||
createNotebookController(id, viewType, label, executeHandler, preloads) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebookKernels.createNotebookController(extension, id, viewType, label, executeHandler, preloads);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1233,11 +1239,12 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
InlineHint: extHostTypes.InlineHint,
|
||||
InlineHintKind: extHostTypes.InlineHintKind,
|
||||
RemoteAuthorityResolverError: extHostTypes.RemoteAuthorityResolverError,
|
||||
RemoteTrustOption: RemoteTrustOption,
|
||||
ResolvedAuthority: extHostTypes.ResolvedAuthority,
|
||||
SourceControlInputBoxValidationType: extHostTypes.SourceControlInputBoxValidationType,
|
||||
ExtensionRuntime: extHostTypes.ExtensionRuntime,
|
||||
TimelineItem: extHostTypes.TimelineItem,
|
||||
NotebookCellRange: extHostTypes.NotebookCellRange,
|
||||
NotebookRange: extHostTypes.NotebookRange,
|
||||
NotebookCellKind: extHostTypes.NotebookCellKind,
|
||||
NotebookCellExecutionState: extHostTypes.NotebookCellExecutionState,
|
||||
NotebookDocumentMetadata: extHostTypes.NotebookDocumentMetadata,
|
||||
@@ -1248,11 +1255,13 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
NotebookEditorRevealType: extHostTypes.NotebookEditorRevealType,
|
||||
NotebookCellOutput: extHostTypes.NotebookCellOutput,
|
||||
NotebookCellOutputItem: extHostTypes.NotebookCellOutputItem,
|
||||
NotebookCellStatusBarItem: extHostTypes.NotebookCellStatusBarItem,
|
||||
NotebookControllerAffinity: extHostTypes.NotebookControllerAffinity,
|
||||
LinkedEditingRanges: extHostTypes.LinkedEditingRanges,
|
||||
TestItem: extHostTypes.TestItem,
|
||||
TestState: extHostTypes.TestState,
|
||||
TestResult: extHostTypes.TestResult,
|
||||
TestItemStatus: extHostTypes.TestItemStatus,
|
||||
TestResultState: extHostTypes.TestResultState,
|
||||
TestMessage: extHostTypes.TestMessage,
|
||||
TextSearchCompleteMessageType: TextSearchCompleteMessageType,
|
||||
TestMessageSeverity: extHostTypes.TestMessageSeverity,
|
||||
WorkspaceTrustState: extHostTypes.WorkspaceTrustState
|
||||
};
|
||||
|
||||
@@ -41,7 +41,7 @@ import * as tasks from 'vs/workbench/api/common/shared/tasks';
|
||||
import { IRevealOptions, ITreeItem } from 'vs/workbench/common/views';
|
||||
import { IAdapterDescriptor, IConfig, IDebugSessionReplMode } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { ITextQueryBuilderOptions } from 'vs/workbench/contrib/search/common/queryBuilder';
|
||||
import { ActivationKind, ExtensionActivationError, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ActivationKind, MissingExtensionDependency, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId, IRPCProtocol } from 'vs/workbench/services/extensions/common/proxyIdentifier';
|
||||
import * as search from 'vs/workbench/services/search/common/search';
|
||||
import { EditorGroupColumn, SaveReason } from 'vs/workbench/common/editor';
|
||||
@@ -50,18 +50,21 @@ import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
|
||||
import { TunnelCreationOptions, TunnelProviderFeatures, TunnelOptions, ProvidedPortAttributes } from 'vs/platform/remote/common/tunnel';
|
||||
import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, InternalTimelineOptions } from 'vs/workbench/contrib/timeline/common/timeline';
|
||||
import { revive } from 'vs/base/common/marshalling';
|
||||
import { NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEventDto, NotebookDataDto, IMainCellDto, INotebookDocumentFilter, TransientMetadata, INotebookCellStatusBarEntry, ICellRange, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter, IOutputDto, TransientOptions, IImmediateCellEditOperation } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEventDto, NotebookDataDto, IMainCellDto, TransientCellMetadata, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter, IOutputDto, TransientOptions, IImmediateCellEditOperation, INotebookCellStatusBarItem, TransientDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange';
|
||||
import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
|
||||
import { Dto } from 'vs/base/common/types';
|
||||
import { DebugConfigurationProviderTriggerKind, WorkspaceTrustState } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { DebugConfigurationProviderTriggerKind, TestResultState } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { IExtensionIdWithVersion } from 'vs/platform/userDataSync/common/extensionsStorageSync';
|
||||
import { InternalTestItem, ITestState, RunTestForProviderRequest, RunTestsRequest, TestIdWithSrc, TestsDiff, ISerializedTestResults } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { InternalTestItem, RunTestForProviderRequest, RunTestsRequest, TestIdWithSrc, TestsDiff, ISerializedTestResults, ITestMessage, ITestItem, ITestRunTask, ExtensionRunTestsRequest } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { CandidatePort } from 'vs/workbench/services/remote/common/remoteExplorerService';
|
||||
import { WorkspaceTrustRequestOptions, WorkspaceTrustStateChangeEvent } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { WorkspaceTrustRequestOptions } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
import { IShellLaunchConfig, ITerminalDimensions, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal';
|
||||
import { IShellLaunchConfig, IShellLaunchConfigDto, ITerminalDimensions, ITerminalEnvironment, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal';
|
||||
import { ITerminalProfile } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { NotebookSelector } from 'vs/workbench/contrib/notebook/common/notebookSelector';
|
||||
import { InputValidationType } from 'vs/workbench/contrib/scm/common/scm';
|
||||
|
||||
export interface IEnvironment {
|
||||
isExtensionDevelopmentDebug: boolean;
|
||||
@@ -454,7 +457,9 @@ export interface TerminalLaunchConfig {
|
||||
shellPath?: string;
|
||||
shellArgs?: string[] | string;
|
||||
cwd?: string | UriComponents;
|
||||
env?: { [key: string]: string | null; };
|
||||
env?: ITerminalEnvironment;
|
||||
icon?: string;
|
||||
initialText?: string;
|
||||
waitOnExit?: boolean;
|
||||
strictEnv?: boolean;
|
||||
hideFromUser?: boolean;
|
||||
@@ -671,10 +676,39 @@ export interface CustomTextEditorCapabilities {
|
||||
readonly supportsMove?: boolean;
|
||||
}
|
||||
|
||||
export const enum WebviewMessageArrayBufferViewType {
|
||||
Int8Array = 1,
|
||||
Uint8Array = 2,
|
||||
Uint8ClampedArray = 3,
|
||||
Int16Array = 4,
|
||||
Uint16Array = 5,
|
||||
Int32Array = 6,
|
||||
Uint32Array = 7,
|
||||
Float32Array = 8,
|
||||
Float64Array = 9,
|
||||
BigInt64Array = 10,
|
||||
BigUint64Array = 11,
|
||||
}
|
||||
|
||||
export interface WebviewMessageArrayBufferReference {
|
||||
readonly $$vscode_array_buffer_reference$$: true,
|
||||
|
||||
readonly index: number;
|
||||
|
||||
/**
|
||||
* Tracks if the reference is to a view instead of directly to an ArrayBuffer.
|
||||
*/
|
||||
readonly view?: {
|
||||
readonly type: WebviewMessageArrayBufferViewType;
|
||||
readonly byteLength: number;
|
||||
readonly byteOffset: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface MainThreadWebviewsShape extends IDisposable {
|
||||
$setHtml(handle: WebviewHandle, value: string): void;
|
||||
$setOptions(handle: WebviewHandle, options: IWebviewOptions): void;
|
||||
$postMessage(handle: WebviewHandle, value: any): Promise<boolean>
|
||||
$postMessage(handle: WebviewHandle, value: any, ...buffers: VSBuffer[]): Promise<boolean>
|
||||
}
|
||||
|
||||
export interface MainThreadWebviewPanelsShape extends IDisposable {
|
||||
@@ -686,6 +720,7 @@ export interface MainThreadWebviewPanelsShape extends IDisposable {
|
||||
title: string;
|
||||
webviewOptions: IWebviewOptions;
|
||||
panelOptions: IWebviewPanelOptions;
|
||||
serializeBuffersForPostMessage: boolean;
|
||||
},
|
||||
showOptions: WebviewPanelShowOptions,
|
||||
): void;
|
||||
@@ -694,13 +729,13 @@ export interface MainThreadWebviewPanelsShape extends IDisposable {
|
||||
$setTitle(handle: WebviewHandle, value: string): void;
|
||||
$setIconPath(handle: WebviewHandle, value: { light: UriComponents, dark: UriComponents; } | undefined): void;
|
||||
|
||||
$registerSerializer(viewType: string): void;
|
||||
$registerSerializer(viewType: string, options: { serializeBuffersForPostMessage: boolean }): void;
|
||||
$unregisterSerializer(viewType: string): void;
|
||||
}
|
||||
|
||||
export interface MainThreadCustomEditorsShape extends IDisposable {
|
||||
$registerTextEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: IWebviewPanelOptions, capabilities: CustomTextEditorCapabilities): void;
|
||||
$registerCustomEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: IWebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean): void;
|
||||
$registerTextEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: IWebviewPanelOptions, capabilities: CustomTextEditorCapabilities, serializeBuffersForPostMessage: boolean): void;
|
||||
$registerCustomEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: IWebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean, serializeBuffersForPostMessage: boolean): void;
|
||||
$unregisterEditorProvider(viewType: string): void;
|
||||
|
||||
$onDidEdit(resource: UriComponents, viewType: string, editId: number, label: string | undefined): void;
|
||||
@@ -708,7 +743,7 @@ export interface MainThreadCustomEditorsShape extends IDisposable {
|
||||
}
|
||||
|
||||
export interface MainThreadWebviewViewsShape extends IDisposable {
|
||||
$registerWebviewViewProvider(extension: WebviewExtensionDescription, viewType: string, options?: { retainContextWhenHidden?: boolean }): void;
|
||||
$registerWebviewViewProvider(extension: WebviewExtensionDescription, viewType: string, options: { retainContextWhenHidden?: boolean, serializeBuffersForPostMessage: boolean }): void;
|
||||
$unregisterWebviewViewProvider(viewType: string): void;
|
||||
|
||||
$setWebviewViewTitle(handle: WebviewHandle, value: string | undefined): void;
|
||||
@@ -726,7 +761,7 @@ export interface WebviewPanelViewStateData {
|
||||
}
|
||||
|
||||
export interface ExtHostWebviewsShape {
|
||||
$onMessage(handle: WebviewHandle, message: any): void;
|
||||
$onMessage(handle: WebviewHandle, jsonSerializedMessage: string, ...buffers: VSBuffer[]): void;
|
||||
$onMissingCsp(handle: WebviewHandle, extensionId: string): void;
|
||||
}
|
||||
|
||||
@@ -827,37 +862,69 @@ export interface INotebookDocumentShowOptions {
|
||||
position?: EditorGroupColumn;
|
||||
preserveFocus?: boolean;
|
||||
pinned?: boolean;
|
||||
selection?: ICellRange;
|
||||
selections?: ICellRange[];
|
||||
}
|
||||
|
||||
export type INotebookCellStatusBarEntryDto = Dto<INotebookCellStatusBarEntry>;
|
||||
export type INotebookCellStatusBarEntryDto = Dto<INotebookCellStatusBarItem>;
|
||||
|
||||
export interface INotebookCellStatusBarListDto {
|
||||
items: INotebookCellStatusBarEntryDto[];
|
||||
cacheId: number;
|
||||
}
|
||||
|
||||
export interface MainThreadNotebookShape extends IDisposable {
|
||||
$registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, options: {
|
||||
transientOutputs: boolean;
|
||||
transientMetadata: TransientMetadata;
|
||||
transientCellMetadata: TransientCellMetadata;
|
||||
transientDocumentMetadata: TransientDocumentMetadata;
|
||||
viewOptions?: { displayName: string; filenamePattern: (string | IRelativePattern | INotebookExclusiveDocumentFilter)[]; exclusive: boolean; };
|
||||
}): Promise<void>;
|
||||
$updateNotebookProviderOptions(viewType: string, options?: { transientOutputs: boolean; transientMetadata: TransientMetadata; }): Promise<void>;
|
||||
$updateNotebookProviderOptions(viewType: string, options?: { transientOutputs: boolean; transientCellMetadata: TransientCellMetadata; transientDocumentMetadata: TransientDocumentMetadata; }): Promise<void>;
|
||||
$unregisterNotebookProvider(viewType: string): Promise<void>;
|
||||
|
||||
$registerNotebookSerializer(handle: number, extension: NotebookExtensionDescription, viewType: string, options: TransientOptions): void;
|
||||
$unregisterNotebookSerializer(handle: number): void;
|
||||
|
||||
$registerNotebookKernelProvider(extension: NotebookExtensionDescription, handle: number, documentFilter: INotebookDocumentFilter): Promise<void>;
|
||||
$unregisterNotebookKernelProvider(handle: number): Promise<void>;
|
||||
$onNotebookKernelChange(handle: number, uri: UriComponents | undefined): void;
|
||||
$trySaveDocument(uri: UriComponents): Promise<boolean>;
|
||||
$tryApplyEdits(viewType: string, resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[]): Promise<boolean>;
|
||||
$applyEdits(resource: UriComponents, edits: IImmediateCellEditOperation[], computeUndoRedo?: boolean): Promise<void>;
|
||||
$postMessage(editorId: string, forRendererId: string | undefined, value: any): Promise<boolean>;
|
||||
$setStatusBarEntry(id: number, statusBarEntry: INotebookCellStatusBarEntryDto): Promise<void>;
|
||||
$tryOpenDocument(uriComponents: UriComponents): Promise<UriComponents>;
|
||||
$registerNotebookCellStatusBarItemProvider(handle: number, eventHandle: number | undefined, selector: NotebookSelector): Promise<void>;
|
||||
$unregisterNotebookCellStatusBarItemProvider(handle: number, eventHandle: number | undefined): Promise<void>;
|
||||
$emitCellStatusBarEvent(eventHandle: number): void;
|
||||
}
|
||||
|
||||
export interface MainThreadNotebookEditorsShape extends IDisposable {
|
||||
$tryShowNotebookDocument(uriComponents: UriComponents, viewType: string, options: INotebookDocumentShowOptions): Promise<string>;
|
||||
$tryRevealRange(id: string, range: ICellRange, revealType: NotebookEditorRevealType): Promise<void>;
|
||||
$registerNotebookEditorDecorationType(key: string, options: INotebookDecorationRenderOptions): void;
|
||||
$removeNotebookEditorDecorationType(key: string): void;
|
||||
$trySetDecorations(id: string, range: ICellRange, decorationKey: string): void;
|
||||
$tryApplyEdits(editorId: string, modelVersionId: number, cellEdits: ICellEditOperation[]): Promise<boolean>
|
||||
}
|
||||
|
||||
export interface MainThreadNotebookDocumentsShape extends IDisposable {
|
||||
$tryOpenDocument(uriComponents: UriComponents): Promise<UriComponents>;
|
||||
$trySaveDocument(uri: UriComponents): Promise<boolean>;
|
||||
$applyEdits(resource: UriComponents, edits: IImmediateCellEditOperation[], computeUndoRedo?: boolean): Promise<void>;
|
||||
}
|
||||
|
||||
export interface INotebookKernelDto2 {
|
||||
id: string;
|
||||
viewType: string;
|
||||
extensionId: ExtensionIdentifier;
|
||||
extensionLocation: UriComponents;
|
||||
label: string;
|
||||
detail?: string;
|
||||
description?: string;
|
||||
supportedLanguages?: string[];
|
||||
supportsInterrupt?: boolean;
|
||||
hasExecutionOrder?: boolean;
|
||||
preloads?: { uri: UriComponents; provides: string[] }[];
|
||||
}
|
||||
|
||||
export interface MainThreadNotebookKernelsShape extends IDisposable {
|
||||
$postMessage(handle: number, editorId: string | undefined, message: any): Promise<boolean>;
|
||||
$addKernel(handle: number, data: INotebookKernelDto2): Promise<void>;
|
||||
$updateKernel(handle: number, data: Partial<INotebookKernelDto2>): void;
|
||||
$removeKernel(handle: number): void;
|
||||
$updateNotebookPriority(handle: number, uri: UriComponents, value: number | undefined): void;
|
||||
}
|
||||
|
||||
export interface MainThreadUrlsShape extends IDisposable {
|
||||
@@ -891,7 +958,7 @@ export interface MainThreadWorkspaceShape extends IDisposable {
|
||||
$saveAll(includeUntitled?: boolean): Promise<boolean>;
|
||||
$updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, workspaceFoldersToAdd: { uri: UriComponents, name?: string; }[]): Promise<void>;
|
||||
$resolveProxy(url: string): Promise<string | undefined>;
|
||||
$requireWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Promise<WorkspaceTrustState>;
|
||||
$requestWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Promise<boolean | undefined>;
|
||||
}
|
||||
|
||||
export interface IFileChangeDto {
|
||||
@@ -945,7 +1012,7 @@ export interface MainThreadExtensionServiceShape extends IDisposable {
|
||||
$activateExtension(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void>;
|
||||
$onWillActivateExtension(extensionId: ExtensionIdentifier): Promise<void>;
|
||||
$onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void;
|
||||
$onExtensionActivationError(extensionId: ExtensionIdentifier, error: ExtensionActivationError): Promise<void>;
|
||||
$onExtensionActivationError(extensionId: ExtensionIdentifier, error: SerializedError, missingExtensionDependency: MissingExtensionDependency | null): Promise<void>;
|
||||
$onExtensionRuntimeError(extensionId: ExtensionIdentifier, error: SerializedError): void;
|
||||
$setPerformanceMarks(marks: performance.PerformanceMark[]): Promise<void>;
|
||||
}
|
||||
@@ -999,6 +1066,8 @@ export interface MainThreadSCMShape extends IDisposable {
|
||||
$setInputBoxValue(sourceControlHandle: number, value: string): void;
|
||||
$setInputBoxPlaceholder(sourceControlHandle: number, placeholder: string): void;
|
||||
$setInputBoxVisibility(sourceControlHandle: number, visible: boolean): void;
|
||||
$setInputBoxFocus(sourceControlHandle: number): void;
|
||||
$showValidationMessage(sourceControlHandle: number, message: string, type: InputValidationType): void;
|
||||
$setValidationProviderIsEnabled(sourceControlHandle: number, enabled: boolean): void;
|
||||
}
|
||||
|
||||
@@ -1059,6 +1128,7 @@ export enum CandidatePortSource {
|
||||
export interface PortAttributesProviderSelector {
|
||||
pid?: number;
|
||||
portRange?: [number, number];
|
||||
commandMatcher?: RegExp;
|
||||
}
|
||||
|
||||
export interface MainThreadTunnelServiceShape extends IDisposable {
|
||||
@@ -1167,10 +1237,10 @@ export interface ExtHostTreeViewsShape {
|
||||
}
|
||||
|
||||
export interface ExtHostWorkspaceShape {
|
||||
$initializeWorkspace(workspace: IWorkspaceData | null, trustState: WorkspaceTrustState): void;
|
||||
$initializeWorkspace(workspace: IWorkspaceData | null, trusted: boolean): void;
|
||||
$acceptWorkspaceData(workspace: IWorkspaceData | null): void;
|
||||
$handleTextSearchResult(result: search.IRawFileMatch2, requestId: number): void;
|
||||
$onDidChangeWorkspaceTrustState(state: WorkspaceTrustStateChangeEvent): void;
|
||||
$onDidGrantWorkspaceTrust(): void;
|
||||
}
|
||||
|
||||
export interface ExtHostFileSystemInfoShape {
|
||||
@@ -1268,8 +1338,8 @@ export interface IWillRunFileOperationParticipation {
|
||||
|
||||
export interface ExtHostFileSystemEventServiceShape {
|
||||
$onFileEvent(events: FileSystemEvents): void;
|
||||
$onWillRunFileOperation(operation: files.FileOperation, files: SourceTargetPair[], timeout: number, token: CancellationToken): Promise<IWillRunFileOperationParticipation | undefined>;
|
||||
$onDidRunFileOperation(operation: files.FileOperation, files: SourceTargetPair[]): void;
|
||||
$onWillRunFileOperation(operation: files.FileOperation, files: readonly SourceTargetPair[], timeout: number, token: CancellationToken): Promise<IWillRunFileOperationParticipation | undefined>;
|
||||
$onDidRunFileOperation(operation: files.FileOperation, files: readonly SourceTargetPair[]): void;
|
||||
}
|
||||
|
||||
export interface ObjectIdentifier {
|
||||
@@ -1609,15 +1679,6 @@ export interface ExtHostTelemetryShape {
|
||||
$onDidChangeTelemetryEnabled(enabled: boolean): void;
|
||||
}
|
||||
|
||||
export interface IShellLaunchConfigDto {
|
||||
name?: string;
|
||||
executable?: string;
|
||||
args?: string[] | string;
|
||||
cwd?: string | UriComponents;
|
||||
env?: { [key: string]: string | null; };
|
||||
hideFromUser?: boolean;
|
||||
}
|
||||
|
||||
export interface IShellAndArgsDto {
|
||||
shell: string;
|
||||
args: string[] | string | undefined;
|
||||
@@ -1656,9 +1717,7 @@ export interface ExtHostTerminalServiceShape {
|
||||
$acceptProcessRequestInitialCwd(id: number): void;
|
||||
$acceptProcessRequestCwd(id: number): void;
|
||||
$acceptProcessRequestLatency(id: number): number;
|
||||
$acceptWorkspacePermissionsChanged(isAllowed: boolean): void;
|
||||
// TODO: Change quickLaunchOnly to "includeAutoDetected" or something similar
|
||||
$getAvailableProfiles(quickLaunchOnly: boolean): Promise<ITerminalProfile[]>;
|
||||
$getAvailableProfiles(configuredProfilesOnly: boolean): Promise<ITerminalProfile[]>;
|
||||
$getDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto>;
|
||||
$provideLinks(id: number, line: string): Promise<ITerminalLinkDto[]>;
|
||||
$activateLink(id: number, linkId: number): void;
|
||||
@@ -1857,34 +1916,47 @@ export interface INotebookKernelInfoDto2 {
|
||||
description?: string;
|
||||
detail?: string;
|
||||
isPreferred?: boolean;
|
||||
preloads?: UriComponents[];
|
||||
preloads?: { uri: UriComponents; provides: string[] }[];
|
||||
supportedLanguages?: string[]
|
||||
implementsInterrupt?: boolean;
|
||||
}
|
||||
|
||||
export interface ExtHostNotebookShape {
|
||||
$resolveNotebookEditor(viewType: string, uri: UriComponents, editorId: string): Promise<void>;
|
||||
$acceptNotebookActiveKernelChange(event: { uri: UriComponents, providerHandle: number | undefined, kernelFriendlyId: string | undefined }): void;
|
||||
$provideNotebookKernels(handle: number, uri: UriComponents, token: CancellationToken): Promise<INotebookKernelInfoDto2[]>;
|
||||
$resolveNotebookKernel(handle: number, editorId: string, uri: UriComponents, kernelId: string, token: CancellationToken): Promise<void>;
|
||||
$executeNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellRanges: ICellRange[]): Promise<void>;
|
||||
$cancelNotebookCellExecution(handle: number, uri: UriComponents, kernelId: string, cellRange: ICellRange[]): Promise<void>;
|
||||
$onDidReceiveMessage(editorId: string, rendererId: string | undefined, message: unknown): void;
|
||||
export interface ExtHostNotebookShape extends ExtHostNotebookDocumentsAndEditorsShape, ExtHostNotebookDocumentsShape, ExtHostNotebookEditorsShape {
|
||||
$provideNotebookCellStatusBarItems(handle: number, uri: UriComponents, index: number, token: CancellationToken): Promise<INotebookCellStatusBarListDto | undefined>;
|
||||
$releaseNotebookCellStatusBarItems(id: number): void;
|
||||
|
||||
$openNotebook(viewType: string, uri: UriComponents, backupId: string | undefined, untitledDocumentData: VSBuffer | undefined, token: CancellationToken): Promise<NotebookDataDto>;
|
||||
$saveNotebook(viewType: string, uri: UriComponents, token: CancellationToken): Promise<boolean>;
|
||||
$saveNotebookAs(viewType: string, uri: UriComponents, target: UriComponents, token: CancellationToken): Promise<boolean>;
|
||||
$backupNotebook(viewType: string, uri: UriComponents, cancellation: CancellationToken): Promise<string>;
|
||||
|
||||
$dataToNotebook(handle: number, data: VSBuffer): Promise<NotebookDataDto>;
|
||||
$notebookToData(handle: number, data: NotebookDataDto): Promise<VSBuffer>;
|
||||
$dataToNotebook(handle: number, data: VSBuffer, token: CancellationToken): Promise<NotebookDataDto>;
|
||||
$notebookToData(handle: number, data: NotebookDataDto, token: CancellationToken): Promise<VSBuffer>;
|
||||
}
|
||||
|
||||
export interface ExtHostNotebookDocumentsAndEditorsShape {
|
||||
$acceptDocumentAndEditorsDelta(delta: INotebookDocumentsAndEditorsDelta): void;
|
||||
}
|
||||
|
||||
export interface ExtHostNotebookDocumentsShape {
|
||||
$acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEventDto, isDirty: boolean): void;
|
||||
$acceptDirtyStateChanged(uriComponents: UriComponents, isDirty: boolean): void;
|
||||
$acceptModelSaved(uriComponents: UriComponents): void;
|
||||
$acceptDocumentPropertiesChanged(uriComponents: UriComponents, data: INotebookDocumentPropertiesChangeData): void;
|
||||
$acceptDocumentAndEditorsDelta(delta: INotebookDocumentsAndEditorsDelta): void;
|
||||
}
|
||||
|
||||
export type INotebookEditorViewColumnInfo = Record<string, number>;
|
||||
|
||||
export interface ExtHostNotebookEditorsShape {
|
||||
$acceptEditorPropertiesChanged(id: string, data: INotebookEditorPropertiesChangeData): void;
|
||||
$acceptEditorViewColumns(data: INotebookEditorViewColumnInfo): void;
|
||||
}
|
||||
|
||||
export interface ExtHostNotebookKernelsShape {
|
||||
$acceptSelection(handle: number, uri: UriComponents, value: boolean): void;
|
||||
$executeCells(handle: number, uri: UriComponents, handles: number[]): Promise<void>;
|
||||
$cancelCells(handle: number, uri: UriComponents, handles: number[]): Promise<void>;
|
||||
$acceptRendererMessage(handle: number, editorId: string, message: any): void;
|
||||
}
|
||||
|
||||
export interface ExtHostStorageShape {
|
||||
@@ -1927,14 +1999,39 @@ export interface ExtHostTestingShape {
|
||||
}
|
||||
|
||||
export interface MainThreadTestingShape {
|
||||
$registerTestProvider(id: string): void;
|
||||
$unregisterTestProvider(id: string): void;
|
||||
/** Registeres that there's a test controller with the given ID */
|
||||
$registerTestController(id: string): void;
|
||||
/** Diposes of the test controller with the given ID */
|
||||
$unregisterTestController(id: string): void;
|
||||
/** Requests tests from the given resource/uri, from the observer API. */
|
||||
$subscribeToDiffs(resource: ExtHostTestingResource, uri: UriComponents): void;
|
||||
/** Stops requesting tests from the given resource/uri, from the observer API. */
|
||||
$unsubscribeFromDiffs(resource: ExtHostTestingResource, uri: UriComponents): void;
|
||||
/** Publishes that new tests were available on the given source. */
|
||||
$publishDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void;
|
||||
$updateTestStateInRun(runId: string, testId: string, state: ITestState): void;
|
||||
/** Request by an extension to run tests. */
|
||||
$runTests(req: RunTestsRequest, token: CancellationToken): Promise<string>;
|
||||
$publishExtensionProvidedResults(results: ISerializedTestResults, persist: boolean): void;
|
||||
|
||||
// --- test run handling:
|
||||
/**
|
||||
* Adds tests to the run. The tests are given in descending depth. The first
|
||||
* item will be a previously-known test, or a test root.
|
||||
*/
|
||||
$addTestsToRun(runId: string, tests: ITestItem[]): void;
|
||||
/** Updates the state of a test run in the given run. */
|
||||
$updateTestStateInRun(runId: string, taskId: string, testId: string, state: TestResultState, duration?: number): void;
|
||||
/** Appends a message to a test in the run. */
|
||||
$appendTestMessageInRun(runId: string, taskId: string, testId: string, message: ITestMessage): void;
|
||||
/** Appends raw output to the test run.. */
|
||||
$appendOutputToRun(runId: string, taskId: string, output: VSBuffer): void;
|
||||
/** Signals a task in a test run started. */
|
||||
$startedTestRunTask(runId: string, task: ITestRunTask): void;
|
||||
/** Signals a task in a test run ended. */
|
||||
$finishedTestRunTask(runId: string, taskId: string): void;
|
||||
/** Start a new extension-provided test run. */
|
||||
$startedExtensionTestRun(req: ExtensionRunTestsRequest): void;
|
||||
/** Signals that an extension-provided test run finished. */
|
||||
$finishedExtensionTestRun(runId: string): void;
|
||||
}
|
||||
|
||||
// --- proxy identifiers
|
||||
@@ -1987,6 +2084,9 @@ export const MainContext = {
|
||||
MainThreadWindow: createMainId<MainThreadWindowShape>('MainThreadWindow'),
|
||||
MainThreadLabelService: createMainId<MainThreadLabelServiceShape>('MainThreadLabelService'),
|
||||
MainThreadNotebook: createMainId<MainThreadNotebookShape>('MainThreadNotebook'),
|
||||
MainThreadNotebookDocuments: createMainId<MainThreadNotebookDocumentsShape>('MainThreadNotebookDocumentsShape'),
|
||||
MainThreadNotebookEditors: createMainId<MainThreadNotebookEditorsShape>('MainThreadNotebookEditorsShape'),
|
||||
MainThreadNotebookKernels: createMainId<MainThreadNotebookKernelsShape>('MainThreadNotebookKernels'),
|
||||
MainThreadTheming: createMainId<MainThreadThemingShape>('MainThreadTheming'),
|
||||
MainThreadTunnelService: createMainId<MainThreadTunnelServiceShape>('MainThreadTunnelService'),
|
||||
MainThreadTimeline: createMainId<MainThreadTimelineShape>('MainThreadTimeline'),
|
||||
@@ -2033,6 +2133,7 @@ export const ExtHostContext = {
|
||||
ExtHostOutputService: createMainId<ExtHostOutputServiceShape>('ExtHostOutputService'),
|
||||
ExtHosLabelService: createMainId<ExtHostLabelServiceShape>('ExtHostLabelService'),
|
||||
ExtHostNotebook: createMainId<ExtHostNotebookShape>('ExtHostNotebook'),
|
||||
ExtHostNotebookKernels: createMainId<ExtHostNotebookKernelsShape>('ExtHostNotebookKernels'),
|
||||
ExtHostTheming: createMainId<ExtHostThemingShape>('ExtHostTheming'),
|
||||
ExtHostTunnelService: createMainId<ExtHostTunnelServiceShape>('ExtHostTunnelService'),
|
||||
ExtHostAuthentication: createMainId<ExtHostAuthenticationShape>('ExtHostAuthentication'),
|
||||
|
||||
@@ -18,7 +18,7 @@ import { ICommandsExecutor, RemoveFromRecentlyOpenedAPICommand, OpenIssueReporte
|
||||
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import { TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { TransientCellMetadata, TransientDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { decodeSemanticTokensDto } from 'vs/editor/common/services/semanticTokensDto';
|
||||
@@ -30,13 +30,13 @@ const newCommands: ApiCommand[] = [
|
||||
new ApiCommand(
|
||||
'vscode.executeDocumentHighlights', '_executeDocumentHighlights', 'Execute document highlight provider.',
|
||||
[ApiCommandArgument.Uri, ApiCommandArgument.Position],
|
||||
new ApiCommandResult<modes.DocumentHighlight[], types.DocumentHighlight[] | undefined>('A promise that resolves to an array of SymbolInformation and DocumentSymbol instances.', tryMapWith(typeConverters.DocumentHighlight.to))
|
||||
new ApiCommandResult<modes.DocumentHighlight[], types.DocumentHighlight[] | undefined>('A promise that resolves to an array of DocumentHighlight-instances.', tryMapWith(typeConverters.DocumentHighlight.to))
|
||||
),
|
||||
// -- document symbols
|
||||
new ApiCommand(
|
||||
'vscode.executeDocumentSymbolProvider', '_executeDocumentSymbolProvider', 'Execute document symbol provider.',
|
||||
[ApiCommandArgument.Uri],
|
||||
new ApiCommandResult<modes.DocumentSymbol[], vscode.SymbolInformation[] | undefined>('A promise that resolves to an array of DocumentHighlight-instances.', (value, apiArgs) => {
|
||||
new ApiCommandResult<modes.DocumentSymbol[], vscode.SymbolInformation[] | undefined>('A promise that resolves to an array of SymbolInformation and DocumentSymbol instances.', (value, apiArgs) => {
|
||||
|
||||
if (isFalsyOrEmpty(value)) {
|
||||
return undefined;
|
||||
@@ -60,7 +60,7 @@ const newCommands: ApiCommand[] = [
|
||||
range!: vscode.Range;
|
||||
selectionRange!: vscode.Range;
|
||||
children!: vscode.DocumentSymbol[];
|
||||
containerName!: string;
|
||||
override containerName!: string;
|
||||
}
|
||||
return value.map(MergedInfo.to);
|
||||
|
||||
@@ -343,18 +343,22 @@ const newCommands: ApiCommand[] = [
|
||||
new ApiCommandResult<{
|
||||
viewType: string;
|
||||
displayName: string;
|
||||
options: { transientOutputs: boolean; transientMetadata: TransientMetadata };
|
||||
options: { transientOutputs: boolean; transientCellMetadata: TransientCellMetadata; transientDocumentMetadata: TransientDocumentMetadata; };
|
||||
filenamePattern: (string | types.RelativePattern | { include: string | types.RelativePattern, exclude: string | types.RelativePattern })[]
|
||||
}[], {
|
||||
viewType: string;
|
||||
displayName: string;
|
||||
filenamePattern: vscode.NotebookFilenamePattern[];
|
||||
filenamePattern: (vscode.GlobPattern | { include: vscode.GlobPattern; exclude: vscode.GlobPattern; })[];
|
||||
options: vscode.NotebookDocumentContentOptions;
|
||||
}[] | undefined>('A promise that resolves to an array of NotebookContentProvider static info objects.', tryMapWith(item => {
|
||||
return {
|
||||
viewType: item.viewType,
|
||||
displayName: item.displayName,
|
||||
options: { transientOutputs: item.options.transientOutputs, transientMetadata: item.options.transientMetadata },
|
||||
options: {
|
||||
transientOutputs: item.options.transientOutputs,
|
||||
transientCellMetadata: item.options.transientCellMetadata,
|
||||
transientDocumentMetadata: item.options.transientDocumentMetadata
|
||||
},
|
||||
filenamePattern: item.filenamePattern.map(pattern => typeConverters.NotebookExclusiveDocumentPattern.to(pattern))
|
||||
};
|
||||
}))
|
||||
|
||||
@@ -14,7 +14,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
|
||||
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { ExtHostWebviews, toExtensionData } from 'vs/workbench/api/common/extHostWebview';
|
||||
import { ExtHostWebviews, shouldSerializeBuffersForPostMessage, toExtensionData } from 'vs/workbench/api/common/extHostWebview';
|
||||
import { ExtHostWebviewPanels } from 'vs/workbench/api/common/extHostWebviewPanels';
|
||||
import { EditorGroupColumn } from 'vs/workbench/common/editor';
|
||||
import type * as vscode from 'vscode';
|
||||
@@ -183,7 +183,7 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
|
||||
disposables.add(this._editorProviders.addTextProvider(viewType, extension, provider));
|
||||
this._proxy.$registerTextEditorProvider(toExtensionData(extension), viewType, options.webviewOptions || {}, {
|
||||
supportsMove: !!provider.moveCustomTextEditor,
|
||||
});
|
||||
}, shouldSerializeBuffersForPostMessage(extension));
|
||||
} else {
|
||||
disposables.add(this._editorProviders.addCustomProvider(viewType, extension, provider));
|
||||
|
||||
@@ -199,7 +199,7 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
|
||||
}));
|
||||
}
|
||||
|
||||
this._proxy.$registerCustomEditorProvider(toExtensionData(extension), viewType, options.webviewOptions || {}, !!options.supportsMultipleEditorsPerDocument);
|
||||
this._proxy.$registerCustomEditorProvider(toExtensionData(extension), viewType, options.webviewOptions || {}, !!options.supportsMultipleEditorsPerDocument, shouldSerializeBuffersForPostMessage(extension));
|
||||
}
|
||||
|
||||
return extHostTypes.Disposable.from(
|
||||
|
||||
@@ -384,7 +384,7 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
|
||||
}
|
||||
};
|
||||
}
|
||||
return this._variableResolver.resolveAny(ws, config);
|
||||
return this._variableResolver.resolveAnyAsync(ws, config);
|
||||
}
|
||||
|
||||
protected createDebugAdapter(adapter: IAdapterDescriptor, session: ExtHostDebugSession): AbstractDebugAdapter | undefined {
|
||||
@@ -990,7 +990,7 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}, undefined, process.env);
|
||||
}, undefined, Promise.resolve(process.env));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -288,7 +288,7 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
|
||||
super(name!, owner, ExtHostDiagnostics._maxDiagnosticsPerFile, loggingProxy, _onDidChangeDiagnostics);
|
||||
_collections.set(owner, this);
|
||||
}
|
||||
dispose() {
|
||||
override dispose() {
|
||||
super.dispose();
|
||||
_collections.delete(owner);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ export class ExtHostDocumentData extends MirrorTextModel {
|
||||
super(uri, lines, eol, versionId);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
override dispose(): void {
|
||||
// we don't really dispose documents but let
|
||||
// extensions still read from them. some
|
||||
// operations, live saving, will now error tho
|
||||
|
||||
@@ -3,12 +3,11 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import type * as vscode from 'vscode';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtensionActivationError, MissingDependencyError } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { MissingExtensionDependency } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
const NO_OP_VOID_PROMISE = Promise.resolve<void>(undefined);
|
||||
@@ -158,7 +157,7 @@ export class FailedExtension extends ActivatedExtension {
|
||||
}
|
||||
|
||||
export interface IExtensionsActivatorHost {
|
||||
onExtensionActivationError(extensionId: ExtensionIdentifier, error: ExtensionActivationError): void;
|
||||
onExtensionActivationError(extensionId: ExtensionIdentifier, error: Error | null, missingExtensionDependency: MissingExtensionDependency | null): void;
|
||||
actualActivateExtension(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<ActivatedExtension>;
|
||||
}
|
||||
|
||||
@@ -255,8 +254,12 @@ export class ExtensionsActivator {
|
||||
const currentExtension = this._registry.getExtensionDescription(currentActivation.id);
|
||||
if (!currentExtension) {
|
||||
// Error condition 0: unknown extension
|
||||
this._host.onExtensionActivationError(currentActivation.id, new MissingDependencyError(currentActivation.id.value));
|
||||
const error = new Error(`Unknown dependency '${currentActivation.id.value}'`);
|
||||
const error = new Error(`Cannot activate unknown extension '${currentActivation.id.value}'`);
|
||||
this._host.onExtensionActivationError(
|
||||
currentActivation.id,
|
||||
error,
|
||||
new MissingExtensionDependency(currentActivation.id.value)
|
||||
);
|
||||
this._activatedExtensions.set(ExtensionIdentifier.toKey(currentActivation.id), new FailedExtension(error));
|
||||
return;
|
||||
}
|
||||
@@ -280,9 +283,16 @@ export class ExtensionsActivator {
|
||||
|
||||
if (dep && dep.activationFailed) {
|
||||
// Error condition 2: a dependency has already failed activation
|
||||
this._host.onExtensionActivationError(currentExtension.identifier, nls.localize('failedDep1', "Cannot activate extension '{0}' because it depends on extension '{1}', which failed to activate.", currentExtension.displayName || currentExtension.identifier.value, depId));
|
||||
const error = new Error(`Dependency ${depId} failed to activate`);
|
||||
const currentExtensionFriendlyName = currentExtension.displayName || currentExtension.identifier.value;
|
||||
const depDesc = this._registry.getExtensionDescription(depId);
|
||||
const depFriendlyName = (depDesc ? depDesc.displayName || depId : depId);
|
||||
const error = new Error(`Cannot activate the '${currentExtensionFriendlyName}' extension because its dependency '${depFriendlyName}' failed to activate`);
|
||||
(<any>error).detail = dep.activationFailedError;
|
||||
this._host.onExtensionActivationError(
|
||||
currentExtension.identifier,
|
||||
error,
|
||||
null
|
||||
);
|
||||
this._activatedExtensions.set(ExtensionIdentifier.toKey(currentExtension.identifier), new FailedExtension(error));
|
||||
return;
|
||||
}
|
||||
@@ -309,8 +319,13 @@ export class ExtensionsActivator {
|
||||
}
|
||||
|
||||
// Error condition 1: unknown dependency
|
||||
this._host.onExtensionActivationError(currentExtension.identifier, new MissingDependencyError(depId));
|
||||
const error = new Error(`Unknown dependency '${depId}'`);
|
||||
const currentExtensionFriendlyName = currentExtension.displayName || currentExtension.identifier.value;
|
||||
const error = new Error(`Cannot activate the '${currentExtensionFriendlyName}' extension because it depends on unknown extension '${depId}'`);
|
||||
this._host.onExtensionActivationError(
|
||||
currentExtension.identifier,
|
||||
error,
|
||||
new MissingExtensionDependency(depId)
|
||||
);
|
||||
this._activatedExtensions.set(ExtensionIdentifier.toKey(currentExtension.identifier), new FailedExtension(error));
|
||||
return;
|
||||
}
|
||||
@@ -372,7 +387,25 @@ export class ExtensionsActivator {
|
||||
}
|
||||
|
||||
const newlyActivatingExtension = this._host.actualActivateExtension(extensionId, reason).then(undefined, (err) => {
|
||||
this._host.onExtensionActivationError(extensionId, nls.localize('activationError', "Activating extension '{0}' failed: {1}.", extensionId.value, err.message));
|
||||
|
||||
const error = new Error();
|
||||
if (err && err.name) {
|
||||
error.name = err.name;
|
||||
}
|
||||
if (err && err.message) {
|
||||
error.message = `Activating extension '${extensionId.value}' failed: ${err.message}.`;
|
||||
} else {
|
||||
error.message = `Activating extension '${extensionId.value}' failed: ${err}.`;
|
||||
}
|
||||
if (err && err.stack) {
|
||||
error.stack = err.stack;
|
||||
}
|
||||
|
||||
this._host.onExtensionActivationError(
|
||||
extensionId,
|
||||
error,
|
||||
null
|
||||
);
|
||||
this._logService.error(`Activating extension ${extensionId.value} failed due to an error:`);
|
||||
this._logService.error(err);
|
||||
// Treat the extension as being empty
|
||||
|
||||
@@ -17,12 +17,11 @@ import { ExtHostConfiguration, IExtHostConfiguration } from 'vs/workbench/api/co
|
||||
import { ActivatedExtension, EmptyExtension, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionModule, HostExtension, ExtensionActivationTimesFragment } from 'vs/workbench/api/common/extHostExtensionActivator';
|
||||
import { ExtHostStorage, IExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
|
||||
import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { ExtensionActivationError, checkProposedApiEnabled, ActivationKind } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { MissingExtensionDependency, checkProposedApiEnabled, ActivationKind } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import type * as vscode from 'vscode';
|
||||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { ExtensionGlobalMemento, ExtensionMemento } from 'vs/workbench/api/common/extHostMemento';
|
||||
import { RemoteAuthorityResolverError, ExtensionKind, ExtensionMode, ExtensionRuntime } from 'vs/workbench/api/common/extHostTypes';
|
||||
@@ -161,8 +160,8 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
this._initData.resolvedExtensions,
|
||||
this._initData.hostExtensions,
|
||||
{
|
||||
onExtensionActivationError: (extensionId: ExtensionIdentifier, error: ExtensionActivationError): void => {
|
||||
this._mainThreadExtensionsProxy.$onExtensionActivationError(extensionId, error);
|
||||
onExtensionActivationError: (extensionId: ExtensionIdentifier, error: Error, missingExtensionDependency: MissingExtensionDependency | null): void => {
|
||||
this._mainThreadExtensionsProxy.$onExtensionActivationError(extensionId, errors.transformErrorForSerialization(error), missingExtensionDependency);
|
||||
},
|
||||
|
||||
actualActivateExtension: async (extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<ActivatedExtension> => {
|
||||
@@ -561,17 +560,15 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
|
||||
private async _doHandleExtensionTests(): Promise<number> {
|
||||
const { extensionDevelopmentLocationURI, extensionTestsLocationURI } = this._initData.environment;
|
||||
if (!extensionDevelopmentLocationURI || !extensionTestsLocationURI || extensionTestsLocationURI.scheme !== Schemas.file) {
|
||||
if (!extensionDevelopmentLocationURI || !extensionTestsLocationURI) {
|
||||
throw new Error(nls.localize('extensionTestError1', "Cannot load test runner."));
|
||||
}
|
||||
|
||||
const extensionTestsPath = originalFSPath(extensionTestsLocationURI);
|
||||
|
||||
// Require the test runner via node require from the provided path
|
||||
const testRunner: ITestRunner | INewTestRunner | undefined = await this._loadCommonJSModule(null, extensionTestsLocationURI, new ExtensionActivationTimesBuilder(false));
|
||||
|
||||
if (!testRunner || typeof testRunner.run !== 'function') {
|
||||
throw new Error(nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", extensionTestsPath));
|
||||
throw new Error(nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", extensionTestsLocationURI.toString()));
|
||||
}
|
||||
|
||||
// Execute the runner if it follows the old `run` spec
|
||||
@@ -584,6 +581,8 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
}
|
||||
};
|
||||
|
||||
const extensionTestsPath = originalFSPath(extensionTestsLocationURI); // for the old test runner API
|
||||
|
||||
const runResult = testRunner.run(extensionTestsPath, oldTestRunnerCallback);
|
||||
|
||||
// Using the new API `run(): Promise<void>`
|
||||
@@ -655,10 +654,10 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
}
|
||||
|
||||
try {
|
||||
this._disposables.add(await this._extHostTunnelService.setTunnelExtensionFunctions(resolver));
|
||||
performance.mark(`code/extHost/willResolveAuthority/${authorityPrefix}`);
|
||||
const result = await resolver.resolve(remoteAuthority, { resolveAttempt });
|
||||
performance.mark(`code/extHost/didResolveAuthorityOK/${authorityPrefix}`);
|
||||
this._disposables.add(await this._extHostTunnelService.setTunnelExtensionFunctions(resolver));
|
||||
|
||||
// Split merged API result into separate authority/options
|
||||
const authority: ResolvedAuthority = {
|
||||
@@ -668,7 +667,8 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
connectionToken: result.connectionToken
|
||||
};
|
||||
const options: ResolvedOptions = {
|
||||
extensionHostEnv: result.extensionHostEnv
|
||||
extensionHostEnv: result.extensionHostEnv,
|
||||
trust: result.trust
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -7,6 +7,7 @@ import type * as vscode from 'vscode';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { DeferredPromise, RunOnceScheduler } from 'vs/base/common/async';
|
||||
|
||||
export class ExtensionMemento implements vscode.Memento {
|
||||
|
||||
@@ -18,6 +19,9 @@ export class ExtensionMemento implements vscode.Memento {
|
||||
private _value?: { [n: string]: any; };
|
||||
private readonly _storageListener: IDisposable;
|
||||
|
||||
private _deferredPromises: Map<string, DeferredPromise<void>> = new Map();
|
||||
private _scheduler: RunOnceScheduler;
|
||||
|
||||
constructor(id: string, global: boolean, storage: ExtHostStorage) {
|
||||
this._id = id;
|
||||
this._shared = global;
|
||||
@@ -33,6 +37,23 @@ export class ExtensionMemento implements vscode.Memento {
|
||||
this._value = e.value;
|
||||
}
|
||||
});
|
||||
|
||||
this._scheduler = new RunOnceScheduler(() => {
|
||||
const records = this._deferredPromises;
|
||||
this._deferredPromises = new Map();
|
||||
(async () => {
|
||||
try {
|
||||
await this._storage.setValue(this._shared, this._id, this._value!);
|
||||
for (const value of records.values()) {
|
||||
value.complete();
|
||||
}
|
||||
} catch (e) {
|
||||
for (const value of records.values()) {
|
||||
value.error(e);
|
||||
}
|
||||
}
|
||||
})();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
get whenReady(): Promise<ExtensionMemento> {
|
||||
@@ -51,7 +72,20 @@ export class ExtensionMemento implements vscode.Memento {
|
||||
|
||||
update(key: string, value: any): Promise<void> {
|
||||
this._value![key] = value;
|
||||
return this._storage.setValue(this._shared, this._id, this._value!);
|
||||
|
||||
let record = this._deferredPromises.get(key);
|
||||
if (record !== undefined) {
|
||||
return record.p;
|
||||
}
|
||||
|
||||
const promise = new DeferredPromise<void>();
|
||||
this._deferredPromises.set(key, promise);
|
||||
|
||||
if (!this._scheduler.isScheduled()) {
|
||||
this._scheduler.schedule();
|
||||
}
|
||||
|
||||
return promise.p;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -73,7 +73,7 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD
|
||||
this._cellUris = new ResourceMap();
|
||||
const cellLengths: number[] = [];
|
||||
const cellLineCounts: number[] = [];
|
||||
for (const cell of this._notebook.cells) {
|
||||
for (const cell of this._notebook.getCells()) {
|
||||
if (cell.kind === types.NotebookCellKind.Code && (!this._selector || score(this._selector, cell.document.uri, cell.document.languageId, true))) {
|
||||
this._cellUris.set(cell.document.uri, this._cells.length);
|
||||
this._cells.push(cell);
|
||||
|
||||
@@ -3,12 +3,10 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable, DisposableStore, dispose } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { deepFreeze, equals } from 'vs/base/common/objects';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { CellKind, INotebookDocumentPropertiesChangeData, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { CellKind, INotebookDocumentPropertiesChangeData, MainThreadNotebookDocumentsShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
import { ExtHostDocumentsAndEditors, IExtHostModelAddedData } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import * as extHostTypeConverters from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
@@ -26,7 +24,7 @@ class RawContentChangeEvent {
|
||||
start: event.start,
|
||||
deletedCount: event.deletedCount,
|
||||
deletedItems: event.deletedItems,
|
||||
items: event.items.map(data => data.cell)
|
||||
items: event.items.map(data => data.apiCell)
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -46,9 +44,6 @@ export class ExtHostCell {
|
||||
};
|
||||
}
|
||||
|
||||
private _onDidDispose = new Emitter<void>();
|
||||
readonly onDidDispose: Event<void> = this._onDidDispose.event;
|
||||
|
||||
private _outputs: extHostTypes.NotebookCellOutput[];
|
||||
private _metadata: extHostTypes.NotebookCellMetadata;
|
||||
private _previousResult: vscode.NotebookCellExecutionSummary | undefined;
|
||||
@@ -74,16 +69,11 @@ export class ExtHostCell {
|
||||
this._previousResult = extHostTypeConverters.NotebookCellPreviousExecutionResult.to(this._internalMetadata);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this._onDidDispose.fire();
|
||||
this._onDidDispose.dispose();
|
||||
}
|
||||
|
||||
get internalMetadata(): NotebookCellMetadata {
|
||||
return this._internalMetadata;
|
||||
}
|
||||
|
||||
get cell(): vscode.NotebookCell {
|
||||
get apiCell(): vscode.NotebookCell {
|
||||
if (!this._cell) {
|
||||
const that = this;
|
||||
const data = this._extHostDocument.getDocument(this.uri);
|
||||
@@ -92,7 +82,7 @@ export class ExtHostCell {
|
||||
}
|
||||
this._cell = Object.freeze<vscode.NotebookCell>({
|
||||
get index() { return that._notebook.getCellIndex(that); },
|
||||
notebook: that._notebook.notebookDocument,
|
||||
notebook: that._notebook.apiNotebook,
|
||||
kind: extHostTypeConverters.NotebookCellKind.to(this._cellData.cellKind),
|
||||
document: data.document,
|
||||
get outputs() { return that._outputs.slice(0); },
|
||||
@@ -133,15 +123,13 @@ export interface INotebookEventEmitter {
|
||||
}
|
||||
|
||||
|
||||
export class ExtHostNotebookDocument extends Disposable {
|
||||
export class ExtHostNotebookDocument {
|
||||
|
||||
private static _handlePool: number = 0;
|
||||
readonly handle = ExtHostNotebookDocument._handlePool++;
|
||||
|
||||
private _cells: ExtHostCell[] = [];
|
||||
|
||||
private _cellDisposableMapping = new Map<number, DisposableStore>();
|
||||
|
||||
private _notebook: vscode.NotebookDocument | undefined;
|
||||
private _versionId: number = 0;
|
||||
private _isDirty: boolean = false;
|
||||
@@ -149,39 +137,43 @@ export class ExtHostNotebookDocument extends Disposable {
|
||||
private _disposed: boolean = false;
|
||||
|
||||
constructor(
|
||||
private readonly _proxy: MainThreadNotebookShape,
|
||||
private readonly _proxy: MainThreadNotebookDocumentsShape,
|
||||
private readonly _textDocumentsAndEditors: ExtHostDocumentsAndEditors,
|
||||
private readonly _textDocuments: ExtHostDocuments,
|
||||
private readonly _emitter: INotebookEventEmitter,
|
||||
private readonly _viewType: string,
|
||||
private _metadata: extHostTypes.NotebookDocumentMetadata,
|
||||
readonly uri: URI,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
) { }
|
||||
|
||||
dispose() {
|
||||
this._disposed = true;
|
||||
super.dispose();
|
||||
dispose(this._cellDisposableMapping.values());
|
||||
}
|
||||
|
||||
|
||||
get notebookDocument(): vscode.NotebookDocument {
|
||||
get apiNotebook(): vscode.NotebookDocument {
|
||||
if (!this._notebook) {
|
||||
const that = this;
|
||||
this._notebook = Object.freeze({
|
||||
this._notebook = {
|
||||
get uri() { return that.uri; },
|
||||
get version() { return that._versionId; },
|
||||
get fileName() { return that.uri.fsPath; },
|
||||
get viewType() { return that._viewType; },
|
||||
get isDirty() { return that._isDirty; },
|
||||
get isUntitled() { return that.uri.scheme === Schemas.untitled; },
|
||||
get cells(): ReadonlyArray<vscode.NotebookCell> { return that._cells.map(cell => cell.cell); },
|
||||
get isClosed() { return that._disposed; },
|
||||
get metadata() { return that._metadata; },
|
||||
set metadata(_value: Required<vscode.NotebookDocumentMetadata>) { throw new Error('Use WorkspaceEdit to update metadata.'); },
|
||||
save() { return that._save(); }
|
||||
});
|
||||
get cellCount() { return that._cells.length; },
|
||||
cellAt(index) {
|
||||
index = that._validateIndex(index);
|
||||
return that._cells[index].apiCell;
|
||||
},
|
||||
getCells(range) {
|
||||
const cells = range ? that._getCells(range) : that._cells;
|
||||
return cells.map(cell => cell.apiCell);
|
||||
},
|
||||
save() {
|
||||
return that._save();
|
||||
}
|
||||
};
|
||||
}
|
||||
return this._notebook;
|
||||
}
|
||||
@@ -225,6 +217,35 @@ export class ExtHostNotebookDocument extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
private _validateIndex(index: number): number {
|
||||
if (index < 0) {
|
||||
return 0;
|
||||
} else if (index >= this._cells.length) {
|
||||
return this._cells.length - 1;
|
||||
} else {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
private _validateRange(range: vscode.NotebookRange): vscode.NotebookRange {
|
||||
if (range.start < 0) {
|
||||
range = range.with({ start: 0 });
|
||||
}
|
||||
if (range.end > this._cells.length) {
|
||||
range = range.with({ end: this._cells.length });
|
||||
}
|
||||
return range;
|
||||
}
|
||||
|
||||
private _getCells(range: vscode.NotebookRange): ExtHostCell[] {
|
||||
range = this._validateRange(range);
|
||||
const result: ExtHostCell[] = [];
|
||||
for (let i = range.start; i < range.end; i++) {
|
||||
result.push(this._cells[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private async _save(): Promise<boolean> {
|
||||
if (this._disposed) {
|
||||
return Promise.reject(new Error('Notebook has been closed'));
|
||||
@@ -246,30 +267,17 @@ export class ExtHostNotebookDocument extends Disposable {
|
||||
const newCells = cellDtos.map(cell => {
|
||||
|
||||
const extCell = new ExtHostCell(this, this._textDocumentsAndEditors, cell);
|
||||
|
||||
if (!initialization) {
|
||||
addedCellDocuments.push(ExtHostCell.asModelAddData(this.notebookDocument, cell));
|
||||
addedCellDocuments.push(ExtHostCell.asModelAddData(this.apiNotebook, cell));
|
||||
}
|
||||
|
||||
if (!this._cellDisposableMapping.has(extCell.handle)) {
|
||||
const store = new DisposableStore();
|
||||
store.add(extCell);
|
||||
this._cellDisposableMapping.set(extCell.handle, store);
|
||||
}
|
||||
|
||||
return extCell;
|
||||
});
|
||||
|
||||
for (let j = splice[0]; j < splice[0] + splice[1]; j++) {
|
||||
this._cellDisposableMapping.get(this._cells[j].handle)?.dispose();
|
||||
this._cellDisposableMapping.delete(this._cells[j].handle);
|
||||
}
|
||||
|
||||
const changeEvent = new RawContentChangeEvent(splice[0], splice[1], [], newCells);
|
||||
const deletedItems = this._cells.splice(splice[0], splice[1], ...newCells);
|
||||
for (let cell of deletedItems) {
|
||||
removedCellDocuments.push(cell.uri);
|
||||
changeEvent.deletedItems.push(cell.cell);
|
||||
changeEvent.deletedItems.push(cell.apiCell);
|
||||
}
|
||||
|
||||
contentChangeEvents.push(changeEvent);
|
||||
@@ -282,7 +290,7 @@ export class ExtHostNotebookDocument extends Disposable {
|
||||
|
||||
if (!initialization) {
|
||||
this._emitter.emitModelChange(deepFreeze({
|
||||
document: this.notebookDocument,
|
||||
document: this.apiNotebook,
|
||||
changes: RawContentChangeEvent.asApiEvents(contentChangeEvents)
|
||||
}));
|
||||
}
|
||||
@@ -292,11 +300,11 @@ export class ExtHostNotebookDocument extends Disposable {
|
||||
const cells = this._cells.splice(index, 1);
|
||||
this._cells.splice(newIdx, 0, ...cells);
|
||||
const changes = [
|
||||
new RawContentChangeEvent(index, 1, cells.map(c => c.cell), []),
|
||||
new RawContentChangeEvent(index, 1, cells.map(c => c.apiCell), []),
|
||||
new RawContentChangeEvent(newIdx, 0, [], cells)
|
||||
];
|
||||
this._emitter.emitModelChange(deepFreeze({
|
||||
document: this.notebookDocument,
|
||||
document: this.apiNotebook,
|
||||
changes: RawContentChangeEvent.asApiEvents(changes)
|
||||
}));
|
||||
}
|
||||
@@ -304,18 +312,18 @@ export class ExtHostNotebookDocument extends Disposable {
|
||||
private _setCellOutputs(index: number, outputs: IOutputDto[]): void {
|
||||
const cell = this._cells[index];
|
||||
cell.setOutputs(outputs);
|
||||
this._emitter.emitCellOutputsChange(deepFreeze({ document: this.notebookDocument, cells: [cell.cell] }));
|
||||
this._emitter.emitCellOutputsChange(deepFreeze({ document: this.apiNotebook, cells: [cell.apiCell] }));
|
||||
}
|
||||
|
||||
private _setCellOutputItems(index: number, outputId: string, append: boolean, outputItems: IOutputItemDto[]): void {
|
||||
const cell = this._cells[index];
|
||||
cell.setOutputItems(outputId, append, outputItems);
|
||||
this._emitter.emitCellOutputsChange(deepFreeze({ document: this.notebookDocument, cells: [cell.cell] }));
|
||||
this._emitter.emitCellOutputsChange(deepFreeze({ document: this.apiNotebook, cells: [cell.apiCell] }));
|
||||
}
|
||||
|
||||
private _changeCellLanguage(index: number, newModeId: string): void {
|
||||
const cell = this._cells[index];
|
||||
if (cell.cell.document.languageId !== newModeId) {
|
||||
if (cell.apiCell.document.languageId !== newModeId) {
|
||||
this._textDocuments.$acceptModelModeChanged(cell.uri, newModeId);
|
||||
}
|
||||
}
|
||||
@@ -324,17 +332,17 @@ export class ExtHostNotebookDocument extends Disposable {
|
||||
const cell = this._cells[index];
|
||||
|
||||
const originalInternalMetadata = cell.internalMetadata;
|
||||
const originalExtMetadata = cell.cell.metadata;
|
||||
const originalExtMetadata = cell.apiCell.metadata;
|
||||
cell.setMetadata(newMetadata);
|
||||
const newExtMetadata = cell.cell.metadata;
|
||||
const newExtMetadata = cell.apiCell.metadata;
|
||||
|
||||
if (!equals(originalExtMetadata, newExtMetadata)) {
|
||||
this._emitter.emitCellMetadataChange(deepFreeze({ document: this.notebookDocument, cell: cell.cell }));
|
||||
this._emitter.emitCellMetadataChange(deepFreeze({ document: this.apiNotebook, cell: cell.apiCell }));
|
||||
}
|
||||
|
||||
if (originalInternalMetadata.runState !== newMetadata.runState) {
|
||||
const executionState = newMetadata.runState ?? extHostTypes.NotebookCellExecutionState.Idle;
|
||||
this._emitter.emitCellExecutionStateChange(deepFreeze({ document: this.notebookDocument, cell: cell.cell, executionState }));
|
||||
this._emitter.emitCellExecutionStateChange(deepFreeze({ document: this.apiNotebook, cell: cell.apiCell, executionState }));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { MainThreadNotebookEditorsShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
|
||||
import * as extHostConverter from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { CellEditType, ICellEditOperation, ICellReplaceEdit } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
@@ -58,17 +57,6 @@ class NotebookEditorCellEditBuilder implements vscode.NotebookEditorEdit {
|
||||
});
|
||||
}
|
||||
|
||||
replaceCellOutput(index: number, outputs: vscode.NotebookCellOutput[]): void {
|
||||
this._throwIfFinalized();
|
||||
this._collectedEdits.push({
|
||||
editType: CellEditType.Output,
|
||||
index,
|
||||
outputs: outputs.map(output => {
|
||||
return extHostConverter.NotebookCellOutput.from(output);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
replaceCells(from: number, to: number, cells: vscode.NotebookCellData[]): void {
|
||||
this._throwIfFinalized();
|
||||
if (from === to && cells.length === 0) {
|
||||
@@ -85,27 +73,21 @@ class NotebookEditorCellEditBuilder implements vscode.NotebookEditorEdit {
|
||||
|
||||
export class ExtHostNotebookEditor {
|
||||
|
||||
private _selections: vscode.NotebookCellRange[] = [];
|
||||
private _visibleRanges: vscode.NotebookCellRange[] = [];
|
||||
private _selections: vscode.NotebookRange[] = [];
|
||||
private _visibleRanges: vscode.NotebookRange[] = [];
|
||||
private _viewColumn?: vscode.ViewColumn;
|
||||
|
||||
private _visible: boolean = false;
|
||||
private _kernel?: vscode.NotebookKernel;
|
||||
|
||||
private readonly _hasDecorationsForKey = new Set<string>();
|
||||
private readonly _onDidDispose = new Emitter<void>();
|
||||
readonly onDidDispose: Event<void> = this._onDidDispose.event;
|
||||
|
||||
|
||||
private _editor?: vscode.NotebookEditor;
|
||||
|
||||
constructor(
|
||||
readonly id: string,
|
||||
private readonly _viewType: string,
|
||||
private readonly _proxy: MainThreadNotebookShape,
|
||||
private readonly _proxy: MainThreadNotebookEditorsShape,
|
||||
readonly notebookData: ExtHostNotebookDocument,
|
||||
visibleRanges: vscode.NotebookCellRange[],
|
||||
selections: vscode.NotebookCellRange[],
|
||||
visibleRanges: vscode.NotebookRange[],
|
||||
selections: vscode.NotebookRange[],
|
||||
viewColumn: vscode.ViewColumn | undefined
|
||||
) {
|
||||
this._selections = selections;
|
||||
@@ -113,21 +95,12 @@ export class ExtHostNotebookEditor {
|
||||
this._viewColumn = viewColumn;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this._onDidDispose.fire();
|
||||
this._onDidDispose.dispose();
|
||||
}
|
||||
|
||||
get editor(): vscode.NotebookEditor {
|
||||
get apiEditor(): vscode.NotebookEditor {
|
||||
if (!this._editor) {
|
||||
const that = this;
|
||||
this._editor = {
|
||||
get document() {
|
||||
return that.notebookData.notebookDocument;
|
||||
},
|
||||
get selection() {
|
||||
const primarySelection = that._selections[0];
|
||||
return primarySelection && that.notebookData.getCellFromIndex(primarySelection.start)?.cell;
|
||||
return that.notebookData.apiNotebook;
|
||||
},
|
||||
get selections() {
|
||||
return that._selections;
|
||||
@@ -138,24 +111,18 @@ export class ExtHostNotebookEditor {
|
||||
revealRange(range, revealType) {
|
||||
that._proxy.$tryRevealRange(
|
||||
that.id,
|
||||
extHostConverter.NotebookCellRange.from(range),
|
||||
extHostConverter.NotebookRange.from(range),
|
||||
revealType ?? extHostTypes.NotebookEditorRevealType.Default
|
||||
);
|
||||
},
|
||||
get viewColumn() {
|
||||
return that._viewColumn;
|
||||
},
|
||||
get onDidDispose() {
|
||||
return that.onDidDispose;
|
||||
},
|
||||
edit(callback) {
|
||||
const edit = new NotebookEditorCellEditBuilder(this.document.version);
|
||||
callback(edit);
|
||||
return that._applyEdit(edit.finalize());
|
||||
},
|
||||
get kernel() {
|
||||
return that._kernel;
|
||||
},
|
||||
setDecorations(decorationType, range) {
|
||||
return that.setDecorations(decorationType, range);
|
||||
}
|
||||
@@ -164,10 +131,6 @@ export class ExtHostNotebookEditor {
|
||||
return this._editor;
|
||||
}
|
||||
|
||||
_acceptKernel(kernel?: vscode.NotebookKernel) {
|
||||
this._kernel = kernel;
|
||||
}
|
||||
|
||||
get visible(): boolean {
|
||||
return this._visible;
|
||||
}
|
||||
@@ -176,14 +139,18 @@ export class ExtHostNotebookEditor {
|
||||
this._visible = value;
|
||||
}
|
||||
|
||||
_acceptVisibleRanges(value: vscode.NotebookCellRange[]): void {
|
||||
_acceptVisibleRanges(value: vscode.NotebookRange[]): void {
|
||||
this._visibleRanges = value;
|
||||
}
|
||||
|
||||
_acceptSelections(selections: vscode.NotebookCellRange[]): void {
|
||||
_acceptSelections(selections: vscode.NotebookRange[]): void {
|
||||
this._selections = selections;
|
||||
}
|
||||
|
||||
_acceptViewColumn(value: vscode.ViewColumn | undefined) {
|
||||
this._viewColumn = value;
|
||||
}
|
||||
|
||||
private _applyEdit(editData: INotebookEditData): Promise<boolean> {
|
||||
|
||||
// return when there is nothing to do
|
||||
@@ -217,10 +184,10 @@ export class ExtHostNotebookEditor {
|
||||
compressedEditsIndex++;
|
||||
}
|
||||
|
||||
return this._proxy.$tryApplyEdits(this._viewType, this.notebookData.uri, editData.documentVersionId, compressedEdits);
|
||||
return this._proxy.$tryApplyEdits(this.id, editData.documentVersionId, compressedEdits);
|
||||
}
|
||||
|
||||
setDecorations(decorationType: vscode.NotebookEditorDecorationType, range: vscode.NotebookCellRange): void {
|
||||
setDecorations(decorationType: vscode.NotebookEditorDecorationType, range: vscode.NotebookRange): void {
|
||||
if (range.isEmpty && !this._hasDecorationsForKey.has(decorationType.key)) {
|
||||
// avoid no-op call to the renderer
|
||||
return;
|
||||
@@ -233,7 +200,7 @@ export class ExtHostNotebookEditor {
|
||||
|
||||
return this._proxy.$trySetDecorations(
|
||||
this.id,
|
||||
extHostConverter.NotebookCellRange.from(range),
|
||||
extHostConverter.NotebookRange.from(range),
|
||||
decorationType.key
|
||||
);
|
||||
}
|
||||
|
||||
259
lib/vscode/src/vs/workbench/api/common/extHostNotebookKernels.ts
Normal file
259
lib/vscode/src/vs/workbench/api/common/extHostNotebookKernels.ts
Normal file
@@ -0,0 +1,259 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { ExtHostNotebookKernelsShape, IMainContext, INotebookKernelDto2, MainContext, MainThreadNotebookKernelsShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import * as vscode from 'vscode';
|
||||
import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook';
|
||||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import * as extHostTypeConverters from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { asWebviewUri } from 'vs/workbench/api/common/shared/webview';
|
||||
|
||||
interface IKernelData {
|
||||
extensionId: ExtensionIdentifier,
|
||||
controller: vscode.NotebookController;
|
||||
onDidChangeSelection: Emitter<{ selected: boolean; notebook: vscode.NotebookDocument; }>;
|
||||
onDidReceiveMessage: Emitter<{ editor: vscode.NotebookEditor, message: any }>;
|
||||
}
|
||||
|
||||
export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape {
|
||||
|
||||
private readonly _proxy: MainThreadNotebookKernelsShape;
|
||||
|
||||
private readonly _kernelData = new Map<number, IKernelData>();
|
||||
private _handlePool: number = 0;
|
||||
|
||||
constructor(
|
||||
mainContext: IMainContext,
|
||||
private readonly _initData: IExtHostInitDataService,
|
||||
private readonly _extHostNotebook: ExtHostNotebookController
|
||||
) {
|
||||
this._proxy = mainContext.getProxy(MainContext.MainThreadNotebookKernels);
|
||||
}
|
||||
|
||||
createNotebookController(extension: IExtensionDescription, id: string, viewType: string, label: string, handler?: vscode.NotebookExecuteHandler, preloads?: vscode.NotebookKernelPreload[]): vscode.NotebookController {
|
||||
|
||||
for (let data of this._kernelData.values()) {
|
||||
if (data.controller.id === id && ExtensionIdentifier.equals(extension.identifier, data.extensionId)) {
|
||||
throw new Error(`notebook controller with id '${id}' ALREADY exist`);
|
||||
}
|
||||
}
|
||||
|
||||
const handle = this._handlePool++;
|
||||
const that = this;
|
||||
|
||||
const _defaultExecutHandler = () => console.warn(`NO execute handler from notebook controller '${data.id}' of extension: '${extension.identifier}'`);
|
||||
|
||||
let isDisposed = false;
|
||||
const commandDisposables = new DisposableStore();
|
||||
|
||||
const onDidChangeSelection = new Emitter<{ selected: boolean, notebook: vscode.NotebookDocument }>();
|
||||
const onDidReceiveMessage = new Emitter<{ editor: vscode.NotebookEditor, message: any }>();
|
||||
|
||||
const data: INotebookKernelDto2 = {
|
||||
id: `${extension.identifier.value}/${id}`,
|
||||
viewType,
|
||||
extensionId: extension.identifier,
|
||||
extensionLocation: extension.extensionLocation,
|
||||
label: label || extension.identifier.value,
|
||||
preloads: preloads ? preloads.map(extHostTypeConverters.NotebookKernelPreload.from) : []
|
||||
};
|
||||
|
||||
//
|
||||
let _executeHandler: vscode.NotebookExecuteHandler = handler ?? _defaultExecutHandler;
|
||||
let _interruptHandler: vscode.NotebookInterruptHandler | undefined;
|
||||
|
||||
// todo@jrieken the selector needs to be massaged
|
||||
this._proxy.$addKernel(handle, data).catch(err => {
|
||||
// this can happen when a kernel with that ID is already registered
|
||||
console.log(err);
|
||||
isDisposed = true;
|
||||
});
|
||||
|
||||
// update: all setters write directly into the dto object
|
||||
// and trigger an update. the actual update will only happen
|
||||
// once per event loop execution
|
||||
let tokenPool = 0;
|
||||
const _update = () => {
|
||||
if (isDisposed) {
|
||||
return;
|
||||
}
|
||||
const myToken = ++tokenPool;
|
||||
Promise.resolve().then(() => {
|
||||
if (myToken === tokenPool) {
|
||||
this._proxy.$updateKernel(handle, data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const controller: vscode.NotebookController = {
|
||||
get id() { return id; },
|
||||
get viewType() { return data.viewType; },
|
||||
onDidChangeNotebookAssociation: onDidChangeSelection.event,
|
||||
get label() {
|
||||
return data.label;
|
||||
},
|
||||
set label(value) {
|
||||
data.label = value ?? extension.displayName ?? extension.name;
|
||||
_update();
|
||||
},
|
||||
get detail() {
|
||||
return data.detail ?? '';
|
||||
},
|
||||
set detail(value) {
|
||||
data.detail = value;
|
||||
_update();
|
||||
},
|
||||
get description() {
|
||||
return data.description ?? '';
|
||||
},
|
||||
set description(value) {
|
||||
data.description = value;
|
||||
_update();
|
||||
},
|
||||
get supportedLanguages() {
|
||||
return data.supportedLanguages;
|
||||
},
|
||||
set supportedLanguages(value) {
|
||||
data.supportedLanguages = value;
|
||||
_update();
|
||||
},
|
||||
get hasExecutionOrder() {
|
||||
return data.hasExecutionOrder ?? false;
|
||||
},
|
||||
set hasExecutionOrder(value) {
|
||||
data.hasExecutionOrder = value;
|
||||
_update();
|
||||
},
|
||||
get preloads() {
|
||||
return data.preloads ? data.preloads.map(extHostTypeConverters.NotebookKernelPreload.to) : [];
|
||||
},
|
||||
get executeHandler() {
|
||||
return _executeHandler;
|
||||
},
|
||||
set executeHandler(value) {
|
||||
_executeHandler = value ?? _defaultExecutHandler;
|
||||
},
|
||||
get interruptHandler() {
|
||||
return _interruptHandler;
|
||||
},
|
||||
set interruptHandler(value) {
|
||||
_interruptHandler = value;
|
||||
data.supportsInterrupt = Boolean(value);
|
||||
_update();
|
||||
},
|
||||
createNotebookCellExecutionTask(cell) {
|
||||
if (isDisposed) {
|
||||
throw new Error('notebook controller is DISPOSED');
|
||||
}
|
||||
//todo@jrieken
|
||||
return that._extHostNotebook.createNotebookCellExecution(cell.notebook.uri, cell.index, data.id)!;
|
||||
},
|
||||
dispose: () => {
|
||||
if (!isDisposed) {
|
||||
isDisposed = true;
|
||||
this._kernelData.delete(handle);
|
||||
commandDisposables.dispose();
|
||||
onDidChangeSelection.dispose();
|
||||
onDidReceiveMessage.dispose();
|
||||
this._proxy.$removeKernel(handle);
|
||||
}
|
||||
},
|
||||
// --- ipc
|
||||
onDidReceiveMessage: onDidReceiveMessage.event,
|
||||
postMessage(message, editor) {
|
||||
return that._proxy.$postMessage(handle, editor && that._extHostNotebook.getIdByEditor(editor), message);
|
||||
},
|
||||
asWebviewUri(uri: URI) {
|
||||
return asWebviewUri(that._initData.environment, String(handle), uri);
|
||||
},
|
||||
// --- priority
|
||||
updateNotebookAffinity(notebook, priority) {
|
||||
that._proxy.$updateNotebookPriority(handle, notebook.uri, priority);
|
||||
}
|
||||
};
|
||||
|
||||
this._kernelData.set(handle, { extensionId: extension.identifier, controller, onDidChangeSelection, onDidReceiveMessage });
|
||||
return controller;
|
||||
}
|
||||
|
||||
$acceptSelection(handle: number, uri: UriComponents, value: boolean): void {
|
||||
const obj = this._kernelData.get(handle);
|
||||
if (obj) {
|
||||
obj.onDidChangeSelection.fire({
|
||||
selected: value,
|
||||
notebook: this._extHostNotebook.lookupNotebookDocument(URI.revive(uri))!.apiNotebook
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async $executeCells(handle: number, uri: UriComponents, handles: number[]): Promise<void> {
|
||||
const obj = this._kernelData.get(handle);
|
||||
if (!obj) {
|
||||
// extension can dispose kernels in the meantime
|
||||
return;
|
||||
}
|
||||
const document = this._extHostNotebook.lookupNotebookDocument(URI.revive(uri));
|
||||
if (!document) {
|
||||
throw new Error('MISSING notebook');
|
||||
}
|
||||
|
||||
const cells: vscode.NotebookCell[] = [];
|
||||
for (let cellHandle of handles) {
|
||||
const cell = document.getCell(cellHandle);
|
||||
if (cell) {
|
||||
cells.push(cell.apiCell);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await obj.controller.executeHandler.call(obj.controller, cells, document.apiNotebook, obj.controller);
|
||||
} catch (err) {
|
||||
//
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
async $cancelCells(handle: number, uri: UriComponents, handles: number[]): Promise<void> {
|
||||
const obj = this._kernelData.get(handle);
|
||||
if (!obj) {
|
||||
// extension can dispose kernels in the meantime
|
||||
return;
|
||||
}
|
||||
const document = this._extHostNotebook.lookupNotebookDocument(URI.revive(uri));
|
||||
if (!document) {
|
||||
throw new Error('MISSING notebook');
|
||||
}
|
||||
if (obj.controller.interruptHandler) {
|
||||
await obj.controller.interruptHandler.call(obj.controller, document.apiNotebook);
|
||||
}
|
||||
|
||||
// we do both? interrupt and cancellation or should we be selective?
|
||||
for (let cellHandle of handles) {
|
||||
const cell = document.getCell(cellHandle);
|
||||
if (cell) {
|
||||
this._extHostNotebook.cancelOneNotebookCellExecution(cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$acceptRendererMessage(handle: number, editorId: string, message: any): void {
|
||||
const obj = this._kernelData.get(handle);
|
||||
if (!obj) {
|
||||
// extension can dispose kernels in the meantime
|
||||
return;
|
||||
}
|
||||
|
||||
const editor = this._extHostNotebook.getEditorById(editorId);
|
||||
if (!editor) {
|
||||
throw new Error(`send message for UNKNOWN editor: ${editorId}`);
|
||||
}
|
||||
|
||||
obj.onDidReceiveMessage.fire(Object.freeze({ editor: editor.apiEditor, message }));
|
||||
}
|
||||
}
|
||||
@@ -73,7 +73,7 @@ export abstract class AbstractExtHostOutputChannel extends Disposable implements
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
override dispose(): void {
|
||||
super.dispose();
|
||||
|
||||
if (!this._disposed) {
|
||||
@@ -90,7 +90,7 @@ export class ExtHostPushOutputChannel extends AbstractExtHostOutputChannel {
|
||||
super(name, false, undefined, proxy);
|
||||
}
|
||||
|
||||
append(value: string): void {
|
||||
override append(value: string): void {
|
||||
super.append(value);
|
||||
this._id.then(id => this._proxy.$append(id, value));
|
||||
this._onDidAppend.fire();
|
||||
@@ -103,7 +103,7 @@ class ExtHostLogFileOutputChannel extends AbstractExtHostOutputChannel {
|
||||
super(name, true, file, proxy);
|
||||
}
|
||||
|
||||
append(value: string): void {
|
||||
override append(value: string): void {
|
||||
throw new Error('Not supported');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,6 +259,22 @@ export class ExtHostSCMInputBox implements vscode.SourceControlInputBox {
|
||||
// noop
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
checkProposedApiEnabled(this._extension);
|
||||
|
||||
if (!this._visible) {
|
||||
this.visible = true;
|
||||
}
|
||||
|
||||
this._proxy.$setInputBoxFocus(this._sourceControlHandle);
|
||||
}
|
||||
|
||||
showValidationMessage(message: string, type: vscode.SourceControlInputBoxValidationType) {
|
||||
checkProposedApiEnabled(this._extension);
|
||||
|
||||
this._proxy.$showValidationMessage(this._sourceControlHandle, message, type as any);
|
||||
}
|
||||
|
||||
$onInputBoxValueChange(value: string): void {
|
||||
this.updateValue(value);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import type * as vscode from 'vscode';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IShellLaunchConfigDto, IShellAndArgsDto, ITerminalDimensionsDto, ITerminalLinkDto, TerminalIdentifier } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IShellAndArgsDto, ITerminalDimensionsDto, ITerminalLinkDto, TerminalIdentifier } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
@@ -19,7 +19,7 @@ import { serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/ter
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
import { ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalLaunchError, TerminalShellType } from 'vs/platform/terminal/common/terminal';
|
||||
import { IShellLaunchConfigDto, ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalEnvironment, ITerminalLaunchError, TerminalShellType } from 'vs/platform/terminal/common/terminal';
|
||||
import { TerminalDataBufferer } from 'vs/platform/terminal/common/terminalDataBuffering';
|
||||
import { ITerminalProfile } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
|
||||
@@ -117,7 +117,9 @@ export class ExtHostTerminal {
|
||||
shellPath?: string,
|
||||
shellArgs?: string[] | string,
|
||||
cwd?: string | URI,
|
||||
env?: { [key: string]: string | null },
|
||||
env?: ITerminalEnvironment,
|
||||
icon?: string,
|
||||
initialText?: string,
|
||||
waitOnExit?: boolean,
|
||||
strictEnv?: boolean,
|
||||
hideFromUser?: boolean,
|
||||
@@ -127,7 +129,7 @@ export class ExtHostTerminal {
|
||||
if (typeof this._id !== 'string') {
|
||||
throw new Error('Terminal has already been created');
|
||||
}
|
||||
await this._proxy.$createTerminal(this._id, { name: this._name, shellPath, shellArgs, cwd, env, waitOnExit, strictEnv, hideFromUser, isFeatureTerminal, isExtensionOwnedTerminal });
|
||||
await this._proxy.$createTerminal(this._id, { name: this._name, shellPath, shellArgs, cwd, env, icon, initialText, waitOnExit, strictEnv, hideFromUser, isFeatureTerminal, isExtensionOwnedTerminal });
|
||||
}
|
||||
|
||||
public async createExtensionTerminal(): Promise<number> {
|
||||
@@ -225,6 +227,10 @@ export class ExtHostPseudoterminal implements ITerminalChildProcess {
|
||||
}
|
||||
}
|
||||
|
||||
async processBinary(data: string): Promise<void> {
|
||||
// No-op, processBinary is not supported in extextion owned terminals.
|
||||
}
|
||||
|
||||
acknowledgeDataEvent(charCount: number): void {
|
||||
// No-op, flow control is not supported in extension owned terminals. If this is ever
|
||||
// implemented it will need new pause and resume VS Code APIs.
|
||||
@@ -328,9 +334,8 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
|
||||
public abstract createTerminalFromOptions(options: vscode.TerminalOptions): vscode.Terminal;
|
||||
public abstract getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string;
|
||||
public abstract getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string;
|
||||
public abstract $getAvailableProfiles(quickLaunchOnly: boolean): Promise<ITerminalProfile[]>;
|
||||
public abstract $getAvailableProfiles(configuredProfilesOnly: boolean): Promise<ITerminalProfile[]>;
|
||||
public abstract $getDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto>;
|
||||
public abstract $acceptWorkspacePermissionsChanged(isAllowed: boolean): void;
|
||||
|
||||
public createExtensionTerminal(options: vscode.ExtensionTerminalOptions): vscode.Terminal {
|
||||
const terminal = new ExtHostTerminal(this._proxy, generateUuid(), options, options.name);
|
||||
@@ -771,23 +776,18 @@ export class WorkerExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
}
|
||||
|
||||
public getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string {
|
||||
// Return the empty string to avoid throwing
|
||||
return '';
|
||||
throw new NotSupportedError();
|
||||
}
|
||||
|
||||
public getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string {
|
||||
throw new NotSupportedError();
|
||||
}
|
||||
|
||||
public $getAvailableProfiles(quickLaunchOnly: boolean): Promise<ITerminalProfile[]> {
|
||||
public $getAvailableProfiles(configuredProfilesOnly: boolean): Promise<ITerminalProfile[]> {
|
||||
throw new NotSupportedError();
|
||||
}
|
||||
|
||||
public async $getDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto> {
|
||||
throw new NotSupportedError();
|
||||
}
|
||||
|
||||
public $acceptWorkspacePermissionsChanged(isAllowed: boolean): void {
|
||||
// No-op for web worker ext host as workspace permissions aren't used
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { mapFind } from 'vs/base/common/arrays';
|
||||
import { disposableTimeout } from 'vs/base/common/async';
|
||||
import { Barrier, DeferredPromise, disposableTimeout, isThenable } from 'vs/base/common/async';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { once } from 'vs/base/common/functional';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { deepFreeze } from 'vs/base/common/objects';
|
||||
import { isDefined } from 'vs/base/common/types';
|
||||
@@ -17,34 +19,40 @@ import { ExtHostTestingResource, ExtHostTestingShape, MainContext, MainThreadTes
|
||||
import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData';
|
||||
import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { ExtHostTestItemEventType, getPrivateApiFor } from 'vs/workbench/api/common/extHostTestingPrivateApi';
|
||||
import * as Convert from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { Disposable, TestItem as TestItemImpl, TestItemHookProperty } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { Disposable, TestItemImpl } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { OwnedTestCollection, SingleUseTestCollection, TestPosition } from 'vs/workbench/contrib/testing/common/ownedTestCollection';
|
||||
import { AbstractIncrementalTestCollection, IncrementalChangeCollector, IncrementalTestCollectionItem, InternalTestItem, ISerializedTestResults, RunTestForProviderRequest, TestDiffOpType, TestIdWithSrc, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { AbstractIncrementalTestCollection, IncrementalChangeCollector, IncrementalTestCollectionItem, InternalTestItem, ISerializedTestResults, ITestItem, RunTestForProviderRequest, TestDiffOpType, TestIdWithSrc, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import type * as vscode from 'vscode';
|
||||
|
||||
const getTestSubscriptionKey = (resource: ExtHostTestingResource, uri: URI) => `${resource}:${uri.toString()}`;
|
||||
|
||||
export class ExtHostTesting implements ExtHostTestingShape {
|
||||
private readonly resultsChangedEmitter = new Emitter<void>();
|
||||
private readonly providers = new Map<string, vscode.TestProvider>();
|
||||
private readonly controllers = new Map<string, {
|
||||
extensionId: string,
|
||||
instance: vscode.TestController<unknown>
|
||||
}>();
|
||||
private readonly proxy: MainThreadTestingShape;
|
||||
private readonly ownedTests = new OwnedTestCollection();
|
||||
private readonly testSubscriptions = new Map<string, {
|
||||
private readonly runQueue: TestRunQueue;
|
||||
private readonly testControllers = new Map<string, {
|
||||
collection: SingleUseTestCollection;
|
||||
store: IDisposable;
|
||||
subscribeFn: (id: string, provider: vscode.TestProvider) => void;
|
||||
subscribeFn: (id: string, provider: vscode.TestController<unknown>) => void;
|
||||
}>();
|
||||
|
||||
private workspaceObservers: WorkspaceFolderTestObserverFactory;
|
||||
private textDocumentObservers: TextDocumentTestObserverFactory;
|
||||
|
||||
public onResultsChanged = this.resultsChangedEmitter.event;
|
||||
public results: ReadonlyArray<vscode.TestResults> = [];
|
||||
public results: ReadonlyArray<vscode.TestRunResult> = [];
|
||||
|
||||
constructor(@IExtHostRpcService rpc: IExtHostRpcService, @IExtHostDocumentsAndEditors private readonly documents: IExtHostDocumentsAndEditors, @IExtHostWorkspace private readonly workspace: IExtHostWorkspace) {
|
||||
this.proxy = rpc.getProxy(MainContext.MainThreadTesting);
|
||||
this.runQueue = new TestRunQueue(this.proxy);
|
||||
this.workspaceObservers = new WorkspaceFolderTestObserverFactory(this.proxy);
|
||||
this.textDocumentObservers = new TextDocumentTestObserverFactory(this.proxy, documents);
|
||||
}
|
||||
@@ -52,22 +60,22 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
/**
|
||||
* Implements vscode.test.registerTestProvider
|
||||
*/
|
||||
public registerTestProvider<T extends vscode.TestItem>(provider: vscode.TestProvider<T>): vscode.Disposable {
|
||||
const providerId = generateUuid();
|
||||
this.providers.set(providerId, provider);
|
||||
this.proxy.$registerTestProvider(providerId);
|
||||
public registerTestController<T>(extensionId: string, controller: vscode.TestController<T>): vscode.Disposable {
|
||||
const controllerId = generateUuid();
|
||||
this.controllers.set(controllerId, { instance: controller, extensionId });
|
||||
this.proxy.$registerTestController(controllerId);
|
||||
|
||||
// give the ext a moment to register things rather than synchronously invoking within activate()
|
||||
const toSubscribe = [...this.testSubscriptions.keys()];
|
||||
const toSubscribe = [...this.testControllers.keys()];
|
||||
setTimeout(() => {
|
||||
for (const subscription of toSubscribe) {
|
||||
this.testSubscriptions.get(subscription)?.subscribeFn(providerId, provider);
|
||||
this.testControllers.get(subscription)?.subscribeFn(controllerId, controller);
|
||||
}
|
||||
}, 0);
|
||||
|
||||
return new Disposable(() => {
|
||||
this.providers.delete(providerId);
|
||||
this.proxy.$unregisterTestProvider(providerId);
|
||||
this.controllers.delete(controllerId);
|
||||
this.proxy.$unregisterTestController(controllerId);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -88,8 +96,8 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
/**
|
||||
* Implements vscode.test.runTests
|
||||
*/
|
||||
public async runTests(req: vscode.TestRunOptions<vscode.TestItem>, token = CancellationToken.None) {
|
||||
const testListToProviders = (tests: ReadonlyArray<vscode.TestItem>) =>
|
||||
public async runTests(req: vscode.TestRunRequest<unknown>, token = CancellationToken.None) {
|
||||
const testListToProviders = (tests: ReadonlyArray<vscode.TestItem<unknown>>) =>
|
||||
tests
|
||||
.map(this.getInternalTestForReference, this)
|
||||
.filter(isDefined)
|
||||
@@ -103,10 +111,10 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements vscode.test.publishTestResults
|
||||
* Implements vscode.test.createTestRun
|
||||
*/
|
||||
public publishExtensionProvidedResults(results: vscode.TestResults, persist: boolean): void {
|
||||
this.proxy.$publishExtensionProvidedResults(Convert.TestResults.from(generateUuid(), results), persist);
|
||||
public createTestRun<T>(extensionId: string, request: vscode.TestRunRequest<T>, name: string | undefined, persist = true): vscode.TestRun<T> {
|
||||
return this.runQueue.createTestRun(extensionId, request, name, persist);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,12 +140,12 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
public async $subscribeToTests(resource: ExtHostTestingResource, uriComponents: UriComponents) {
|
||||
const uri = URI.revive(uriComponents);
|
||||
const subscriptionKey = getTestSubscriptionKey(resource, uri);
|
||||
if (this.testSubscriptions.has(subscriptionKey)) {
|
||||
if (this.testControllers.has(subscriptionKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cancellation = new CancellationTokenSource();
|
||||
let method: undefined | ((p: vscode.TestProvider) => vscode.ProviderResult<vscode.TestItem>);
|
||||
let method: undefined | ((p: vscode.TestController<unknown>) => vscode.ProviderResult<vscode.TestItem<unknown>>);
|
||||
if (resource === ExtHostTestingResource.TextDocument) {
|
||||
let document = this.documents.getDocument(uri);
|
||||
|
||||
@@ -156,14 +164,14 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
|
||||
if (document) {
|
||||
const folder = await this.workspace.getWorkspaceFolder2(uri, false);
|
||||
method = p => p.provideDocumentTestRoot
|
||||
? p.provideDocumentTestRoot(document!.document, cancellation.token)
|
||||
method = p => p.createDocumentTestRoot
|
||||
? p.createDocumentTestRoot(document!.document, cancellation.token)
|
||||
: createDefaultDocumentTestRoot(p, document!.document, folder, cancellation.token);
|
||||
}
|
||||
} else {
|
||||
const folder = await this.workspace.getWorkspaceFolder2(uri, false);
|
||||
if (folder) {
|
||||
method = p => p.provideWorkspaceTestRoot(folder, cancellation.token);
|
||||
method = p => p.createWorkspaceTestRoot(folder, cancellation.token);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +179,7 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
return;
|
||||
}
|
||||
|
||||
const subscribeFn = async (id: string, provider: vscode.TestProvider) => {
|
||||
const subscribeFn = async (id: string, provider: vscode.TestController<unknown>) => {
|
||||
try {
|
||||
const root = await method!(provider);
|
||||
if (root) {
|
||||
@@ -186,15 +194,16 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
const collection = disposable.add(this.ownedTests.createForHierarchy(
|
||||
diff => this.proxy.$publishDiff(resource, uriComponents, diff)));
|
||||
disposable.add(toDisposable(() => cancellation.dispose(true)));
|
||||
for (const [id, provider] of this.providers) {
|
||||
subscribeFn(id, provider);
|
||||
const subscribes: Promise<void>[] = [];
|
||||
for (const [id, controller] of this.controllers) {
|
||||
subscribes.push(subscribeFn(id, controller.instance));
|
||||
}
|
||||
|
||||
// note: we don't increment the root count initially -- this is done by the
|
||||
// note: we don't increment the count initially -- this is done by the
|
||||
// main thread, incrementing once per extension host. We just push the
|
||||
// diff to signal that roots have been discovered.
|
||||
collection.pushDiff([TestDiffOpType.DeltaRootsComplete, -1]);
|
||||
this.testSubscriptions.set(subscriptionKey, { store: disposable, collection, subscribeFn });
|
||||
Promise.all(subscribes).then(() => collection.pushDiff([TestDiffOpType.IncrementPendingExtHosts, -1]));
|
||||
this.testControllers.set(subscriptionKey, { store: disposable, collection, subscribeFn });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -203,7 +212,7 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
* @override
|
||||
*/
|
||||
public async $expandTest(test: TestIdWithSrc, levels: number) {
|
||||
const sub = mapFind(this.testSubscriptions.values(), s => s.collection.treeId === test.src.tree ? s : undefined);
|
||||
const sub = mapFind(this.testControllers.values(), s => s.collection.treeId === test.src.tree ? s : undefined);
|
||||
await sub?.collection.expand(test.testId, levels < 0 ? Infinity : levels);
|
||||
this.flushCollectionDiffs();
|
||||
}
|
||||
@@ -215,8 +224,8 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
public $unsubscribeFromTests(resource: ExtHostTestingResource, uriComponents: UriComponents) {
|
||||
const uri = URI.revive(uriComponents);
|
||||
const subscriptionKey = getTestSubscriptionKey(resource, uri);
|
||||
this.testSubscriptions.get(subscriptionKey)?.store.dispose();
|
||||
this.testSubscriptions.delete(subscriptionKey);
|
||||
this.testControllers.get(subscriptionKey)?.store.dispose();
|
||||
this.testControllers.delete(subscriptionKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -237,14 +246,14 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
* providers to be run.
|
||||
* @override
|
||||
*/
|
||||
public async $runTestsForProvider(req: RunTestForProviderRequest, cancellation: CancellationToken): Promise<void> {
|
||||
const provider = this.providers.get(req.tests[0].src.provider);
|
||||
if (!provider) {
|
||||
public async $runTestsForProvider(req: RunTestForProviderRequest, token: CancellationToken): Promise<void> {
|
||||
const controller = this.controllers.get(req.tests[0].src.controller);
|
||||
if (!controller) {
|
||||
return;
|
||||
}
|
||||
|
||||
const includeTests = req.tests
|
||||
.map(({ testId, src }) => this.ownedTests.getTestById(testId, src.tree))
|
||||
.map(({ testId, src }) => this.ownedTests.getTestById(testId, src?.tree))
|
||||
.filter(isDefined)
|
||||
.map(([_tree, test]) => test);
|
||||
|
||||
@@ -259,35 +268,19 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await provider.runTests({
|
||||
setState: (test, state) => {
|
||||
// for test providers that don't support excluding natively,
|
||||
// make sure not to report excluded result otherwise summaries will be off.
|
||||
for (const [tree, exclude] of excludeTests) {
|
||||
const e = tree.comparePositions(exclude, test.id);
|
||||
if (e === TestPosition.IsChild || e === TestPosition.IsSame) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const publicReq: vscode.TestRunRequest<unknown> = {
|
||||
tests: includeTests.map(t => TestItemFilteredWrapper.unwrap(t.actual)),
|
||||
exclude: excludeTests.map(([, t]) => TestItemFilteredWrapper.unwrap(t.actual)),
|
||||
debug: req.debug,
|
||||
};
|
||||
|
||||
this.flushCollectionDiffs();
|
||||
this.proxy.$updateTestStateInRun(req.runId, test.id, Convert.TestState.from(state));
|
||||
},
|
||||
tests: includeTests.map(t => TestItemFilteredWrapper.unwrap(t.actual)),
|
||||
exclude: excludeTests.map(([, t]) => TestItemFilteredWrapper.unwrap(t.actual)),
|
||||
debug: req.debug,
|
||||
}, cancellation);
|
||||
|
||||
for (const { collection } of this.testSubscriptions.values()) {
|
||||
collection.flushDiff(); // ensure all states are updated
|
||||
}
|
||||
|
||||
return;
|
||||
} catch (e) {
|
||||
console.error(e); // so it appears to attached debuggers
|
||||
throw e;
|
||||
}
|
||||
await this.runQueue.enqueueRun({
|
||||
dto: TestRunDto.fromInternal(req),
|
||||
token,
|
||||
extensionId: controller.extensionId,
|
||||
req: publicReq,
|
||||
doRun: () => controller!.instance.runTests(publicReq, token)
|
||||
});
|
||||
}
|
||||
|
||||
public $lookupTest(req: TestIdWithSrc): Promise<InternalTestItem | undefined> {
|
||||
@@ -305,7 +298,7 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
* main thread is updated.
|
||||
*/
|
||||
private flushCollectionDiffs() {
|
||||
for (const { collection } of this.testSubscriptions.values()) {
|
||||
for (const { collection } of this.testControllers.values()) {
|
||||
collection.flushDiff();
|
||||
}
|
||||
}
|
||||
@@ -313,18 +306,236 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
/**
|
||||
* Gets the internal test item associated with the reference from the extension.
|
||||
*/
|
||||
private getInternalTestForReference(test: vscode.TestItem) {
|
||||
private getInternalTestForReference(test: vscode.TestItem<unknown>) {
|
||||
// Find workspace items first, then owned tests, then document tests.
|
||||
// If a test instance exists in both the workspace and document, prefer
|
||||
// the workspace because it's less ephemeral.
|
||||
return this.workspaceObservers.getMirroredTestDataByReference(test)
|
||||
?? mapFind(this.testSubscriptions.values(), c => c.collection.getTestByReference(test))
|
||||
?? mapFind(this.testControllers.values(), c => c.collection.getTestByReference(test))
|
||||
?? this.textDocumentObservers.getMirroredTestDataByReference(test);
|
||||
}
|
||||
}
|
||||
|
||||
export const createDefaultDocumentTestRoot = async <T extends vscode.TestItem>(
|
||||
provider: vscode.TestProvider<T>,
|
||||
/**
|
||||
* Queues runs for a single extension and provides the currently-executing
|
||||
* run so that `createTestRun` can be properly correlated.
|
||||
*/
|
||||
class TestRunQueue {
|
||||
private readonly state = new Map</* extensionId */ string, {
|
||||
current: {
|
||||
publicReq: vscode.TestRunRequest<unknown>,
|
||||
factory: (name: string | undefined) => TestRunTask<unknown>,
|
||||
},
|
||||
queue: (() => (Promise<void> | void))[];
|
||||
}>();
|
||||
|
||||
constructor(private readonly proxy: MainThreadTestingShape) { }
|
||||
|
||||
/**
|
||||
* Registers and enqueues a test run. `doRun` will be called when an
|
||||
* invokation to {@link TestController.runTests} should be called.
|
||||
*/
|
||||
public enqueueRun(opts: {
|
||||
extensionId: string,
|
||||
req: vscode.TestRunRequest<unknown>,
|
||||
dto: TestRunDto,
|
||||
token: CancellationToken,
|
||||
doRun: () => Thenable<void> | void,
|
||||
},
|
||||
) {
|
||||
let record = this.state.get(opts.extensionId);
|
||||
if (!record) {
|
||||
record = { queue: [], current: undefined as any };
|
||||
this.state.set(opts.extensionId, record);
|
||||
}
|
||||
|
||||
const deferred = new DeferredPromise<void>();
|
||||
const runner = () => {
|
||||
const tasks: TestRunTask<unknown>[] = [];
|
||||
const shared = new Set<string>();
|
||||
record!.current = {
|
||||
publicReq: opts.req,
|
||||
factory: name => {
|
||||
const task = new TestRunTask(name, opts.dto, shared, this.proxy);
|
||||
tasks.push(task);
|
||||
opts.token.onCancellationRequested(() => task.end());
|
||||
return task;
|
||||
},
|
||||
};
|
||||
|
||||
this.invokeRunner(opts.extensionId, opts.dto.id, opts.doRun, tasks).finally(() => deferred.complete());
|
||||
};
|
||||
|
||||
record.queue.push(runner);
|
||||
if (record.queue.length === 1) {
|
||||
runner();
|
||||
}
|
||||
|
||||
return deferred.p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the public `createTestRun` API.
|
||||
*/
|
||||
public createTestRun<T>(extensionId: string, request: vscode.TestRunRequest<T>, name: string | undefined, persist: boolean): vscode.TestRun<T> {
|
||||
const state = this.state.get(extensionId);
|
||||
// If the request is for the currently-executing `runTests`, then correlate
|
||||
// it to that existing run. Otherwise return a new, detached run.
|
||||
if (state?.current.publicReq === request) {
|
||||
return state.current.factory(name);
|
||||
}
|
||||
|
||||
const dto = TestRunDto.fromPublic(request);
|
||||
const task = new TestRunTask(name, dto, new Set(), this.proxy);
|
||||
this.proxy.$startedExtensionTestRun({
|
||||
debug: request.debug,
|
||||
exclude: request.exclude?.map(t => t.id) ?? [],
|
||||
id: dto.id,
|
||||
tests: request.tests.map(t => t.id),
|
||||
persist: persist
|
||||
});
|
||||
task.onEnd.wait().then(() => this.proxy.$finishedExtensionTestRun(dto.id));
|
||||
return task;
|
||||
}
|
||||
|
||||
private invokeRunner<T>(extensionId: string, runId: string, fn: () => Thenable<void> | void, tasks: TestRunTask<T>[]): Promise<void> {
|
||||
try {
|
||||
const res = fn();
|
||||
if (isThenable(res)) {
|
||||
return res
|
||||
.then(() => this.handleInvokeResult(extensionId, runId, tasks, undefined))
|
||||
.catch(err => this.handleInvokeResult(extensionId, runId, tasks, err));
|
||||
} else {
|
||||
return this.handleInvokeResult(extensionId, runId, tasks, undefined);
|
||||
}
|
||||
} catch (e) {
|
||||
return this.handleInvokeResult(extensionId, runId, tasks, e);
|
||||
}
|
||||
}
|
||||
|
||||
private async handleInvokeResult<T>(extensionId: string, runId: string, tasks: TestRunTask<T>[], error?: Error) {
|
||||
const record = this.state.get(extensionId);
|
||||
if (!record) {
|
||||
return;
|
||||
}
|
||||
|
||||
record.queue.shift();
|
||||
if (record.queue.length > 0) {
|
||||
record.queue[0]();
|
||||
} else {
|
||||
this.state.delete(extensionId);
|
||||
}
|
||||
|
||||
await Promise.all(tasks.map(t => t.onEnd.wait()));
|
||||
}
|
||||
}
|
||||
|
||||
class TestRunDto {
|
||||
public static fromPublic(request: vscode.TestRunRequest<unknown>) {
|
||||
return new TestRunDto(
|
||||
generateUuid(),
|
||||
new Set(request.tests.map(t => t.id)),
|
||||
new Set(request.exclude?.map(t => t.id) ?? Iterable.empty()),
|
||||
);
|
||||
}
|
||||
|
||||
public static fromInternal(request: RunTestForProviderRequest) {
|
||||
return new TestRunDto(
|
||||
request.runId,
|
||||
new Set(request.tests.map(t => t.testId)),
|
||||
new Set(request.excludeExtIds),
|
||||
);
|
||||
}
|
||||
|
||||
constructor(
|
||||
public readonly id: string,
|
||||
private readonly include: ReadonlySet<string>,
|
||||
private readonly exclude: ReadonlySet<string>,
|
||||
) { }
|
||||
|
||||
public isIncluded(test: vscode.TestItem<unknown>) {
|
||||
for (let t: vscode.TestItem<unknown> | undefined = test; t; t = t.parent) {
|
||||
if (this.include.has(t.id)) {
|
||||
return true;
|
||||
} else if (this.exclude.has(t.id)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class TestRunTask<T> implements vscode.TestRun<T> {
|
||||
readonly #proxy: MainThreadTestingShape;
|
||||
readonly #req: TestRunDto;
|
||||
readonly #taskId = generateUuid();
|
||||
readonly #sharedIds: Set<string>;
|
||||
public readonly onEnd = new Barrier();
|
||||
|
||||
constructor(
|
||||
public readonly name: string | undefined,
|
||||
dto: TestRunDto,
|
||||
sharedTestIds: Set<string>,
|
||||
proxy: MainThreadTestingShape,
|
||||
) {
|
||||
this.#proxy = proxy;
|
||||
this.#req = dto;
|
||||
this.#sharedIds = sharedTestIds;
|
||||
proxy.$startedTestRunTask(dto.id, { id: this.#taskId, name, running: true });
|
||||
}
|
||||
|
||||
setState(test: vscode.TestItem<T>, state: vscode.TestResultState, duration?: number): void {
|
||||
if (this.#req.isIncluded(test)) {
|
||||
this.ensureTestIsKnown(test);
|
||||
this.#proxy.$updateTestStateInRun(this.#req.id, this.#taskId, test.id, state, duration);
|
||||
}
|
||||
}
|
||||
|
||||
appendMessage(test: vscode.TestItem<T>, message: vscode.TestMessage): void {
|
||||
if (this.#req.isIncluded(test)) {
|
||||
this.ensureTestIsKnown(test);
|
||||
this.#proxy.$appendTestMessageInRun(this.#req.id, this.#taskId, test.id, Convert.TestMessage.from(message));
|
||||
}
|
||||
}
|
||||
|
||||
appendOutput(output: string): void {
|
||||
this.#proxy.$appendOutputToRun(this.#req.id, this.#taskId, VSBuffer.fromString(output));
|
||||
}
|
||||
|
||||
end(): void {
|
||||
this.#proxy.$finishedTestRunTask(this.#req.id, this.#taskId);
|
||||
this.onEnd.open();
|
||||
}
|
||||
|
||||
private ensureTestIsKnown(test: vscode.TestItem<T>) {
|
||||
const sent = this.#sharedIds;
|
||||
if (sent.has(test.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const chain: ITestItem[] = [];
|
||||
while (true) {
|
||||
chain.unshift(Convert.TestItem.from(test));
|
||||
|
||||
if (sent.has(test.id)) {
|
||||
break;
|
||||
}
|
||||
|
||||
sent.add(test.id);
|
||||
if (!test.parent) {
|
||||
break;
|
||||
}
|
||||
|
||||
test = test.parent;
|
||||
}
|
||||
|
||||
this.#proxy.$addTestsToRun(this.#req.id, chain);
|
||||
}
|
||||
}
|
||||
|
||||
export const createDefaultDocumentTestRoot = async <T>(
|
||||
provider: vscode.TestController<T>,
|
||||
document: vscode.TextDocument,
|
||||
folder: vscode.WorkspaceFolder | undefined,
|
||||
token: CancellationToken,
|
||||
@@ -333,7 +544,7 @@ export const createDefaultDocumentTestRoot = async <T extends vscode.TestItem>(
|
||||
return;
|
||||
}
|
||||
|
||||
const root = await provider.provideWorkspaceTestRoot(folder, token);
|
||||
const root = await provider.createWorkspaceTestRoot(folder, token);
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
@@ -342,32 +553,35 @@ export const createDefaultDocumentTestRoot = async <T extends vscode.TestItem>(
|
||||
TestItemFilteredWrapper.removeFilter(document);
|
||||
});
|
||||
|
||||
return TestItemFilteredWrapper.getWrapperForTestItem(root, document);
|
||||
const wrapper = TestItemFilteredWrapper.getWrapperForTestItem(root, document);
|
||||
wrapper.refreshMatch();
|
||||
return wrapper;
|
||||
};
|
||||
|
||||
/*
|
||||
* A class which wraps a vscode.TestItem that provides the ability to filter a TestItem's children
|
||||
* to only the children that are located in a certain vscode.Uri.
|
||||
*/
|
||||
export class TestItemFilteredWrapper<T extends vscode.TestItem = vscode.TestItem> extends TestItemImpl {
|
||||
private static wrapperMap = new WeakMap<vscode.TextDocument, WeakMap<vscode.TestItem, TestItemFilteredWrapper>>();
|
||||
export class TestItemFilteredWrapper extends TestItemImpl {
|
||||
private static wrapperMap = new WeakMap<vscode.TextDocument, WeakMap<vscode.TestItem<unknown>, TestItemFilteredWrapper>>();
|
||||
|
||||
public static removeFilter(document: vscode.TextDocument): void {
|
||||
this.wrapperMap.delete(document);
|
||||
}
|
||||
|
||||
// Wraps the TestItem specified in a TestItemFilteredWrapper and pulls from a cache if it already exists.
|
||||
public static getWrapperForTestItem<T extends vscode.TestItem>(
|
||||
item: T,
|
||||
public static getWrapperForTestItem(
|
||||
item: vscode.TestItem<unknown>,
|
||||
filterDocument: vscode.TextDocument,
|
||||
parent?: TestItemFilteredWrapper<T>,
|
||||
): TestItemFilteredWrapper<T> {
|
||||
parent?: TestItemFilteredWrapper,
|
||||
): TestItemFilteredWrapper {
|
||||
let innerMap = this.wrapperMap.get(filterDocument);
|
||||
if (innerMap?.has(item)) {
|
||||
return innerMap.get(item) as TestItemFilteredWrapper<T>;
|
||||
return innerMap.get(item) as TestItemFilteredWrapper;
|
||||
}
|
||||
|
||||
if (!innerMap) {
|
||||
innerMap = new WeakMap<vscode.TestItem, TestItemFilteredWrapper>();
|
||||
innerMap = new WeakMap();
|
||||
this.wrapperMap.set(filterDocument, innerMap);
|
||||
}
|
||||
|
||||
@@ -380,8 +594,8 @@ export class TestItemFilteredWrapper<T extends vscode.TestItem = vscode.TestItem
|
||||
* If the TestItem is wrapped, returns the unwrapped item provided
|
||||
* by the extension.
|
||||
*/
|
||||
public static unwrap(item: vscode.TestItem) {
|
||||
return item instanceof TestItemFilteredWrapper ? item.actual : item;
|
||||
public static unwrap<T>(item: vscode.TestItem<T> | TestItemFilteredWrapper) {
|
||||
return item instanceof TestItemFilteredWrapper ? item.actual as vscode.TestItem<T> : item;
|
||||
}
|
||||
|
||||
private _cachedMatchesFilter: boolean | undefined;
|
||||
@@ -398,21 +612,39 @@ export class TestItemFilteredWrapper<T extends vscode.TestItem = vscode.TestItem
|
||||
}
|
||||
|
||||
private constructor(
|
||||
public readonly actual: T,
|
||||
public readonly actual: vscode.TestItem<unknown>,
|
||||
private filterDocument: vscode.TextDocument,
|
||||
public readonly parent?: TestItemFilteredWrapper<T>,
|
||||
public readonly actualParent?: TestItemFilteredWrapper,
|
||||
) {
|
||||
super(actual.id, actual.label, actual.uri, actual.expandable);
|
||||
super(actual.id, actual.label, actual.uri, undefined);
|
||||
if (!(actual instanceof TestItemImpl)) {
|
||||
throw new Error(`TestItems provided to the VS Code API must extend \`vscode.TestItem\`, but ${actual.id} did not`);
|
||||
}
|
||||
|
||||
(actual as TestItemImpl)[TestItemHookProperty] = {
|
||||
setProp: (key, value) => (this as Record<string, unknown>)[key] = value,
|
||||
created: child => TestItemFilteredWrapper.getWrapperForTestItem(child, this.filterDocument, this).refreshMatch(),
|
||||
invalidate: () => this.invalidate(),
|
||||
delete: child => this.children.delete(child),
|
||||
};
|
||||
this.debuggable = actual.debuggable;
|
||||
this.runnable = actual.runnable;
|
||||
this.description = actual.description;
|
||||
this.error = actual.error;
|
||||
this.status = actual.status;
|
||||
this.range = actual.range;
|
||||
this.resolveHandler = actual.resolveHandler;
|
||||
|
||||
const wrapperApi = getPrivateApiFor(this);
|
||||
const actualApi = getPrivateApiFor(actual);
|
||||
actualApi.bus.event(evt => {
|
||||
switch (evt[0]) {
|
||||
case ExtHostTestItemEventType.SetProp:
|
||||
(this as Record<string, unknown>)[evt[1]] = evt[2];
|
||||
break;
|
||||
case ExtHostTestItemEventType.NewChild:
|
||||
const wrapper = TestItemFilteredWrapper.getWrapperForTestItem(evt[1], this.filterDocument, this);
|
||||
getPrivateApiFor(wrapper).parent = actual;
|
||||
wrapper.refreshMatch();
|
||||
break;
|
||||
default:
|
||||
wrapperApi.bus.fire(evt);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -420,17 +652,17 @@ export class TestItemFilteredWrapper<T extends vscode.TestItem = vscode.TestItem
|
||||
* if the test itself has a location that matches, or if any of its
|
||||
* children do.
|
||||
*/
|
||||
private refreshMatch() {
|
||||
public refreshMatch() {
|
||||
const didMatch = this._cachedMatchesFilter;
|
||||
|
||||
// The `children` of the wrapper only include the children who match the
|
||||
// filter. Synchronize them.
|
||||
for (const rawChild of this.actual.children) {
|
||||
for (const rawChild of this.actual.children.values()) {
|
||||
const wrapper = TestItemFilteredWrapper.getWrapperForTestItem(rawChild, this.filterDocument, this);
|
||||
if (wrapper.hasNodeMatchingFilter) {
|
||||
this.children.add(wrapper);
|
||||
} else {
|
||||
this.children.delete(wrapper);
|
||||
if (!wrapper.hasNodeMatchingFilter) {
|
||||
wrapper.dispose();
|
||||
} else if (!this.children.has(wrapper.id)) {
|
||||
this.addChild(wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -438,18 +670,26 @@ export class TestItemFilteredWrapper<T extends vscode.TestItem = vscode.TestItem
|
||||
this._cachedMatchesFilter = nowMatches;
|
||||
|
||||
if (nowMatches !== didMatch) {
|
||||
this.parent?.refreshMatch();
|
||||
this.actualParent?.refreshMatch();
|
||||
}
|
||||
|
||||
return this._cachedMatchesFilter;
|
||||
}
|
||||
|
||||
public override dispose() {
|
||||
if (this.actualParent) {
|
||||
getPrivateApiFor(this.actualParent).children.delete(this.id);
|
||||
}
|
||||
|
||||
getPrivateApiFor(this).bus.fire([ExtHostTestItemEventType.Disposed]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
interface MirroredCollectionTestItem extends IncrementalTestCollectionItem {
|
||||
revived: vscode.TestItem;
|
||||
revived: vscode.TestItem<never>;
|
||||
depth: number;
|
||||
}
|
||||
|
||||
@@ -464,21 +704,21 @@ class MirroredChangeCollector extends IncrementalChangeCollector<MirroredCollect
|
||||
return this.added.size === 0 && this.removed.size === 0 && this.updated.size === 0;
|
||||
}
|
||||
|
||||
constructor(private readonly emitter: Emitter<vscode.TestChangeEvent>) {
|
||||
constructor(private readonly emitter: Emitter<vscode.TestsChangeEvent>) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public add(node: MirroredCollectionTestItem): void {
|
||||
public override add(node: MirroredCollectionTestItem): void {
|
||||
this.added.add(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public update(node: MirroredCollectionTestItem): void {
|
||||
public override update(node: MirroredCollectionTestItem): void {
|
||||
Object.assign(node.revived, Convert.TestItem.toPlain(node.item));
|
||||
if (!this.added.has(node)) {
|
||||
this.updated.add(node);
|
||||
@@ -488,7 +728,7 @@ class MirroredChangeCollector extends IncrementalChangeCollector<MirroredCollect
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public remove(node: MirroredCollectionTestItem): void {
|
||||
public override remove(node: MirroredCollectionTestItem): void {
|
||||
if (this.added.has(node)) {
|
||||
this.added.delete(node);
|
||||
return;
|
||||
@@ -507,7 +747,7 @@ class MirroredChangeCollector extends IncrementalChangeCollector<MirroredCollect
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public getChangeEvent(): vscode.TestChangeEvent {
|
||||
public getChangeEvent(): vscode.TestsChangeEvent {
|
||||
const { added, updated, removed } = this;
|
||||
return {
|
||||
get added() { return [...added].map(n => n.revived); },
|
||||
@@ -516,7 +756,7 @@ class MirroredChangeCollector extends IncrementalChangeCollector<MirroredCollect
|
||||
};
|
||||
}
|
||||
|
||||
public complete() {
|
||||
public override complete() {
|
||||
if (!this.isEmpty) {
|
||||
this.emitter.fire(this.getChangeEvent());
|
||||
}
|
||||
@@ -528,7 +768,7 @@ class MirroredChangeCollector extends IncrementalChangeCollector<MirroredCollect
|
||||
* @private
|
||||
*/
|
||||
export class MirroredTestCollection extends AbstractIncrementalTestCollection<MirroredCollectionTestItem> {
|
||||
private changeEmitter = new Emitter<vscode.TestChangeEvent>();
|
||||
private changeEmitter = new Emitter<vscode.TestsChangeEvent>();
|
||||
|
||||
/**
|
||||
* Change emitter that fires with the same sematics as `TestObserver.onDidChangeTests`.
|
||||
@@ -546,7 +786,7 @@ export class MirroredTestCollection extends AbstractIncrementalTestCollection<Mi
|
||||
* Translates the item IDs to TestItems for exposure to extensions.
|
||||
*/
|
||||
public getAllAsTestItem(itemIds: Iterable<string>) {
|
||||
let output: vscode.TestItem[] = [];
|
||||
let output: vscode.TestItem<never>[] = [];
|
||||
for (const itemId of itemIds) {
|
||||
const item = this.items.get(itemId);
|
||||
if (item) {
|
||||
@@ -568,7 +808,7 @@ export class MirroredTestCollection extends AbstractIncrementalTestCollection<Mi
|
||||
/**
|
||||
* If the test item is a mirrored test item, returns its underlying ID.
|
||||
*/
|
||||
public getMirroredTestDataByReference(item: vscode.TestItem) {
|
||||
public getMirroredTestDataByReference(item: vscode.TestItem<unknown>) {
|
||||
return this.items.get(item.id);
|
||||
}
|
||||
|
||||
@@ -579,7 +819,7 @@ export class MirroredTestCollection extends AbstractIncrementalTestCollection<Mi
|
||||
return {
|
||||
...item,
|
||||
// todo@connor4312: make this work well again with children
|
||||
revived: Convert.TestItem.toPlain(item.item) as vscode.TestItem,
|
||||
revived: Convert.TestItem.toPlain(item.item) as vscode.TestItem<never>,
|
||||
depth: parent ? parent.depth + 1 : 0,
|
||||
children: new Set(),
|
||||
};
|
||||
@@ -588,7 +828,7 @@ export class MirroredTestCollection extends AbstractIncrementalTestCollection<Mi
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
protected createChangeCollector() {
|
||||
protected override createChangeCollector() {
|
||||
return new MirroredChangeCollector(this.changeEmitter);
|
||||
}
|
||||
}
|
||||
@@ -628,7 +868,7 @@ abstract class AbstractTestObserverFactory {
|
||||
/**
|
||||
* Gets the internal test data by its reference, in any observer.
|
||||
*/
|
||||
public getMirroredTestDataByReference(ref: vscode.TestItem) {
|
||||
public getMirroredTestDataByReference(ref: vscode.TestItem<unknown>) {
|
||||
for (const { tests } of this.resources.values()) {
|
||||
const v = tests.getMirroredTestDataByReference(ref);
|
||||
if (v) {
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { TestItemImpl } from 'vs/workbench/api/common/extHostTypes';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export const enum ExtHostTestItemEventType {
|
||||
NewChild,
|
||||
Disposed,
|
||||
Invalidated,
|
||||
SetProp,
|
||||
}
|
||||
|
||||
export type ExtHostTestItemEvent =
|
||||
| [evt: ExtHostTestItemEventType.NewChild, item: TestItemImpl]
|
||||
| [evt: ExtHostTestItemEventType.Disposed]
|
||||
| [evt: ExtHostTestItemEventType.Invalidated]
|
||||
| [evt: ExtHostTestItemEventType.SetProp, key: keyof vscode.TestItem<never>, value: any];
|
||||
|
||||
export interface IExtHostTestItemApi {
|
||||
children: Map<string, TestItemImpl>;
|
||||
parent?: TestItemImpl;
|
||||
bus: Emitter<ExtHostTestItemEvent>;
|
||||
}
|
||||
|
||||
const eventPrivateApis = new WeakMap<TestItemImpl, IExtHostTestItemApi>();
|
||||
|
||||
/**
|
||||
* Gets the private API for a test item implementation. This implementation
|
||||
* is a managed object, but we keep a weakmap to avoid exposing any of the
|
||||
* internals to extensions.
|
||||
*/
|
||||
export const getPrivateApiFor = (impl: TestItemImpl) => {
|
||||
let api = eventPrivateApis.get(impl);
|
||||
if (!api) {
|
||||
api = { children: new Map(), bus: new Emitter() };
|
||||
eventPrivateApis.set(impl, api);
|
||||
}
|
||||
|
||||
return api;
|
||||
};
|
||||
@@ -497,12 +497,12 @@ class ExtHostTreeView<T> extends Disposable {
|
||||
|
||||
private getHandlesToRefresh(elements: T[]): TreeItemHandle[] {
|
||||
const elementsToUpdate = new Set<TreeItemHandle>();
|
||||
for (const element of elements) {
|
||||
const elementNode = this.nodes.get(element);
|
||||
const elementNodes = elements.map(element => this.nodes.get(element));
|
||||
for (const elementNode of elementNodes) {
|
||||
if (elementNode && !elementsToUpdate.has(elementNode.item.handle)) {
|
||||
// check if an ancestor of extElement is already in the elements to update list
|
||||
// check if an ancestor of extElement is already in the elements list
|
||||
let currentNode: TreeNode | undefined = elementNode;
|
||||
while (currentNode && currentNode.parent && !elementsToUpdate.has(currentNode.parent.item.handle)) {
|
||||
while (currentNode && currentNode.parent && elementNodes.findIndex(node => currentNode && currentNode.parent && node && node.item.handle === currentNode.parent.item.handle) === -1) {
|
||||
const parentElement: T | undefined = this.elements.get(currentNode.parent.item.handle);
|
||||
currentNode = parentElement ? this.nodes.get(parentElement) : undefined;
|
||||
}
|
||||
@@ -749,7 +749,7 @@ class ExtHostTreeView<T> extends Disposable {
|
||||
this.nodes.clear();
|
||||
}
|
||||
|
||||
dispose() {
|
||||
override dispose() {
|
||||
this._refreshCancellationSource.dispose();
|
||||
|
||||
this.clearAll();
|
||||
|
||||
@@ -46,7 +46,7 @@ export interface IExtHostTunnelService extends ExtHostTunnelServiceShape {
|
||||
getTunnels(): Promise<vscode.TunnelDescription[]>;
|
||||
onDidChangeTunnels: vscode.Event<void>;
|
||||
setTunnelExtensionFunctions(provider: vscode.RemoteAuthorityResolver | undefined): Promise<IDisposable>;
|
||||
registerPortsAttributesProvider(portSelector: { pid?: number, portRange?: [number, number] }, provider: vscode.PortAttributesProvider): IDisposable;
|
||||
registerPortsAttributesProvider(portSelector: { pid?: number, portRange?: [number, number], commandMatcher?: RegExp }, provider: vscode.PortAttributesProvider): IDisposable;
|
||||
}
|
||||
|
||||
export const IExtHostTunnelService = createDecorator<IExtHostTunnelService>('IExtHostTunnelService');
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import { coalesce, isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import * as htmlContent from 'vs/base/common/htmlContent';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import * as marked from 'vs/base/common/marked/marked';
|
||||
import { parse } from 'vs/base/common/marshalling';
|
||||
import { cloneAndChange } from 'vs/base/common/objects';
|
||||
@@ -27,8 +28,9 @@ import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocum
|
||||
import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook';
|
||||
import { EditorGroupColumn, SaveReason } from 'vs/workbench/common/editor';
|
||||
import * as notebooks from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange';
|
||||
import * as search from 'vs/workbench/contrib/search/common/search';
|
||||
import { ISerializedTestResults, ITestItem, ITestMessage, ITestState, SerializedTestResultItem, TestItemExpandState } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { ISerializedTestResults, ITestItem, ITestMessage, SerializedTestResultItem } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
import type * as vscode from 'vscode';
|
||||
import * as types from './extHostTypes';
|
||||
@@ -543,7 +545,7 @@ export namespace WorkspaceEdit {
|
||||
resource: entry.uri,
|
||||
edit: entry.edit,
|
||||
notebookMetadata: entry.notebookMetadata,
|
||||
notebookVersionId: extHostNotebooks?.lookupNotebookDocument(entry.uri)?.notebookDocument.version
|
||||
notebookVersionId: extHostNotebooks?.lookupNotebookDocument(entry.uri)?.apiNotebook.version
|
||||
});
|
||||
|
||||
} else if (entry._type === types.FileEditType.CellOutput) {
|
||||
@@ -578,7 +580,7 @@ export namespace WorkspaceEdit {
|
||||
_type: extHostProtocol.WorkspaceEditType.Cell,
|
||||
metadata: entry.metadata,
|
||||
resource: entry.uri,
|
||||
notebookVersionId: extHostNotebooks?.lookupNotebookDocument(entry.uri)?.notebookDocument.version,
|
||||
notebookVersionId: extHostNotebooks?.lookupNotebookDocument(entry.uri)?.apiNotebook.version,
|
||||
edit: {
|
||||
editType: notebooks.CellEditType.Replace,
|
||||
index: entry.index,
|
||||
@@ -1404,21 +1406,31 @@ export namespace LanguageSelector {
|
||||
}
|
||||
}
|
||||
|
||||
export namespace NotebookCellRange {
|
||||
export namespace NotebookRange {
|
||||
|
||||
export function from(range: vscode.NotebookCellRange): notebooks.ICellRange {
|
||||
export function from(range: vscode.NotebookRange): ICellRange {
|
||||
return { start: range.start, end: range.end };
|
||||
}
|
||||
|
||||
export function to(range: notebooks.ICellRange): types.NotebookCellRange {
|
||||
return new types.NotebookCellRange(range.start, range.end);
|
||||
export function to(range: ICellRange): types.NotebookRange {
|
||||
return new types.NotebookRange(range.start, range.end);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace NotebookCellMetadata {
|
||||
|
||||
export function to(data: notebooks.NotebookCellMetadata): types.NotebookCellMetadata {
|
||||
return new types.NotebookCellMetadata(data.editable, data.breakpointMargin, data.hasExecutionOrder, data.statusMessage, data.inputCollapsed, data.outputCollapsed, data.custom);
|
||||
return new types.NotebookCellMetadata().with({
|
||||
...data,
|
||||
...{
|
||||
executionOrder: null,
|
||||
lastRunSuccess: null,
|
||||
runState: null,
|
||||
runStartTime: null,
|
||||
runStartTimeAdjustment: null,
|
||||
runEndTime: null
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1429,14 +1441,15 @@ export namespace NotebookDocumentMetadata {
|
||||
}
|
||||
|
||||
export function to(data: notebooks.NotebookDocumentMetadata): types.NotebookDocumentMetadata {
|
||||
return new types.NotebookDocumentMetadata(data.editable, data.cellEditable, data.cellHasExecutionOrder, data.custom, data.trusted);
|
||||
return new types.NotebookDocumentMetadata().with(data);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace NotebookCellPreviousExecutionResult {
|
||||
export function to(data: notebooks.NotebookCellMetadata): vscode.NotebookCellExecutionSummary {
|
||||
return {
|
||||
duration: data.lastRunDuration,
|
||||
startTime: data.runStartTime,
|
||||
endTime: data.runEndTime,
|
||||
executionOrder: data.executionOrder,
|
||||
success: data.lastRunSuccess
|
||||
};
|
||||
@@ -1445,7 +1458,8 @@ export namespace NotebookCellPreviousExecutionResult {
|
||||
export function from(data: vscode.NotebookCellExecutionSummary): Partial<notebooks.NotebookCellMetadata> {
|
||||
return {
|
||||
lastRunSuccess: data.success,
|
||||
lastRunDuration: data.duration,
|
||||
runStartTime: data.startTime,
|
||||
runEndTime: data.endTime,
|
||||
executionOrder: data.executionOrder
|
||||
};
|
||||
}
|
||||
@@ -1612,38 +1626,52 @@ export namespace NotebookDecorationRenderOptions {
|
||||
}
|
||||
}
|
||||
|
||||
export namespace NotebookDocumentContentOptions {
|
||||
export function from(options: vscode.NotebookDocumentContentOptions | undefined): notebooks.TransientOptions {
|
||||
export namespace NotebookStatusBarItem {
|
||||
export function from(item: vscode.NotebookCellStatusBarItem, commandsConverter: CommandsConverter, disposables: DisposableStore): notebooks.INotebookCellStatusBarItem {
|
||||
const command = typeof item.command === 'string' ? { title: '', command: item.command } : item.command;
|
||||
return {
|
||||
transientOutputs: options ? options.transientOutputs : false,
|
||||
transientMetadata: {
|
||||
...(options?.transientMetadata ?? {}),
|
||||
...{
|
||||
executionOrder: true,
|
||||
lastRunDuration: true,
|
||||
runState: true,
|
||||
runStartTime: true,
|
||||
lastRunSuccess: true
|
||||
}
|
||||
}
|
||||
alignment: item.alignment === types.NotebookCellStatusBarAlignment.Left ? notebooks.CellStatusbarAlignment.Left : notebooks.CellStatusbarAlignment.Right,
|
||||
command: commandsConverter.toInternal(command, disposables), // TODO@roblou
|
||||
text: item.text,
|
||||
tooltip: item.tooltip,
|
||||
accessibilityInformation: item.accessibilityInformation,
|
||||
priority: item.priority
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export namespace TestState {
|
||||
export function from(item: vscode.TestState): ITestState {
|
||||
export namespace NotebookDocumentContentOptions {
|
||||
export function from(options: vscode.NotebookDocumentContentOptions | undefined): notebooks.TransientOptions {
|
||||
return {
|
||||
state: item.state,
|
||||
duration: item.duration,
|
||||
messages: item.messages.map(TestMessage.from),
|
||||
transientOutputs: options?.transientOutputs ?? false,
|
||||
transientCellMetadata: {
|
||||
...options?.transientCellMetadata,
|
||||
executionOrder: true,
|
||||
runState: true,
|
||||
runStartTime: true,
|
||||
runStartTimeAdjustment: true,
|
||||
runEndTime: true,
|
||||
lastRunSuccess: true
|
||||
},
|
||||
transientDocumentMetadata: options?.transientDocumentMetadata ?? {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function to(item: ITestState): vscode.TestState {
|
||||
const ts = new types.TestState(item.state);
|
||||
ts.duration = item.duration;
|
||||
ts.messages = item.messages.map(TestMessage.to);
|
||||
return ts;
|
||||
export namespace NotebookKernelPreload {
|
||||
export function from(preload: vscode.NotebookKernelPreload): { uri: UriComponents; provides: string[] } {
|
||||
return {
|
||||
uri: preload.uri,
|
||||
provides: typeof preload.provides === 'string'
|
||||
? [preload.provides]
|
||||
: preload.provides ?? []
|
||||
};
|
||||
}
|
||||
export function to(preload: { uri: UriComponents; provides: string[] }): vscode.NotebookKernelPreload {
|
||||
return {
|
||||
uri: URI.revive(preload.uri),
|
||||
provides: preload.provides
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1668,18 +1696,18 @@ export namespace TestMessage {
|
||||
}
|
||||
|
||||
export namespace TestItem {
|
||||
export type Raw = vscode.TestItem;
|
||||
export type Raw<T = unknown> = vscode.TestItem<T>;
|
||||
|
||||
export function from(item: vscode.TestItem): ITestItem {
|
||||
export function from(item: vscode.TestItem<unknown>): ITestItem {
|
||||
return {
|
||||
extId: item.id,
|
||||
label: item.label,
|
||||
uri: item.uri,
|
||||
range: Range.from(item.range),
|
||||
range: Range.from(item.range) || null,
|
||||
debuggable: item.debuggable ?? false,
|
||||
description: item.description,
|
||||
description: item.description || null,
|
||||
runnable: item.runnable ?? true,
|
||||
expandable: item.expandable,
|
||||
error: item.error ? (MarkdownString.fromStrict(item.error) || null) : null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1688,84 +1716,55 @@ export namespace TestItem {
|
||||
extId: item.id,
|
||||
label: item.label,
|
||||
uri: item.uri,
|
||||
range: Range.from(item.range),
|
||||
range: Range.from(item.range) || null,
|
||||
debuggable: false,
|
||||
description: item.description,
|
||||
description: item.description || null,
|
||||
error: null,
|
||||
runnable: true,
|
||||
expandable: true,
|
||||
};
|
||||
}
|
||||
|
||||
export function toPlain(item: ITestItem): Omit<vscode.TestItem, 'children' | 'invalidate' | 'discoverChildren'> {
|
||||
export function toPlain(item: ITestItem): Omit<vscode.TestItem<never>, 'children' | 'invalidate' | 'discoverChildren'> {
|
||||
return {
|
||||
id: item.extId,
|
||||
label: item.label,
|
||||
uri: URI.revive(item.uri),
|
||||
range: Range.to(item.range),
|
||||
expandable: item.expandable,
|
||||
range: Range.to(item.range || undefined),
|
||||
addChild: () => undefined,
|
||||
dispose: () => undefined,
|
||||
status: types.TestItemStatus.Pending,
|
||||
data: undefined as never,
|
||||
debuggable: item.debuggable,
|
||||
description: item.description,
|
||||
description: item.description || undefined,
|
||||
runnable: item.runnable,
|
||||
};
|
||||
}
|
||||
|
||||
export function to(item: ITestItem): types.TestItem {
|
||||
const testItem = new types.TestItem(item.extId, item.label, URI.revive(item.uri), item.expandable);
|
||||
testItem.range = Range.to(item.range);
|
||||
export function to(item: ITestItem): types.TestItemImpl {
|
||||
const testItem = new types.TestItemImpl(item.extId, item.label, URI.revive(item.uri), undefined);
|
||||
testItem.range = Range.to(item.range || undefined);
|
||||
testItem.debuggable = item.debuggable;
|
||||
testItem.description = item.description;
|
||||
testItem.description = item.description || undefined;
|
||||
testItem.runnable = item.runnable;
|
||||
return testItem;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace TestResults {
|
||||
export function from(id: string, results: vscode.TestResults): ISerializedTestResults {
|
||||
const serialized: ISerializedTestResults = {
|
||||
completedAt: results.completedAt,
|
||||
id,
|
||||
items: [],
|
||||
};
|
||||
|
||||
const queue: [parent: SerializedTestResultItem | null, children: Iterable<vscode.TestResultSnapshot>][] = [
|
||||
[null, results.results],
|
||||
];
|
||||
|
||||
while (queue.length) {
|
||||
const [parent, children] = queue.pop()!;
|
||||
for (const item of children) {
|
||||
const serializedItem: SerializedTestResultItem = {
|
||||
children: item.children?.map(c => c.id) ?? [],
|
||||
computedState: item.result.state,
|
||||
item: TestItem.fromResultSnapshot(item),
|
||||
state: TestState.from(item.result),
|
||||
retired: undefined,
|
||||
expand: TestItemExpandState.Expanded,
|
||||
parent: parent?.item.extId ?? null,
|
||||
src: { provider: '', tree: -1 },
|
||||
direct: !parent,
|
||||
};
|
||||
|
||||
serialized.items.push(serializedItem);
|
||||
if (item.children) {
|
||||
queue.push([serializedItem, item.children]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return serialized;
|
||||
}
|
||||
|
||||
const convertTestResultItem = (item: SerializedTestResultItem, byInternalId: Map<string, SerializedTestResultItem>): vscode.TestResultSnapshot => ({
|
||||
...TestItem.toPlain(item.item),
|
||||
result: TestState.to(item.state),
|
||||
taskStates: item.tasks.map(t => ({
|
||||
state: t.state,
|
||||
duration: t.duration,
|
||||
messages: t.messages.map(TestMessage.to),
|
||||
})),
|
||||
children: item.children
|
||||
.map(c => byInternalId.get(c))
|
||||
.filter(isDefined)
|
||||
.map(c => convertTestResultItem(c, byInternalId)),
|
||||
});
|
||||
|
||||
export function to(serialized: ISerializedTestResults): vscode.TestResults {
|
||||
export function to(serialized: ISerializedTestResults): vscode.TestRunResult {
|
||||
const roots: SerializedTestResultItem[] = [];
|
||||
const byInternalId = new Map<string, SerializedTestResultItem>();
|
||||
for (const item of serialized.items) {
|
||||
|
||||
@@ -7,12 +7,14 @@ import { coalesceInPlace, equals } from 'vs/base/common/arrays';
|
||||
import { illegalArgument } from 'vs/base/common/errors';
|
||||
import { IRelativePattern } from 'vs/base/common/glob';
|
||||
import { isMarkdownString, MarkdownString as BaseMarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { ResourceMap } from 'vs/base/common/map';
|
||||
import { ReadonlyMapView, ResourceMap } from 'vs/base/common/map';
|
||||
import { isFalsyOrWhitespace } from 'vs/base/common/strings';
|
||||
import { isStringArray } from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { FileSystemProviderErrorCode, markAsFileSystemProviderError } from 'vs/platform/files/common/files';
|
||||
import { RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { getPrivateApiFor, ExtHostTestItemEventType, IExtHostTestItemApi } from 'vs/workbench/api/common/extHostTestingPrivateApi';
|
||||
import { CellEditType, ICellEditOperation } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import type * as vscode from 'vscode';
|
||||
|
||||
@@ -419,7 +421,7 @@ export class Selection extends Range {
|
||||
return this._anchor === this._end;
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
override toJSON() {
|
||||
return {
|
||||
start: this.start,
|
||||
end: this.end,
|
||||
@@ -676,34 +678,33 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit {
|
||||
this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.DocumentMetadata, metadata: value }, notebookMetadata: value });
|
||||
}
|
||||
|
||||
replaceNotebookCells(uri: URI, start: number, end: number, cells: vscode.NotebookCellData[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
|
||||
if (start !== end || cells.length > 0) {
|
||||
this._edits.push({ _type: FileEditType.CellReplace, uri, index: start, count: end - start, cells, metadata });
|
||||
replaceNotebookCells(uri: URI, range: vscode.NotebookRange, cells: vscode.NotebookCellData[], metadata?: vscode.WorkspaceEditEntryMetadata): void;
|
||||
replaceNotebookCells(uri: URI, start: number, end: number, cells: vscode.NotebookCellData[], metadata?: vscode.WorkspaceEditEntryMetadata): void;
|
||||
replaceNotebookCells(uri: URI, startOrRange: number | vscode.NotebookRange, endOrCells: number | vscode.NotebookCellData[], cellsOrMetadata?: vscode.NotebookCellData[] | vscode.WorkspaceEditEntryMetadata, metadata?: vscode.WorkspaceEditEntryMetadata): void {
|
||||
let start: number | undefined;
|
||||
let end: number | undefined;
|
||||
let cellData: vscode.NotebookCellData[] = [];
|
||||
let workspaceEditMetadata: vscode.WorkspaceEditEntryMetadata | undefined;
|
||||
|
||||
if (NotebookRange.isNotebookRange(startOrRange) && NotebookCellData.isNotebookCellDataArray(endOrCells) && !NotebookCellData.isNotebookCellDataArray(cellsOrMetadata)) {
|
||||
start = startOrRange.start;
|
||||
end = startOrRange.end;
|
||||
cellData = endOrCells;
|
||||
workspaceEditMetadata = cellsOrMetadata;
|
||||
} else if (typeof startOrRange === 'number' && typeof endOrCells === 'number' && NotebookCellData.isNotebookCellDataArray(cellsOrMetadata)) {
|
||||
start = startOrRange;
|
||||
end = endOrCells;
|
||||
cellData = cellsOrMetadata;
|
||||
workspaceEditMetadata = metadata;
|
||||
}
|
||||
}
|
||||
|
||||
replaceNotebookCellOutput(uri: URI, index: number, outputs: vscode.NotebookCellOutput[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
|
||||
this._editNotebookCellOutput(uri, index, false, outputs, metadata);
|
||||
}
|
||||
if (start === undefined || end === undefined) {
|
||||
throw new Error('Invalid arguments');
|
||||
}
|
||||
|
||||
appendNotebookCellOutput(uri: URI, index: number, outputs: vscode.NotebookCellOutput[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
|
||||
this._editNotebookCellOutput(uri, index, true, outputs, metadata);
|
||||
}
|
||||
|
||||
replaceNotebookCellOutputItems(uri: URI, index: number, outputId: string, items: NotebookCellOutputItem[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
|
||||
this._editNotebookCellOutputItems(uri, index, outputId, false, items, metadata);
|
||||
}
|
||||
|
||||
appendNotebookCellOutputItems(uri: URI, index: number, outputId: string, items: NotebookCellOutputItem[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
|
||||
this._editNotebookCellOutputItems(uri, index, outputId, true, items, metadata);
|
||||
}
|
||||
|
||||
private _editNotebookCellOutputItems(uri: URI, index: number, id: string, append: boolean, items: vscode.NotebookCellOutputItem[], metadata: vscode.WorkspaceEditEntryMetadata | undefined): void {
|
||||
this._edits.push({ _type: FileEditType.CellOutputItem, metadata, uri, index, outputId: id, append, newOutputItems: items });
|
||||
}
|
||||
|
||||
private _editNotebookCellOutput(uri: URI, index: number, append: boolean, outputs: vscode.NotebookCellOutput[], metadata: vscode.WorkspaceEditEntryMetadata | undefined): void {
|
||||
this._edits.push({ _type: FileEditType.CellOutput, metadata, uri, index, append, newOutputs: outputs, newMetadata: undefined });
|
||||
if (start !== end || cellData.length > 0) {
|
||||
this._edits.push({ _type: FileEditType.CellReplace, uri, index: start, count: end - start, cells: cellData, metadata: workspaceEditMetadata });
|
||||
}
|
||||
}
|
||||
|
||||
replaceNotebookCellMetadata(uri: URI, index: number, cellMetadata: vscode.NotebookCellMetadata, metadata?: vscode.WorkspaceEditEntryMetadata): void {
|
||||
@@ -2895,7 +2896,17 @@ export enum ColorThemeKind {
|
||||
|
||||
//#region Notebook
|
||||
|
||||
export class NotebookCellRange {
|
||||
export class NotebookRange {
|
||||
static isNotebookRange(thing: any): thing is vscode.NotebookRange {
|
||||
if (thing instanceof NotebookRange) {
|
||||
return true;
|
||||
}
|
||||
if (!thing) {
|
||||
return false;
|
||||
}
|
||||
return typeof (<NotebookRange>thing).start === 'number'
|
||||
&& typeof (<NotebookRange>thing).end === 'number';
|
||||
}
|
||||
|
||||
private _start: number;
|
||||
private _end: number;
|
||||
@@ -2916,58 +2927,59 @@ export class NotebookCellRange {
|
||||
if (start < 0) {
|
||||
throw illegalArgument('start must be positive');
|
||||
}
|
||||
if (end < start) {
|
||||
throw illegalArgument('end cannot be smaller than start');
|
||||
if (end < 0) {
|
||||
throw illegalArgument('end must be positive');
|
||||
}
|
||||
this._start = start;
|
||||
this._end = end;
|
||||
if (start <= end) {
|
||||
this._start = start;
|
||||
this._end = end;
|
||||
} else {
|
||||
this._start = end;
|
||||
this._end = start;
|
||||
}
|
||||
}
|
||||
|
||||
with(change: { start?: number, end?: number }): NotebookRange {
|
||||
let start = this._start;
|
||||
let end = this._end;
|
||||
|
||||
if (change.start !== undefined) {
|
||||
start = change.start;
|
||||
}
|
||||
if (change.end !== undefined) {
|
||||
end = change.end;
|
||||
}
|
||||
if (start === this._start && end === this._end) {
|
||||
return this;
|
||||
}
|
||||
return new NotebookRange(start, end);
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookCellMetadata {
|
||||
readonly inputCollapsed?: boolean;
|
||||
readonly outputCollapsed?: boolean;
|
||||
readonly [key: string]: any;
|
||||
|
||||
constructor(
|
||||
readonly editable?: boolean,
|
||||
readonly breakpointMargin?: boolean,
|
||||
readonly hasExecutionOrder?: boolean,
|
||||
readonly statusMessage?: string,
|
||||
readonly inputCollapsed?: boolean,
|
||||
readonly outputCollapsed?: boolean,
|
||||
readonly custom?: Record<string, any>,
|
||||
) { }
|
||||
constructor(inputCollapsed?: boolean, outputCollapsed?: boolean);
|
||||
constructor(data: Record<string, any>);
|
||||
constructor(inputCollapsedOrData: (boolean | undefined) | Record<string, any>, outputCollapsed?: boolean) {
|
||||
if (typeof inputCollapsedOrData === 'object') {
|
||||
Object.assign(this, inputCollapsedOrData);
|
||||
} else {
|
||||
this.inputCollapsed = inputCollapsedOrData;
|
||||
this.outputCollapsed = outputCollapsed;
|
||||
}
|
||||
}
|
||||
|
||||
with(change: {
|
||||
editable?: boolean | null,
|
||||
breakpointMargin?: boolean | null,
|
||||
hasExecutionOrder?: boolean | null,
|
||||
statusMessage?: string | null,
|
||||
inputCollapsed?: boolean | null,
|
||||
outputCollapsed?: boolean | null,
|
||||
custom?: Record<string, any> | null,
|
||||
[key: string]: any
|
||||
}): NotebookCellMetadata {
|
||||
|
||||
let { editable, breakpointMargin, hasExecutionOrder, statusMessage, inputCollapsed, outputCollapsed, custom } = change;
|
||||
let { inputCollapsed, outputCollapsed, ...remaining } = change;
|
||||
|
||||
if (editable === undefined) {
|
||||
editable = this.editable;
|
||||
} else if (editable === null) {
|
||||
editable = undefined;
|
||||
}
|
||||
if (breakpointMargin === undefined) {
|
||||
breakpointMargin = this.breakpointMargin;
|
||||
} else if (breakpointMargin === null) {
|
||||
breakpointMargin = undefined;
|
||||
}
|
||||
if (hasExecutionOrder === undefined) {
|
||||
hasExecutionOrder = this.hasExecutionOrder;
|
||||
} else if (hasExecutionOrder === null) {
|
||||
hasExecutionOrder = undefined;
|
||||
}
|
||||
if (statusMessage === undefined) {
|
||||
statusMessage = this.statusMessage;
|
||||
} else if (statusMessage === null) {
|
||||
statusMessage = undefined;
|
||||
}
|
||||
if (inputCollapsed === undefined) {
|
||||
inputCollapsed = this.inputCollapsed;
|
||||
} else if (inputCollapsed === null) {
|
||||
@@ -2978,103 +2990,78 @@ export class NotebookCellMetadata {
|
||||
} else if (outputCollapsed === null) {
|
||||
outputCollapsed = undefined;
|
||||
}
|
||||
if (custom === undefined) {
|
||||
custom = this.custom;
|
||||
} else if (custom === null) {
|
||||
custom = undefined;
|
||||
}
|
||||
|
||||
if (editable === this.editable &&
|
||||
breakpointMargin === this.breakpointMargin &&
|
||||
hasExecutionOrder === this.hasExecutionOrder &&
|
||||
statusMessage === this.statusMessage &&
|
||||
inputCollapsed === this.inputCollapsed &&
|
||||
if (inputCollapsed === this.inputCollapsed &&
|
||||
outputCollapsed === this.outputCollapsed &&
|
||||
custom === this.custom
|
||||
Object.keys(remaining).length === 0
|
||||
) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return new NotebookCellMetadata(
|
||||
editable,
|
||||
breakpointMargin,
|
||||
hasExecutionOrder,
|
||||
statusMessage,
|
||||
inputCollapsed,
|
||||
outputCollapsed,
|
||||
custom,
|
||||
{
|
||||
inputCollapsed,
|
||||
outputCollapsed,
|
||||
...remaining
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookDocumentMetadata {
|
||||
readonly trusted: boolean;
|
||||
readonly [key: string]: any;
|
||||
|
||||
constructor(
|
||||
readonly editable: boolean = true,
|
||||
readonly cellEditable: boolean = true,
|
||||
readonly cellHasExecutionOrder: boolean = true,
|
||||
readonly custom: { [key: string]: any; } = {},
|
||||
readonly trusted: boolean = true,
|
||||
) { }
|
||||
constructor(trusted?: boolean);
|
||||
constructor(data: Record<string, any>);
|
||||
constructor(trustedOrData: boolean | Record<string, any> = true) {
|
||||
if (typeof trustedOrData === 'object') {
|
||||
Object.assign(this, trustedOrData);
|
||||
this.trusted = trustedOrData.trusted ?? true;
|
||||
} else {
|
||||
this.trusted = trustedOrData;
|
||||
}
|
||||
}
|
||||
|
||||
with(change: {
|
||||
editable?: boolean | null,
|
||||
cellEditable?: boolean | null,
|
||||
cellHasExecutionOrder?: boolean | null,
|
||||
custom?: { [key: string]: any; } | null,
|
||||
trusted?: boolean | null,
|
||||
[key: string]: any
|
||||
}): NotebookDocumentMetadata {
|
||||
|
||||
let { editable, cellEditable, cellHasExecutionOrder, custom, trusted } = change;
|
||||
let { trusted, ...remaining } = change;
|
||||
|
||||
if (editable === undefined) {
|
||||
editable = this.editable;
|
||||
} else if (editable === null) {
|
||||
editable = undefined;
|
||||
}
|
||||
if (cellEditable === undefined) {
|
||||
cellEditable = this.cellEditable;
|
||||
} else if (cellEditable === null) {
|
||||
cellEditable = undefined;
|
||||
}
|
||||
if (cellHasExecutionOrder === undefined) {
|
||||
cellHasExecutionOrder = this.cellHasExecutionOrder;
|
||||
} else if (cellHasExecutionOrder === null) {
|
||||
cellHasExecutionOrder = undefined;
|
||||
}
|
||||
if (custom === undefined) {
|
||||
custom = this.custom;
|
||||
} else if (custom === null) {
|
||||
custom = undefined;
|
||||
}
|
||||
if (trusted === undefined) {
|
||||
trusted = this.trusted;
|
||||
} else if (trusted === null) {
|
||||
trusted = undefined;
|
||||
}
|
||||
|
||||
if (editable === this.editable &&
|
||||
cellEditable === this.cellEditable &&
|
||||
cellHasExecutionOrder === this.cellHasExecutionOrder &&
|
||||
custom === this.custom &&
|
||||
trusted === this.trusted
|
||||
if (trusted === this.trusted &&
|
||||
Object.keys(remaining).length === 0
|
||||
) {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
return new NotebookDocumentMetadata(
|
||||
editable,
|
||||
cellEditable,
|
||||
cellHasExecutionOrder,
|
||||
custom,
|
||||
trusted
|
||||
{
|
||||
trusted,
|
||||
...remaining
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookCellData {
|
||||
|
||||
static isNotebookCellDataArray(value: unknown): value is vscode.NotebookCellData[] {
|
||||
return Array.isArray(value) && (<unknown[]>value).every(elem => NotebookCellData.isNotebookCellData(elem));
|
||||
}
|
||||
|
||||
static isNotebookCellData(value: unknown): value is vscode.NotebookCellData {
|
||||
// return value instanceof NotebookCellData;
|
||||
return true;
|
||||
}
|
||||
|
||||
kind: NotebookCellKind;
|
||||
source: string;
|
||||
language: string;
|
||||
@@ -3111,17 +3098,21 @@ export class NotebookCellOutputItem {
|
||||
}
|
||||
|
||||
constructor(
|
||||
readonly mime: string,
|
||||
readonly value: unknown, // JSON'able
|
||||
readonly metadata?: Record<string, any>
|
||||
) { }
|
||||
public mime: string,
|
||||
public value: unknown, // JSON'able
|
||||
public metadata?: Record<string, any>
|
||||
) {
|
||||
if (isFalsyOrWhitespace(this.mime)) {
|
||||
throw new Error('INVALID mime type, must not be empty or falsy');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookCellOutput {
|
||||
|
||||
readonly outputs: NotebookCellOutputItem[];
|
||||
readonly id: string;
|
||||
readonly metadata?: Record<string, any>;
|
||||
id: string;
|
||||
outputs: NotebookCellOutputItem[];
|
||||
metadata?: Record<string, any>;
|
||||
|
||||
constructor(
|
||||
outputs: NotebookCellOutputItem[],
|
||||
@@ -3162,6 +3153,21 @@ export enum NotebookEditorRevealType {
|
||||
AtTop = 3
|
||||
}
|
||||
|
||||
export class NotebookCellStatusBarItem {
|
||||
constructor(
|
||||
public text: string,
|
||||
public alignment: NotebookCellStatusBarAlignment,
|
||||
public command?: string | vscode.Command,
|
||||
public tooltip?: string,
|
||||
public priority?: number,
|
||||
public accessibilityInformation?: vscode.AccessibilityInformation) { }
|
||||
}
|
||||
|
||||
|
||||
export enum NotebookControllerAffinity {
|
||||
Default = 1,
|
||||
Preferred = 2
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
@@ -3223,7 +3229,7 @@ export class LinkedEditingRanges {
|
||||
}
|
||||
|
||||
//#region Testing
|
||||
export enum TestResult {
|
||||
export enum TestResultState {
|
||||
Unset = 0,
|
||||
Queued = 1,
|
||||
Running = 2,
|
||||
@@ -3240,16 +3246,17 @@ export enum TestMessageSeverity {
|
||||
Hint = 3
|
||||
}
|
||||
|
||||
export const TestItemHookProperty = Symbol('TestItemHookProperty');
|
||||
|
||||
export interface ITestItemHook {
|
||||
created(item: vscode.TestItem): void;
|
||||
setProp<K extends keyof vscode.TestItem>(key: K, value: vscode.TestItem[K]): void;
|
||||
invalidate(id: string): void;
|
||||
delete(id: string): void;
|
||||
export enum TestItemStatus {
|
||||
Pending = 0,
|
||||
Resolved = 1,
|
||||
}
|
||||
|
||||
const testItemPropAccessor = <K extends keyof vscode.TestItem>(item: TestItem, key: K, defaultValue: vscode.TestItem[K]) => {
|
||||
const testItemPropAccessor = <K extends keyof vscode.TestItem<never>>(
|
||||
api: IExtHostTestItemApi,
|
||||
key: K,
|
||||
defaultValue: vscode.TestItem<never>[K],
|
||||
equals: (a: vscode.TestItem<never>[K], b: vscode.TestItem<never>[K]) => boolean
|
||||
) => {
|
||||
let value = defaultValue;
|
||||
return {
|
||||
enumerable: true,
|
||||
@@ -3257,80 +3264,41 @@ const testItemPropAccessor = <K extends keyof vscode.TestItem>(item: TestItem, k
|
||||
get() {
|
||||
return value;
|
||||
},
|
||||
set(newValue: vscode.TestItem[K]) {
|
||||
item[TestItemHookProperty]?.setProp(key, newValue);
|
||||
value = newValue;
|
||||
set(newValue: vscode.TestItem<never>[K]) {
|
||||
if (!equals(value, newValue)) {
|
||||
value = newValue;
|
||||
api.bus.fire([ExtHostTestItemEventType.SetProp, key, newValue]);
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export class TestChildrenCollection implements vscode.TestChildrenCollection<vscode.TestItem> {
|
||||
#map = new Map<string, vscode.TestItem>();
|
||||
#hookRef: () => ITestItemHook | undefined;
|
||||
const strictEqualComparator = <T>(a: T, b: T) => a === b;
|
||||
const rangeComparator = (a: vscode.Range | undefined, b: vscode.Range | undefined) => {
|
||||
if (a === b) { return true; }
|
||||
if (!a || !b) { return false; }
|
||||
return a.isEqual(b);
|
||||
};
|
||||
|
||||
public get size() {
|
||||
return this.#map.size;
|
||||
}
|
||||
export class TestItemImpl implements vscode.TestItem<unknown> {
|
||||
public readonly id!: string;
|
||||
public readonly uri!: vscode.Uri;
|
||||
public readonly children!: ReadonlyMap<string, TestItemImpl>;
|
||||
public readonly parent!: TestItemImpl | undefined;
|
||||
|
||||
constructor(hookRef: () => ITestItemHook | undefined) {
|
||||
this.#hookRef = hookRef;
|
||||
}
|
||||
|
||||
public add(child: vscode.TestItem) {
|
||||
const map = this.#map;
|
||||
const hook = this.#hookRef();
|
||||
|
||||
const existing = map.get(child.id);
|
||||
if (existing === child) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (existing) {
|
||||
hook?.delete(child.id);
|
||||
}
|
||||
|
||||
map.set(child.id, child);
|
||||
hook?.created(child);
|
||||
}
|
||||
|
||||
public get(id: string) {
|
||||
return this.#map.get(id);
|
||||
}
|
||||
|
||||
public clear() {
|
||||
for (const key of this.#map.keys()) {
|
||||
this.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
public delete(childOrId: vscode.TestItem | string) {
|
||||
const id = typeof childOrId === 'string' ? childOrId : childOrId.id;
|
||||
if (this.#map.has(id)) {
|
||||
this.#map.delete(id);
|
||||
this.#hookRef()?.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
public toJSON() {
|
||||
return [...this.#map.values()];
|
||||
}
|
||||
|
||||
public [Symbol.iterator]() {
|
||||
return this.#map.values();
|
||||
}
|
||||
}
|
||||
|
||||
export class TestItem implements vscode.TestItem {
|
||||
public id!: string;
|
||||
public range!: vscode.Range | undefined;
|
||||
public description!: string | undefined;
|
||||
public runnable!: boolean;
|
||||
public debuggable!: boolean;
|
||||
public children!: TestChildrenCollection;
|
||||
public uri!: vscode.Uri;
|
||||
public [TestItemHookProperty]!: ITestItemHook | undefined;
|
||||
public error!: string | vscode.MarkdownString;
|
||||
public status!: vscode.TestItemStatus;
|
||||
|
||||
/** Extension-owned resolve handler */
|
||||
public resolveHandler?: (token: vscode.CancellationToken) => void;
|
||||
|
||||
constructor(id: string, public label: string, uri: vscode.Uri, public data: unknown) {
|
||||
const api = getPrivateApiFor(this);
|
||||
|
||||
constructor(id: string, public label: string, uri: vscode.Uri, public expandable: boolean) {
|
||||
Object.defineProperties(this, {
|
||||
id: {
|
||||
value: id,
|
||||
@@ -3342,38 +3310,52 @@ export class TestItem implements vscode.TestItem {
|
||||
enumerable: true,
|
||||
writable: false,
|
||||
},
|
||||
parent: {
|
||||
enumerable: false,
|
||||
get: () => api.parent,
|
||||
},
|
||||
children: {
|
||||
value: new TestChildrenCollection(() => this[TestItemHookProperty]),
|
||||
value: new ReadonlyMapView(api.children),
|
||||
enumerable: true,
|
||||
writable: false,
|
||||
},
|
||||
[TestItemHookProperty]: {
|
||||
enumerable: false,
|
||||
writable: true,
|
||||
configurable: false,
|
||||
},
|
||||
range: testItemPropAccessor(this, 'range', undefined),
|
||||
description: testItemPropAccessor(this, 'description', undefined),
|
||||
runnable: testItemPropAccessor(this, 'runnable', true),
|
||||
debuggable: testItemPropAccessor(this, 'debuggable', true),
|
||||
range: testItemPropAccessor(api, 'range', undefined, rangeComparator),
|
||||
description: testItemPropAccessor(api, 'description', undefined, strictEqualComparator),
|
||||
runnable: testItemPropAccessor(api, 'runnable', true, strictEqualComparator),
|
||||
debuggable: testItemPropAccessor(api, 'debuggable', true, strictEqualComparator),
|
||||
status: testItemPropAccessor(api, 'status', TestItemStatus.Resolved, strictEqualComparator),
|
||||
error: testItemPropAccessor(api, 'error', undefined, strictEqualComparator),
|
||||
});
|
||||
}
|
||||
|
||||
public invalidate() {
|
||||
this[TestItemHookProperty]?.invalidate(this.id);
|
||||
getPrivateApiFor(this).bus.fire([ExtHostTestItemEventType.Invalidated]);
|
||||
}
|
||||
|
||||
public discoverChildren(progress: vscode.Progress<{ busy: boolean }>, _token: vscode.CancellationToken) {
|
||||
progress.report({ busy: false });
|
||||
public dispose() {
|
||||
const api = getPrivateApiFor(this);
|
||||
if (api.parent) {
|
||||
getPrivateApiFor(api.parent).children.delete(this.id);
|
||||
}
|
||||
|
||||
api.bus.fire([ExtHostTestItemEventType.Disposed]);
|
||||
}
|
||||
|
||||
public addChild(child: vscode.TestItem<unknown>) {
|
||||
if (!(child instanceof TestItemImpl)) {
|
||||
throw new Error('Test child must be created through vscode.test.createTestItem()');
|
||||
}
|
||||
|
||||
const api = getPrivateApiFor(this);
|
||||
if (api.children.has(child.id)) {
|
||||
throw new Error(`Attempted to insert a duplicate test item ID ${child.id}`);
|
||||
}
|
||||
|
||||
api.children.set(child.id, child);
|
||||
api.bus.fire([ExtHostTestItemEventType.NewChild, child]);
|
||||
}
|
||||
}
|
||||
|
||||
export class TestState implements vscode.TestState {
|
||||
public messages: TestMessage[] = [];
|
||||
public duration?: number;
|
||||
|
||||
constructor(public state: TestResult) { }
|
||||
}
|
||||
|
||||
export class TestMessage implements vscode.TestMessage {
|
||||
public severity = TestMessageSeverity.Error;
|
||||
@@ -3402,7 +3384,7 @@ export enum ExternalUriOpenerPriority {
|
||||
export enum WorkspaceTrustState {
|
||||
Untrusted = 0,
|
||||
Trusted = 1,
|
||||
Unknown = 2
|
||||
Unspecified = 2
|
||||
}
|
||||
|
||||
export enum PortAutoForwardAction {
|
||||
|
||||
@@ -3,11 +3,14 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { normalizeVersion, parseVersion } from 'vs/platform/extensions/common/extensionValidator';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
|
||||
import { deserializeWebviewMessage } from 'vs/workbench/api/common/extHostWebviewMessaging';
|
||||
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview';
|
||||
import type * as vscode from 'vscode';
|
||||
@@ -28,6 +31,8 @@ export class ExtHostWebview implements vscode.Webview {
|
||||
#isDisposed: boolean = false;
|
||||
#hasCalledAsWebviewUri = false;
|
||||
|
||||
#serializeBuffersForPostMessage = false;
|
||||
|
||||
constructor(
|
||||
handle: extHostProtocol.WebviewHandle,
|
||||
proxy: extHostProtocol.MainThreadWebviewsShape,
|
||||
@@ -43,6 +48,7 @@ export class ExtHostWebview implements vscode.Webview {
|
||||
this.#initData = initData;
|
||||
this.#workspace = workspace;
|
||||
this.#extension = extension;
|
||||
this.#serializeBuffersForPostMessage = shouldSerializeBuffersForPostMessage(extension);
|
||||
this.#deprecationService = deprecationService;
|
||||
}
|
||||
|
||||
@@ -104,7 +110,8 @@ export class ExtHostWebview implements vscode.Webview {
|
||||
if (this.#isDisposed) {
|
||||
return false;
|
||||
}
|
||||
return this.#proxy.$postMessage(this.#handle, message);
|
||||
const serialized = serializeMessage(message, { serializeBuffersForPostMessage: this.#serializeBuffersForPostMessage });
|
||||
return this.#proxy.$postMessage(this.#handle, serialized.message, ...serialized.buffers);
|
||||
}
|
||||
|
||||
private assertNotDisposed() {
|
||||
@@ -114,6 +121,49 @@ export class ExtHostWebview implements vscode.Webview {
|
||||
}
|
||||
}
|
||||
|
||||
export function shouldSerializeBuffersForPostMessage(extension: IExtensionDescription): boolean {
|
||||
if (!extension.enableProposedApi) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const version = normalizeVersion(parseVersion(extension.engines.vscode));
|
||||
return !!version && version.majorBase >= 1 && version.minorBase >= 56;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function serializeMessage(message: any, options: { serializeBuffersForPostMessage?: boolean }): { message: string, buffers: VSBuffer[] } {
|
||||
if (options.serializeBuffersForPostMessage) {
|
||||
// Extract all ArrayBuffers from the message and replace them with references.
|
||||
const vsBuffers: Array<{ original: ArrayBuffer, vsBuffer: VSBuffer }> = [];
|
||||
|
||||
const replacer = (_key: string, value: any) => {
|
||||
if (value && value instanceof ArrayBuffer) {
|
||||
let index = vsBuffers.findIndex(x => x.original === value);
|
||||
if (index === -1) {
|
||||
const bytes = new Uint8Array(value);
|
||||
const vsBuffer = VSBuffer.wrap(bytes);
|
||||
index = vsBuffers.length;
|
||||
vsBuffers.push({ original: value, vsBuffer });
|
||||
}
|
||||
|
||||
return <extHostProtocol.WebviewMessageArrayBufferReference>{
|
||||
$$vscode_array_buffer_reference$$: true,
|
||||
index,
|
||||
};
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
const serializedMessage = JSON.stringify(message, replacer);
|
||||
return { message: serializedMessage, buffers: vsBuffers.map(x => x.vsBuffer) };
|
||||
} else {
|
||||
return { message: JSON.stringify(message), buffers: [] };
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
||||
|
||||
private readonly _webviewProxy: extHostProtocol.MainThreadWebviewsShape;
|
||||
@@ -132,10 +182,12 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
||||
|
||||
public $onMessage(
|
||||
handle: extHostProtocol.WebviewHandle,
|
||||
message: any
|
||||
jsonMessage: string,
|
||||
...buffers: VSBuffer[]
|
||||
): void {
|
||||
const webview = this.getWebview(handle);
|
||||
if (webview) {
|
||||
const { message } = deserializeWebviewMessage(jsonMessage, buffers);
|
||||
webview._onMessageEmitter.fire(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import * as extHostProtocol from './extHost.protocol';
|
||||
|
||||
class ArrayBufferSet {
|
||||
public readonly buffers: ArrayBuffer[] = [];
|
||||
|
||||
public add(buffer: ArrayBuffer): number {
|
||||
let index = this.buffers.indexOf(buffer);
|
||||
if (index < 0) {
|
||||
index = this.buffers.length;
|
||||
this.buffers.push(buffer);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
export function serializeWebviewMessage(
|
||||
message: any,
|
||||
transfer?: readonly ArrayBuffer[]
|
||||
): { message: string, buffers: VSBuffer[] } {
|
||||
if (transfer) {
|
||||
// Extract all ArrayBuffers from the message and replace them with references.
|
||||
const arrayBuffers = new ArrayBufferSet();
|
||||
|
||||
const replacer = (_key: string, value: any) => {
|
||||
if (value instanceof ArrayBuffer) {
|
||||
const index = arrayBuffers.add(value);
|
||||
return <extHostProtocol.WebviewMessageArrayBufferReference>{
|
||||
$$vscode_array_buffer_reference$$: true,
|
||||
index,
|
||||
};
|
||||
} else if (ArrayBuffer.isView(value)) {
|
||||
const type = getTypedArrayType(value);
|
||||
if (type) {
|
||||
const index = arrayBuffers.add(value.buffer);
|
||||
return <extHostProtocol.WebviewMessageArrayBufferReference>{
|
||||
$$vscode_array_buffer_reference$$: true,
|
||||
index,
|
||||
view: {
|
||||
type: type,
|
||||
byteLength: value.byteLength,
|
||||
byteOffset: value.byteOffset,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
const serializedMessage = JSON.stringify(message, replacer);
|
||||
|
||||
const buffers = arrayBuffers.buffers.map(arrayBuffer => {
|
||||
const bytes = new Uint8Array(arrayBuffer);
|
||||
return VSBuffer.wrap(bytes);
|
||||
});
|
||||
|
||||
return { message: serializedMessage, buffers };
|
||||
} else {
|
||||
return { message: JSON.stringify(message), buffers: [] };
|
||||
}
|
||||
}
|
||||
|
||||
function getTypedArrayType(value: ArrayBufferView): extHostProtocol.WebviewMessageArrayBufferViewType | undefined {
|
||||
switch (value.constructor.name) {
|
||||
case 'Int8Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Int8Array;
|
||||
case 'Uint8Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Uint8Array;
|
||||
case 'Uint8ClampedArray': return extHostProtocol.WebviewMessageArrayBufferViewType.Uint8ClampedArray;
|
||||
case 'Int16Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Int16Array;
|
||||
case 'Uint16Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Uint16Array;
|
||||
case 'Int32Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Int32Array;
|
||||
case 'Uint32Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Uint32Array;
|
||||
case 'Float32Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Float32Array;
|
||||
case 'Float64Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Float64Array;
|
||||
case 'BigInt64Array': return extHostProtocol.WebviewMessageArrayBufferViewType.BigInt64Array;
|
||||
case 'BigUint64Array': return extHostProtocol.WebviewMessageArrayBufferViewType.BigUint64Array;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function deserializeWebviewMessage(jsonMessage: string, buffers: VSBuffer[]): { message: any, arrayBuffers: ArrayBuffer[] } {
|
||||
const arrayBuffers: ArrayBuffer[] = buffers.map(buffer => {
|
||||
const arrayBuffer = new ArrayBuffer(buffer.byteLength);
|
||||
const uint8Array = new Uint8Array(arrayBuffer);
|
||||
uint8Array.set(buffer.buffer);
|
||||
return arrayBuffer;
|
||||
});
|
||||
|
||||
const reviver = !buffers.length ? undefined : (_key: string, value: any) => {
|
||||
if (typeof value === 'object' && (value as extHostProtocol.WebviewMessageArrayBufferReference).$$vscode_array_buffer_reference$$) {
|
||||
const ref = value as extHostProtocol.WebviewMessageArrayBufferReference;
|
||||
const { index } = ref;
|
||||
const arrayBuffer = arrayBuffers[index];
|
||||
if (ref.view) {
|
||||
switch (ref.view.type) {
|
||||
case extHostProtocol.WebviewMessageArrayBufferViewType.Int8Array: return new Int8Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Int8Array.BYTES_PER_ELEMENT);
|
||||
case extHostProtocol.WebviewMessageArrayBufferViewType.Uint8Array: return new Uint8Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Uint8Array.BYTES_PER_ELEMENT);
|
||||
case extHostProtocol.WebviewMessageArrayBufferViewType.Uint8ClampedArray: return new Uint8ClampedArray(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Uint8ClampedArray.BYTES_PER_ELEMENT);
|
||||
case extHostProtocol.WebviewMessageArrayBufferViewType.Int16Array: return new Int16Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Int16Array.BYTES_PER_ELEMENT);
|
||||
case extHostProtocol.WebviewMessageArrayBufferViewType.Uint16Array: return new Uint16Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Uint16Array.BYTES_PER_ELEMENT);
|
||||
case extHostProtocol.WebviewMessageArrayBufferViewType.Int32Array: return new Int32Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Int32Array.BYTES_PER_ELEMENT);
|
||||
case extHostProtocol.WebviewMessageArrayBufferViewType.Uint32Array: return new Uint32Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Uint32Array.BYTES_PER_ELEMENT);
|
||||
case extHostProtocol.WebviewMessageArrayBufferViewType.Float32Array: return new Float32Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Float32Array.BYTES_PER_ELEMENT);
|
||||
case extHostProtocol.WebviewMessageArrayBufferViewType.Float64Array: return new Float64Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Float64Array.BYTES_PER_ELEMENT);
|
||||
case extHostProtocol.WebviewMessageArrayBufferViewType.BigInt64Array: return new BigInt64Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / BigInt64Array.BYTES_PER_ELEMENT);
|
||||
case extHostProtocol.WebviewMessageArrayBufferViewType.BigUint64Array: return new BigUint64Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / BigUint64Array.BYTES_PER_ELEMENT);
|
||||
default: throw new Error('Unknown array buffer view type');
|
||||
}
|
||||
}
|
||||
return arrayBuffer;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
const message = JSON.parse(jsonMessage, reviver);
|
||||
return { message, arrayBuffers };
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import { URI } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { serializeWebviewOptions, ExtHostWebview, ExtHostWebviews, toExtensionData } from 'vs/workbench/api/common/extHostWebview';
|
||||
import { serializeWebviewOptions, ExtHostWebview, ExtHostWebviews, toExtensionData, shouldSerializeBuffersForPostMessage } from 'vs/workbench/api/common/extHostWebview';
|
||||
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { EditorGroupColumn } from 'vs/workbench/common/editor';
|
||||
import type * as vscode from 'vscode';
|
||||
@@ -60,7 +60,7 @@ class ExtHostWebviewPanel extends Disposable implements vscode.WebviewPanel {
|
||||
this.#webview = webview;
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
public override dispose() {
|
||||
if (this.#isDisposed) {
|
||||
return;
|
||||
}
|
||||
@@ -199,11 +199,13 @@ export class ExtHostWebviewPanels implements extHostProtocol.ExtHostWebviewPanel
|
||||
preserveFocus: typeof showOptions === 'object' && !!showOptions.preserveFocus
|
||||
};
|
||||
|
||||
const serializeBuffersForPostMessage = shouldSerializeBuffersForPostMessage(extension);
|
||||
const handle = ExtHostWebviewPanels.newHandle();
|
||||
this._proxy.$createWebviewPanel(toExtensionData(extension), handle, viewType, {
|
||||
title,
|
||||
panelOptions: serializeWebviewPanelOptions(options),
|
||||
webviewOptions: serializeWebviewOptions(extension, this.workspace, options),
|
||||
serializeBuffersForPostMessage,
|
||||
}, webviewShowOptions);
|
||||
|
||||
const webview = this.webviews.createNewWebview(handle, options, extension);
|
||||
@@ -263,7 +265,9 @@ export class ExtHostWebviewPanels implements extHostProtocol.ExtHostWebviewPanel
|
||||
}
|
||||
|
||||
this._serializers.set(viewType, { serializer, extension });
|
||||
this._proxy.$registerSerializer(viewType);
|
||||
this._proxy.$registerSerializer(viewType, {
|
||||
serializeBuffersForPostMessage: shouldSerializeBuffersForPostMessage(extension)
|
||||
});
|
||||
|
||||
return new extHostTypes.Disposable(() => {
|
||||
this._serializers.delete(viewType);
|
||||
|
||||
@@ -43,7 +43,7 @@ class ExtHostWebviewView extends Disposable implements vscode.WebviewView {
|
||||
this.#isVisible = isVisible;
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
public override dispose() {
|
||||
if (this.#isDisposed) {
|
||||
return;
|
||||
}
|
||||
@@ -146,7 +146,10 @@ export class ExtHostWebviewViews implements extHostProtocol.ExtHostWebviewViewsS
|
||||
}
|
||||
|
||||
this._viewProviders.set(viewType, { provider, extension });
|
||||
this._proxy.$registerWebviewViewProvider(toExtensionData(extension), viewType, webviewOptions);
|
||||
this._proxy.$registerWebviewViewProvider(toExtensionData(extension), viewType, {
|
||||
retainContextWhenHidden: webviewOptions?.retainContextWhenHidden,
|
||||
serializeBuffersForPostMessage: false,
|
||||
});
|
||||
|
||||
return new extHostTypes.Disposable(() => {
|
||||
this._viewProviders.delete(viewType);
|
||||
|
||||
@@ -20,12 +20,11 @@ import { FileSystemProviderCapabilities } from 'vs/platform/files/common/files';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { Severity } from 'vs/platform/notification/common/notification';
|
||||
import { WorkspaceTrustStateChangeEvent } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { Range, RelativePattern, WorkspaceTrustState } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { Range, RelativePattern } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { ITextQueryBuilderOptions } from 'vs/workbench/contrib/search/common/queryBuilder';
|
||||
import { IRawFileMatch2, resultIsMatch } from 'vs/workbench/services/search/common/search';
|
||||
import * as vscode from 'vscode';
|
||||
@@ -169,8 +168,8 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
|
||||
private readonly _onDidChangeWorkspace = new Emitter<vscode.WorkspaceFoldersChangeEvent>();
|
||||
readonly onDidChangeWorkspace: Event<vscode.WorkspaceFoldersChangeEvent> = this._onDidChangeWorkspace.event;
|
||||
|
||||
private readonly _onDidChangeWorkspaceTrustState = new Emitter<vscode.WorkspaceTrustStateChangeEvent>();
|
||||
readonly onDidChangeWorkspaceTrustState: Event<vscode.WorkspaceTrustStateChangeEvent> = this._onDidChangeWorkspaceTrustState.event;
|
||||
private readonly _onDidGrantWorkspaceTrust = new Emitter<void>();
|
||||
readonly onDidGrantWorkspaceTrust: Event<void> = this._onDidGrantWorkspaceTrust.event;
|
||||
|
||||
private readonly _logService: ILogService;
|
||||
private readonly _requestIdProvider: Counter;
|
||||
@@ -185,7 +184,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
|
||||
|
||||
private readonly _activeSearchCallbacks: ((match: IRawFileMatch2) => any)[] = [];
|
||||
|
||||
private _workspaceTrustState: WorkspaceTrustState = WorkspaceTrustState.Unknown;
|
||||
private _trusted: boolean = false;
|
||||
|
||||
constructor(
|
||||
@IExtHostRpcService extHostRpc: IExtHostRpcService,
|
||||
@@ -204,8 +203,8 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
|
||||
this._confirmedWorkspace = data ? new ExtHostWorkspaceImpl(data.id, data.name, [], data.configuration ? URI.revive(data.configuration) : null, !!data.isUntitled, uri => ignorePathCasing(uri, extHostFileSystemInfo)) : undefined;
|
||||
}
|
||||
|
||||
$initializeWorkspace(data: IWorkspaceData | null, trustState: WorkspaceTrustState): void {
|
||||
this._workspaceTrustState = trustState;
|
||||
$initializeWorkspace(data: IWorkspaceData | null, trusted: boolean): void {
|
||||
this._trusted = trusted;
|
||||
this.$acceptWorkspaceData(data);
|
||||
this._barrier.open();
|
||||
}
|
||||
@@ -559,17 +558,20 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
|
||||
|
||||
// --- trust ---
|
||||
|
||||
get trustState(): WorkspaceTrustState {
|
||||
return this._workspaceTrustState;
|
||||
get trusted(): boolean {
|
||||
return this._trusted;
|
||||
}
|
||||
|
||||
requireWorkspaceTrust(options?: vscode.WorkspaceTrustRequestOptions): Promise<WorkspaceTrustState> {
|
||||
return this._proxy.$requireWorkspaceTrust(options);
|
||||
requestWorkspaceTrust(options?: vscode.WorkspaceTrustRequestOptions): Promise<boolean | undefined> {
|
||||
const promise = this._proxy.$requestWorkspaceTrust(options);
|
||||
return options?.modal ? promise : Promise.resolve(this._trusted);
|
||||
}
|
||||
|
||||
$onDidChangeWorkspaceTrustState(state: WorkspaceTrustStateChangeEvent): void {
|
||||
this._workspaceTrustState = state.currentTrustState;
|
||||
this._onDidChangeWorkspaceTrustState.fire(Object.freeze(state));
|
||||
$onDidGrantWorkspaceTrust(): void {
|
||||
if (!this._trusted) {
|
||||
this._trusted = true;
|
||||
this._onDidGrantWorkspaceTrust.fire();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -138,6 +138,12 @@ const apiMenus: IAPIMenu[] = [
|
||||
proposed: true,
|
||||
supportsSubmenus: false
|
||||
},
|
||||
{
|
||||
key: 'statusBar/remoteIndicator',
|
||||
id: MenuId.StatusBarRemoteIndicatorMenu,
|
||||
description: localize('menus.statusBarRemoteIndicator', "The remote indicator menu in the status bar"),
|
||||
supportsSubmenus: false
|
||||
},
|
||||
{
|
||||
key: 'view/title',
|
||||
id: MenuId.ViewTitle,
|
||||
@@ -212,6 +218,11 @@ const apiMenus: IAPIMenu[] = [
|
||||
key: 'ports/item/origin/inline',
|
||||
id: MenuId.TunnelOriginInline,
|
||||
description: localize('view.tunnelOriginInline', "The Ports view item origin inline menu")
|
||||
},
|
||||
{
|
||||
key: 'ports/item/port/inline',
|
||||
id: MenuId.TunnelPortInline,
|
||||
description: localize('view.tunnelPortInline', "The Ports view item port inline menu")
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -35,12 +35,6 @@ export interface StatusPipeArgs {
|
||||
type: 'status';
|
||||
}
|
||||
|
||||
export interface RunCommandPipeArgs {
|
||||
type: 'command';
|
||||
command: string;
|
||||
args: any[];
|
||||
}
|
||||
|
||||
export interface ExtensionManagementPipeArgs {
|
||||
type: 'extensionManagement';
|
||||
list?: { showVersions?: boolean, category?: string; };
|
||||
@@ -49,7 +43,7 @@ export interface ExtensionManagementPipeArgs {
|
||||
force?: boolean;
|
||||
}
|
||||
|
||||
export type PipeCommand = OpenCommandPipeArgs | StatusPipeArgs | RunCommandPipeArgs | OpenExternalCommandPipeArgs | ExtensionManagementPipeArgs;
|
||||
export type PipeCommand = OpenCommandPipeArgs | StatusPipeArgs | OpenExternalCommandPipeArgs | ExtensionManagementPipeArgs;
|
||||
|
||||
export interface ICommandsExecuter {
|
||||
executeCommand<T>(id: string, ...args: any[]): Promise<T>;
|
||||
@@ -106,10 +100,6 @@ export class CLIServerBase {
|
||||
case 'status':
|
||||
this.getStatus(data, res);
|
||||
break;
|
||||
case 'command':
|
||||
this.runCommand(data, res)
|
||||
.catch(this.logService.error);
|
||||
break;
|
||||
case 'extensionManagement':
|
||||
this.manageExtensions(data, res)
|
||||
.catch(this.logService.error);
|
||||
@@ -156,7 +146,7 @@ export class CLIServerBase {
|
||||
const waitMarkerFileURI = waitMarkerFilePath ? URI.file(waitMarkerFilePath) : undefined;
|
||||
const preferNewWindow = !forceReuseWindow && !waitMarkerFileURI && !addMode;
|
||||
const windowOpenArgs: IOpenWindowOptions = { forceNewWindow, diffMode, addMode, gotoLineMode, forceReuseWindow, preferNewWindow, waitMarkerFileURI };
|
||||
this._commands.executeCommand('_files.windowOpen', urisToOpen, windowOpenArgs);
|
||||
this._commands.executeCommand('_remoteCLI.windowOpen', urisToOpen, windowOpenArgs);
|
||||
}
|
||||
res.writeHead(200);
|
||||
res.end();
|
||||
@@ -197,7 +187,7 @@ export class CLIServerBase {
|
||||
|
||||
private async getStatus(data: StatusPipeArgs, res: http.ServerResponse) {
|
||||
try {
|
||||
const status = await this._commands.executeCommand('_issues.getSystemStatus');
|
||||
const status = await this._commands.executeCommand('_remoteCLI.getSystemStatus');
|
||||
res.writeHead(200);
|
||||
res.write(status);
|
||||
res.end();
|
||||
@@ -212,28 +202,6 @@ export class CLIServerBase {
|
||||
}
|
||||
}
|
||||
|
||||
private async runCommand(data: RunCommandPipeArgs, res: http.ServerResponse) {
|
||||
try {
|
||||
const { command, args } = data;
|
||||
const result = await this._commands.executeCommand(command, ...args);
|
||||
res.writeHead(200);
|
||||
res.write(JSON.stringify(result), err => {
|
||||
if (err) {
|
||||
this.logService.error(err);
|
||||
}
|
||||
});
|
||||
res.end();
|
||||
} catch (err) {
|
||||
res.writeHead(500);
|
||||
res.write(String(err), err => {
|
||||
if (err) {
|
||||
this.logService.error(err);
|
||||
}
|
||||
});
|
||||
res.end();
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._server.close();
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ import { createCancelablePromise, firstParallel } from 'vs/base/common/async';
|
||||
|
||||
export class ExtHostDebugService extends ExtHostDebugServiceBase {
|
||||
|
||||
readonly _serviceBrand: undefined;
|
||||
override readonly _serviceBrand: undefined;
|
||||
|
||||
private _integratedTerminalInstances = new DebugTerminalCollection();
|
||||
private _terminalDisposedListener: IDisposable | undefined;
|
||||
@@ -43,7 +43,7 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
|
||||
super(extHostRpcService, workspaceService, extensionService, editorsService, configurationService);
|
||||
}
|
||||
|
||||
protected createDebugAdapter(adapter: IAdapterDescriptor, session: ExtHostDebugSession): AbstractDebugAdapter | undefined {
|
||||
protected override createDebugAdapter(adapter: IAdapterDescriptor, session: ExtHostDebugSession): AbstractDebugAdapter | undefined {
|
||||
switch (adapter.type) {
|
||||
case 'server':
|
||||
return new SocketDebugAdapter(adapter);
|
||||
@@ -55,7 +55,7 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
|
||||
return super.createDebugAdapter(adapter, session);
|
||||
}
|
||||
|
||||
protected daExecutableFromPackage(session: ExtHostDebugSession, extensionRegistry: ExtensionDescriptionRegistry): DebugAdapterExecutable | undefined {
|
||||
protected override daExecutableFromPackage(session: ExtHostDebugSession, extensionRegistry: ExtensionDescriptionRegistry): DebugAdapterExecutable | undefined {
|
||||
const dae = ExecutableDebugAdapter.platformAdapterExecutable(extensionRegistry.getAllExtensionDescriptions(), session.type);
|
||||
if (dae) {
|
||||
return new DebugAdapterExecutable(dae.command, dae.args, dae.options);
|
||||
@@ -63,11 +63,11 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
protected createSignService(): ISignService | undefined {
|
||||
protected override createSignService(): ISignService | undefined {
|
||||
return new SignService();
|
||||
}
|
||||
|
||||
public async $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, sessionId: string): Promise<number | undefined> {
|
||||
public override async $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, sessionId: string): Promise<number | undefined> {
|
||||
|
||||
if (args.kind === 'integrated') {
|
||||
|
||||
|
||||
@@ -47,23 +47,23 @@ export class ExtHostOutputChannelBackedByFile extends AbstractExtHostOutputChann
|
||||
this._appender = appender;
|
||||
}
|
||||
|
||||
append(value: string): void {
|
||||
override append(value: string): void {
|
||||
super.append(value);
|
||||
this._appender.append(value);
|
||||
this._onDidAppend.fire();
|
||||
}
|
||||
|
||||
update(): void {
|
||||
override update(): void {
|
||||
this._appender.flush();
|
||||
super.update();
|
||||
}
|
||||
|
||||
show(columnOrPreserveFocus?: vscode.ViewColumn | boolean, preserveFocus?: boolean): void {
|
||||
override show(columnOrPreserveFocus?: vscode.ViewColumn | boolean, preserveFocus?: boolean): void {
|
||||
this._appender.flush();
|
||||
super.show(columnOrPreserveFocus, preserveFocus);
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
override clear(): void {
|
||||
this._appender.flush();
|
||||
super.clear();
|
||||
}
|
||||
@@ -85,7 +85,7 @@ export class ExtHostOutputService2 extends ExtHostOutputService {
|
||||
this._logsLocation = initData.logsLocation;
|
||||
}
|
||||
|
||||
$setVisibleChannel(channelId: string): void {
|
||||
override $setVisibleChannel(channelId: string): void {
|
||||
if (channelId) {
|
||||
const channel = this._channels.get(channelId);
|
||||
if (channel) {
|
||||
@@ -94,7 +94,7 @@ export class ExtHostOutputService2 extends ExtHostOutputService {
|
||||
}
|
||||
}
|
||||
|
||||
createOutputChannel(name: string): vscode.OutputChannel {
|
||||
override createOutputChannel(name: string): vscode.OutputChannel {
|
||||
name = name.trim();
|
||||
if (!name) {
|
||||
throw new Error('illegal argument `name`. must not be falsy');
|
||||
|
||||
@@ -59,7 +59,7 @@ export class NativeExtHostSearch extends ExtHostSearch {
|
||||
});
|
||||
}
|
||||
|
||||
$provideFileSearchResults(handle: number, session: number, rawQuery: IRawFileQuery, token: vscode.CancellationToken): Promise<ISearchCompleteStats> {
|
||||
override $provideFileSearchResults(handle: number, session: number, rawQuery: IRawFileQuery, token: vscode.CancellationToken): Promise<ISearchCompleteStats> {
|
||||
const query = reviveQuery(rawQuery);
|
||||
if (handle === this._internalFileSearchHandle) {
|
||||
return this.doInternalFileSearch(handle, session, query, token);
|
||||
@@ -91,7 +91,7 @@ export class NativeExtHostSearch extends ExtHostSearch {
|
||||
return <Promise<ISearchCompleteStats>>this._internalFileSearchProvider.doFileSearch(rawQuery, onResult, token);
|
||||
}
|
||||
|
||||
$clearCache(cacheKey: string): Promise<void> {
|
||||
override $clearCache(cacheKey: string): Promise<void> {
|
||||
if (this._internalFileSearchProvider) {
|
||||
this._internalFileSearchProvider.clearCache(cacheKey);
|
||||
}
|
||||
@@ -99,7 +99,7 @@ export class NativeExtHostSearch extends ExtHostSearch {
|
||||
return super.$clearCache(cacheKey);
|
||||
}
|
||||
|
||||
protected createTextSearchManager(query: ITextQuery, provider: vscode.TextSearchProvider): TextSearchManager {
|
||||
protected override createTextSearchManager(query: ITextQuery, provider: vscode.TextSearchProvider): TextSearchManager {
|
||||
return new NativeTextSearchManager(query, provider);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,12 @@ export class ExtHostTask extends ExtHostTaskBase {
|
||||
authority: initData.remote.authority,
|
||||
platform: process.platform
|
||||
});
|
||||
} else {
|
||||
this.registerTaskSystem(Schemas.file, {
|
||||
scheme: Schemas.file,
|
||||
authority: '',
|
||||
platform: process.platform
|
||||
});
|
||||
}
|
||||
this._proxy.$registerSupportedExecutions(true, true, true);
|
||||
}
|
||||
@@ -147,19 +153,19 @@ export class ExtHostTask extends ExtHostTaskBase {
|
||||
}
|
||||
};
|
||||
for (let variable of toResolve.variables) {
|
||||
result.variables[variable] = resolver.resolve(ws, variable);
|
||||
result.variables[variable] = await resolver.resolveAsync(ws, variable);
|
||||
}
|
||||
if (toResolve.process !== undefined) {
|
||||
let paths: string[] | undefined = undefined;
|
||||
if (toResolve.process.path !== undefined) {
|
||||
paths = toResolve.process.path.split(path.delimiter);
|
||||
for (let i = 0; i < paths.length; i++) {
|
||||
paths[i] = resolver.resolve(ws, paths[i]);
|
||||
paths[i] = await resolver.resolveAsync(ws, paths[i]);
|
||||
}
|
||||
}
|
||||
result.process = await win32.findExecutable(
|
||||
resolver.resolve(ws, toResolve.process.name),
|
||||
toResolve.process.cwd !== undefined ? resolver.resolve(ws, toResolve.process.cwd) : undefined,
|
||||
await resolver.resolveAsync(ws, toResolve.process.name),
|
||||
toResolve.process.cwd !== undefined ? await resolver.resolveAsync(ws, toResolve.process.cwd) : undefined,
|
||||
paths
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { getSystemShell, getSystemShellSync } from 'vs/base/node/shell';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { SafeConfigProvider } from 'vs/platform/terminal/common/terminal';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { IShellAndArgsDto } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostConfigProvider, ExtHostConfiguration, IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
|
||||
@@ -16,7 +17,7 @@ import { ExtHostDocumentsAndEditors, IExtHostDocumentsAndEditors } from 'vs/work
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { BaseExtHostTerminalService, ExtHostTerminal } from 'vs/workbench/api/common/extHostTerminalService';
|
||||
import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { ITerminalConfiguration, ITerminalProfile } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { ITerminalProfile } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
|
||||
import { detectAvailableProfiles } from 'vs/workbench/contrib/terminal/node/terminalProfiles';
|
||||
import type * as vscode from 'vscode';
|
||||
@@ -27,8 +28,6 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
private _variableResolverPromise: Promise<ExtHostVariableResolverService>;
|
||||
private _lastActiveWorkspace: IWorkspaceFolder | undefined;
|
||||
|
||||
// TODO: Pull this from main side
|
||||
private _isWorkspaceShellAllowed: boolean = false;
|
||||
private _defaultShell: string | undefined;
|
||||
|
||||
constructor(
|
||||
@@ -43,7 +42,7 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
// Getting the SystemShell is an async operation, however, the ExtHost terminal service is mostly synchronous
|
||||
// and the API `vscode.env.shell` is also synchronous. The default shell _should_ be set when extensions are
|
||||
// starting up but if not, we run getSystemShellSync below which gets a sane default.
|
||||
getSystemShell(platform.platform, process.env as platform.IProcessEnvironment).then(s => this._defaultShell = s);
|
||||
getSystemShell(platform.OS, process.env as platform.IProcessEnvironment).then(s => this._defaultShell = s);
|
||||
|
||||
this._updateLastActiveWorkspace();
|
||||
this._variableResolverPromise = this._updateVariableResolver();
|
||||
@@ -65,6 +64,8 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
withNullAsUndefined(options.shellArgs),
|
||||
withNullAsUndefined(options.cwd),
|
||||
withNullAsUndefined(options.env),
|
||||
withNullAsUndefined(options.icon),
|
||||
withNullAsUndefined(options.message),
|
||||
/*options.waitOnExit*/ undefined,
|
||||
withNullAsUndefined(options.strictEnv),
|
||||
withNullAsUndefined(options.hideFromUser),
|
||||
@@ -75,44 +76,24 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
}
|
||||
|
||||
public getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string {
|
||||
const fetchSetting = (key: string): { userValue: string | string[] | undefined, value: string | string[] | undefined, defaultValue: string | string[] | undefined } => {
|
||||
const setting = configProvider
|
||||
.getConfiguration(key.substr(0, key.lastIndexOf('.')))
|
||||
.inspect<string | string[]>(key.substr(key.lastIndexOf('.') + 1));
|
||||
return this._apiInspectConfigToPlain<string | string[]>(setting);
|
||||
};
|
||||
|
||||
return terminalEnvironment.getDefaultShell(
|
||||
fetchSetting,
|
||||
this._isWorkspaceShellAllowed,
|
||||
this._defaultShell ?? getSystemShellSync(platform.platform, process.env as platform.IProcessEnvironment),
|
||||
this._buildSafeConfigProvider(configProvider),
|
||||
this._defaultShell ?? getSystemShellSync(platform.OS, process.env as platform.IProcessEnvironment),
|
||||
process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432'),
|
||||
process.env.windir,
|
||||
terminalEnvironment.createVariableResolver(this._lastActiveWorkspace, this._variableResolver),
|
||||
terminalEnvironment.createVariableResolver(this._lastActiveWorkspace, process.env, this._variableResolver),
|
||||
this._logService,
|
||||
useAutomationShell
|
||||
);
|
||||
}
|
||||
|
||||
public getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string {
|
||||
const fetchSetting = (key: string): { userValue: string | string[] | undefined, value: string | string[] | undefined, defaultValue: string | string[] | undefined } => {
|
||||
const setting = configProvider
|
||||
.getConfiguration(key.substr(0, key.lastIndexOf('.')))
|
||||
.inspect<string | string[]>(key.substr(key.lastIndexOf('.') + 1));
|
||||
return this._apiInspectConfigToPlain<string | string[]>(setting);
|
||||
};
|
||||
|
||||
return terminalEnvironment.getDefaultShellArgs(fetchSetting, this._isWorkspaceShellAllowed, useAutomationShell, terminalEnvironment.createVariableResolver(this._lastActiveWorkspace, this._variableResolver), this._logService);
|
||||
}
|
||||
|
||||
private _apiInspectConfigToPlain<T>(
|
||||
config: { key: string; defaultValue?: T; globalValue?: T; workspaceValue?: T, workspaceFolderValue?: T } | undefined
|
||||
): { userValue: T | undefined, value: T | undefined, defaultValue: T | undefined } {
|
||||
return {
|
||||
userValue: config ? config.globalValue : undefined,
|
||||
value: config ? config.workspaceValue : undefined,
|
||||
defaultValue: config ? config.defaultValue : undefined,
|
||||
};
|
||||
return terminalEnvironment.getDefaultShellArgs(
|
||||
this._buildSafeConfigProvider(configProvider),
|
||||
useAutomationShell,
|
||||
terminalEnvironment.createVariableResolver(this._lastActiveWorkspace, process.env, this._variableResolver),
|
||||
this._logService
|
||||
);
|
||||
}
|
||||
|
||||
private _registerListeners(): void {
|
||||
@@ -136,9 +117,9 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
return this._variableResolver;
|
||||
}
|
||||
|
||||
public async $getAvailableProfiles(quickLaunchOnly: boolean): Promise<ITerminalProfile[]> {
|
||||
const config = await (await this._extHostConfiguration.getConfigProvider()).getConfiguration().get('terminal.integrated');
|
||||
return detectAvailableProfiles(quickLaunchOnly, this._logService, config as ITerminalConfiguration, await this._variableResolverPromise, this._lastActiveWorkspace);
|
||||
public async $getAvailableProfiles(configuredProfilesOnly: boolean): Promise<ITerminalProfile[]> {
|
||||
const safeConfigProvider = this._buildSafeConfigProvider(await this._extHostConfiguration.getConfigProvider());
|
||||
return detectAvailableProfiles(configuredProfilesOnly, safeConfigProvider, undefined, this._logService, await this._variableResolverPromise, this._lastActiveWorkspace);
|
||||
}
|
||||
|
||||
public async $getDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto> {
|
||||
@@ -149,7 +130,16 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
};
|
||||
}
|
||||
|
||||
public $acceptWorkspacePermissionsChanged(isAllowed: boolean): void {
|
||||
this._isWorkspaceShellAllowed = isAllowed;
|
||||
// TODO: Remove when workspace trust is enabled
|
||||
private _buildSafeConfigProvider(configProvider: ExtHostConfigProvider): SafeConfigProvider {
|
||||
const config = configProvider.getConfiguration();
|
||||
return (key: string) => {
|
||||
const isWorkspaceConfigAllowed = config.get('terminal.integrated.allowWorkspaceConfiguration');
|
||||
if (isWorkspaceConfigAllowed) {
|
||||
return config.get(key) as any;
|
||||
}
|
||||
const inspected = config.inspect(key);
|
||||
return inspected?.globalValue || inspected?.defaultValue;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'
|
||||
import { MovingAverage } from 'vs/base/common/numbers';
|
||||
import { CandidatePort } from 'vs/workbench/services/remote/common/remoteExplorerService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { flatten } from 'vs/base/common/arrays';
|
||||
|
||||
class ExtensionTunnel implements vscode.Tunnel {
|
||||
private _onDispose: Emitter<void> = new Emitter();
|
||||
@@ -203,7 +202,7 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
}
|
||||
|
||||
async openTunnel(extension: IExtensionDescription, forward: TunnelOptions): Promise<vscode.Tunnel | undefined> {
|
||||
this.logService.trace(`ForwardedPorts: (ExtHostTunnelService) ${extension.identifier} called openTunnel API for ${forward.remoteAddress.port}.`);
|
||||
this.logService.trace(`ForwardedPorts: (ExtHostTunnelService) ${extension.identifier.value} called openTunnel API for ${forward.remoteAddress.host}:${forward.remoteAddress.port}.`);
|
||||
const tunnel = await this._proxy.$openTunnel(forward, extension.displayName);
|
||||
if (tunnel) {
|
||||
const disposableTunnel: vscode.Tunnel = new ExtensionTunnel(tunnel.remoteAddress, tunnel.localAddress, () => {
|
||||
@@ -240,17 +239,20 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
}
|
||||
|
||||
async $providePortAttributes(handles: number[], ports: number[], pid: number | undefined, commandline: string | undefined, cancellationToken: vscode.CancellationToken): Promise<ProvidedPortAttributes[]> {
|
||||
const providedAttributes = await Promise.all(handles.map(handle => {
|
||||
const providedAttributes: vscode.ProviderResult<vscode.PortAttributes>[] = [];
|
||||
for (const handle of handles) {
|
||||
const provider = this._portAttributesProviders.get(handle);
|
||||
if (!provider) {
|
||||
return [];
|
||||
}
|
||||
return provider.provider.providePortAttributes(ports, pid, commandline, cancellationToken);
|
||||
}));
|
||||
providedAttributes.push(...(await Promise.all(ports.map(async (port) => {
|
||||
return provider.provider.providePortAttributes(port, pid, commandline, cancellationToken);
|
||||
}))));
|
||||
}
|
||||
|
||||
const allAttributes = <vscode.PortAttributes[][]>providedAttributes.filter(attribute => !!attribute && attribute.length > 0);
|
||||
const allAttributes = <vscode.PortAttributes[]>providedAttributes.filter(attribute => !!attribute);
|
||||
|
||||
return (allAttributes.length > 0) ? flatten(allAttributes).map(attributes => {
|
||||
return (allAttributes.length > 0) ? allAttributes.map(attributes => {
|
||||
return {
|
||||
autoForwardAction: <ProvidedOnAutoForward><unknown>attributes.autoForwardAction,
|
||||
port: attributes.port
|
||||
@@ -282,17 +284,19 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
}
|
||||
|
||||
async setTunnelExtensionFunctions(provider: vscode.RemoteAuthorityResolver | undefined): Promise<IDisposable> {
|
||||
// Do not wait for any of the proxy promises here.
|
||||
// It will delay startup and there is nothing that needs to be waited for.
|
||||
if (provider) {
|
||||
if (provider.candidatePortSource !== undefined) {
|
||||
await this._proxy.$setCandidatePortSource(provider.candidatePortSource);
|
||||
this._proxy.$setCandidatePortSource(provider.candidatePortSource);
|
||||
}
|
||||
if (provider.showCandidatePort) {
|
||||
this._showCandidatePort = provider.showCandidatePort;
|
||||
await this._proxy.$setCandidateFilter();
|
||||
this._proxy.$setCandidateFilter();
|
||||
}
|
||||
if (provider.tunnelFactory) {
|
||||
this._forwardPortProvider = provider.tunnelFactory;
|
||||
await this._proxy.$setTunnelProvider(provider.tunnelFeatures ?? {
|
||||
this._proxy.$setTunnelProvider(provider.tunnelFeatures ?? {
|
||||
elevation: false,
|
||||
public: false
|
||||
});
|
||||
|
||||
@@ -70,7 +70,7 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
|
||||
|
||||
protected async _beforeAlmostReadyToRunExtensions(): Promise<void> {
|
||||
const mainThreadConsole = this._extHostContext.getProxy(MainContext.MainThreadConsole);
|
||||
wrapConsoleMethods(mainThreadConsole);
|
||||
wrapConsoleMethods(mainThreadConsole, this._initData.environment.isExtensionDevelopmentDebug);
|
||||
|
||||
// initialize API and register actors
|
||||
const apiFactory = this._instaService.invokeFunction(createApiFactoryAndRegisterActors);
|
||||
@@ -173,15 +173,19 @@ function ensureSuffix(path: string, suffix: string): string {
|
||||
}
|
||||
|
||||
// copied from bootstrap-fork.js
|
||||
function wrapConsoleMethods(service: MainThreadConsoleShape) {
|
||||
function wrapConsoleMethods(service: MainThreadConsoleShape, callToNative: boolean) {
|
||||
wrap('info', 'log');
|
||||
wrap('log', 'log');
|
||||
wrap('warn', 'warn');
|
||||
wrap('error', 'error');
|
||||
|
||||
function wrap(method: 'error' | 'warn' | 'info' | 'log', severity: 'error' | 'warn' | 'log') {
|
||||
const original = console[method];
|
||||
console[method] = function () {
|
||||
service.$logExtensionHostMessage({ type: '__$console', severity, arguments: safeToArray(arguments) });
|
||||
if (callToNative) {
|
||||
original.apply(console, arguments as any);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user