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());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user