From ca7f2683fda363d848b3bb12b0b2b8f0fa4fd158 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 16 Jun 2020 17:20:53 -0700 Subject: [PATCH] resolve notebook editor with comm object. --- src/vs/vscode.proposed.d.ts | 20 ++++- .../api/browser/mainThreadNotebook.ts | 4 + .../workbench/api/common/extHost.protocol.ts | 1 + .../workbench/api/common/extHostNotebook.ts | 88 +++++++++++++++---- .../notebook/browser/notebookServiceImpl.ts | 5 ++ .../notebook/common/notebookService.ts | 1 + 6 files changed, 102 insertions(+), 17 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 5b7f7b102c4..4321568d6a7 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1639,7 +1639,25 @@ declare module 'vscode' { export interface NotebookContentProvider { openNotebook(uri: Uri, openContext: NotebookDocumentOpenContext): NotebookData | Promise; - resolveNotebook(document: NotebookDocument): Promise; + resolveNotebook(document: NotebookDocument, webview: { + /** + * Fired when the output hosting webview posts a message. + */ + readonly onDidReceiveMessage: Event; + /** + * Post a message to the output hosting webview. + * + * Messages are only delivered if the editor is live. + * + * @param message Body of the message. This must be a string or other json serilizable object. + */ + postMessage(message: any): Thenable; + + /** + * Convert a uri for the local file system to one that can be used inside outputs webview. + */ + asWebviewUri(localResource: Uri): Uri; + }): Promise; saveNotebook(document: NotebookDocument, cancellation: CancellationToken): Promise; saveNotebookAs(targetResource: Uri, document: NotebookDocument, cancellation: CancellationToken): Promise; readonly onDidChangeNotebook: Event; diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index 562c464ad58..fefa481e06d 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -588,6 +588,10 @@ export class MainThreadNotebookController implements IMainNotebookController { return document.textModel; } + async resolveNotebookEditor(viewType: string, uri: URI, editorId: string) { + await this._proxy.$resolveNotebookEditor(viewType, uri, editorId); + } + async tryApplyEdits(resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[], renderers: number[]): Promise { let mainthreadNotebook = this._mapping.get(URI.from(resource).toString()); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index c56bd11806d..6936e77dd34 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1582,6 +1582,7 @@ export interface INotebookDocumentsAndEditorsDelta { export interface ExtHostNotebookShape { $resolveNotebookData(viewType: string, uri: UriComponents, backupId?: string): Promise; + $resolveNotebookEditor(viewType: string, uri: UriComponents, editorId: string): Promise; $executeNotebook(viewType: string, uri: UriComponents, cellHandle: number | undefined, useAttachedKernel: boolean, token: CancellationToken): Promise; $executeNotebook2(kernelId: string, viewType: string, uri: UriComponents, cellHandle: number | undefined, token: CancellationToken): Promise; $saveNotebook(viewType: string, uri: UriComponents, token: CancellationToken): Promise; diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index c94db846842..d2d4cc47c3d 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -515,6 +515,30 @@ export class NotebookEditorCellEditBuilder implements vscode.NotebookEditorCellE } } +class ExtHostWebviewComm extends Disposable { + + onDidReceiveMessage: vscode.Event = this._onDidReceiveMessage.event; + + constructor( + readonly id: string, + public uri: URI, + private _proxy: MainThreadNotebookShape, + private _onDidReceiveMessage: Emitter, + private _webviewInitData: WebviewInitData, + public document: ExtHostNotebookDocument, + ) { + super(); + } + + async postMessage(message: any): Promise { + return this._proxy.$postMessage(this.document.handle, message); + } + + asWebviewUri(localResource: vscode.Uri): vscode.Uri { + return asWebviewUri(this._webviewInitData, this.id, localResource); + } +} + export class ExtHostNotebookEditor extends Disposable implements vscode.NotebookEditor { private _viewColumn: vscode.ViewColumn | undefined; @@ -548,6 +572,7 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook private _onDidDispose = new Emitter(); readonly onDidDispose: Event = this._onDidDispose.event; + private _onDidReceiveMessage = new Emitter(); onDidReceiveMessage: vscode.Event = this._onDidReceiveMessage.event; constructor( @@ -555,8 +580,7 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook readonly id: string, public uri: URI, private _proxy: MainThreadNotebookShape, - private _onDidReceiveMessage: Emitter, - private _webviewInitData: WebviewInitData, + private _webComm: ExtHostWebviewComm, public document: ExtHostNotebookDocument, private _documentsAndEditors: ExtHostDocumentsAndEditors ) { @@ -582,6 +606,10 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook } } })); + + this._register(this._webComm.onDidReceiveMessage(e => { + this._onDidReceiveMessage.fire(e); + })); } edit(callback: (editBuilder: NotebookEditorCellEditBuilder) => void): Thenable { @@ -641,11 +669,11 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook } async postMessage(message: any): Promise { - return this._proxy.$postMessage(this.document.handle, message); + return this._webComm.postMessage(message); } asWebviewUri(localResource: vscode.Uri): vscode.Uri { - return asWebviewUri(this._webviewInitData, this.id, localResource); + return this._webComm.asWebviewUri(localResource); } dispose() { this._onDidDispose.fire(); @@ -692,7 +720,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN private readonly _notebookKernels = new Map(); private readonly _documents = new Map(); private readonly _unInitializedDocuments = new Map(); - private readonly _editors = new Map; }>(); + private readonly _editors = new Map(); + private readonly _webviewComm = new Map }>(); private readonly _notebookOutputRenderers = new Map(); private readonly _onDidChangeNotebookCells = new Emitter(); readonly onDidChangeNotebookCells = this._onDidChangeNotebookCells.event; @@ -946,6 +975,30 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN return; } + async $resolveNotebookEditor(viewType: string, uri: UriComponents, editorId: string): Promise { + const provider = this._notebookContentProviders.get(viewType); + const revivedUri = URI.revive(uri); + const document = this._documents.get(revivedUri.toString()); + if (!document || !provider) { + return; + } + + if (!provider.provider.resolveNotebook) { + return; + } + + let webComm = this._webviewComm.get(editorId)?.comm; + + if (webComm) { + await provider.provider.resolveNotebook(document, webComm); + } else { + const onDidReceiveMessage = new Emitter(); + webComm = new ExtHostWebviewComm(editorId, revivedUri, this._proxy, onDidReceiveMessage, this._webviewInitData, document); + this._webviewComm.set(editorId, { comm: webComm, onDidReceiveMessage }); + await provider.provider.resolveNotebook(document, webComm); + } + } + async $executeNotebook(viewType: string, uri: UriComponents, cellHandle: number | undefined, useAttachedKernel: boolean, token: CancellationToken): Promise { let document = this._documents.get(URI.revive(uri).toString()); @@ -1047,7 +1100,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN // TODO: remove document - editor one on one mapping private _getEditorFromURI(uriComponents: UriComponents) { const uriStr = URI.revive(uriComponents).toString(); - let editor: { editor: ExtHostNotebookEditor, onDidReceiveMessage: Emitter; } | undefined; + let editor: { editor: ExtHostNotebookEditor } | undefined; this._editors.forEach(e => { if (e.editor.uri.toString() === uriStr) { editor = e; @@ -1058,10 +1111,10 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } $onDidReceiveMessage(editorId: string, message: any): void { - let editor = this._editors.get(editorId); + let messageEmitter = this._webviewComm.get(editorId)?.onDidReceiveMessage; - if (editor) { - editor.onDidReceiveMessage.fire(message); + if (messageEmitter) { + messageEmitter.fire(message); } } @@ -1100,16 +1153,21 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } private _createExtHostEditor(document: ExtHostNotebookDocument, editorId: string, selections: number[]) { - const onDidReceiveMessage = new Emitter(); const revivedUri = document.uri; + let webComm = this._webviewComm.get(editorId)?.comm; + + if (!webComm) { + const onDidReceiveMessage = new Emitter(); + webComm = new ExtHostWebviewComm(editorId, revivedUri, this._proxy, onDidReceiveMessage, this._webviewInitData, document); + this._webviewComm.set(editorId, { comm: webComm!, onDidReceiveMessage }); + } let editor = new ExtHostNotebookEditor( document.viewType, editorId, revivedUri, this._proxy, - onDidReceiveMessage, - this._webviewInitData, + webComm, document, this._documentsAndEditors ); @@ -1125,7 +1183,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN this._editors.get(editorId)?.editor.dispose(); - this._editors.set(editorId, { editor, onDidReceiveMessage }); + this._editors.set(editorId, { editor }); } async $acceptDocumentAndEditorsDelta(delta: INotebookDocumentsAndEditorsDelta) { @@ -1146,7 +1204,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN [...this._editors.values()].forEach((e) => { if (e.editor.uri.toString() === revivedUriStr) { e.editor.dispose(); - e.onDidReceiveMessage.dispose(); this._editors.delete(e.editor.id); editorChanged = true; } @@ -1229,7 +1286,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN }); } - const removedEditors: { editor: ExtHostNotebookEditor, onDidReceiveMessage: Emitter; }[] = []; + const removedEditors: { editor: ExtHostNotebookEditor }[] = []; if (delta.removedEditors) { delta.removedEditors.forEach(editorid => { @@ -1251,7 +1308,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN if (editorChanged) { removedEditors.forEach(e => { e.editor.dispose(); - e.onDidReceiveMessage.dispose(); }); } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index fae81aeb059..e5c72709ef9 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -382,6 +382,11 @@ export class NotebookService extends Disposable implements INotebookService, ICu this._onNotebookDocumentAdd.fire([notebookModel!.uri]); // after the document is added to the store and sent to ext host, we transform the ouputs await this.transformTextModelOutputs(notebookModel!); + + if (editorId) { + await provider.controller.resolveNotebookEditor(viewType, uri, editorId); + } + return modelData.model; } diff --git a/src/vs/workbench/contrib/notebook/common/notebookService.ts b/src/vs/workbench/contrib/notebook/common/notebookService.ts index b1d5333d626..74c13fe3984 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookService.ts @@ -20,6 +20,7 @@ export const INotebookService = createDecorator('notebookServi export interface IMainNotebookController { kernel: INotebookKernelInfoDto | undefined; createNotebook(viewType: string, uri: URI, backup: INotebookTextModelBackup | undefined, forceReload: boolean, editorId?: string, backupId?: string): Promise; + resolveNotebookEditor(viewType: string, uri: URI, editorId: string): Promise; executeNotebook(viewType: string, uri: URI, useAttachedKernel: boolean, token: CancellationToken): Promise; onDidReceiveMessage(editorId: string, message: any): void; executeNotebookCell(uri: URI, handle: number, useAttachedKernel: boolean, token: CancellationToken): Promise;