chore(vscode): update to 1.56.0

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

View File

@@ -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);
}
});
}

View File

@@ -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 {

View File

@@ -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;
});
}
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -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();
});

View File

@@ -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

View File

@@ -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> {

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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)
};
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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 };

View File

@@ -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);

View File

@@ -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));
}
}

View File

@@ -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;
}
}

View File

@@ -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) {

View File

@@ -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 {
}
}

View File

@@ -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();
}

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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 });
}
}

View File

@@ -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();
}
}

View File

@@ -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());
}