diff --git a/extensions/vscode-notebook-tests/src/customRenderer.js b/extensions/vscode-notebook-tests/src/customRenderer.js index f23538e38a7..94fd028eee7 100644 --- a/extensions/vscode-notebook-tests/src/customRenderer.js +++ b/extensions/vscode-notebook-tests/src/customRenderer.js @@ -5,14 +5,7 @@ const vscode = acquireVsCodeApi(); -vscode.postMessage({ - type: 'custom_renderer_initialize', - payload: { - firstMessage: true - } -}); - -const notebook = acquireNotebookRendererApi('notebookCoreTestRenderer'); +const notebook = acquireNotebookRendererApi(); notebook.onDidCreateOutput(({ element, mimeType }) => { const div = document.createElement('div'); diff --git a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts index 308e0964ae0..df405e14fda 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts @@ -122,9 +122,6 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape private _onEditorAdd(editor: INotebookEditor) { const ipcListener = editor.onDidReceiveMessage(e => { - if (e.forRenderer) { - return; - } if (!editor.hasModel()) { return; } @@ -134,7 +131,7 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape } for (let [handle, candidate] of this._kernels) { if (candidate[0] === selected) { - this._proxy.$acceptRendererMessage(handle, editor.getId(), e.message); + this._proxy.$acceptKernelMessageFromRenderer(handle, editor.getId(), e.message); break; } } @@ -164,11 +161,11 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape } if (editorId === undefined) { // all editors - editor.postMessage(undefined, message); + editor.postMessage(message); didSend = true; } else if (editor.getId() === editorId) { // selected editors - editor.postMessage(undefined, message); + editor.postMessage(message); didSend = true; break; } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index d20fb626c86..1907f1113b2 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1955,7 +1955,7 @@ export interface ExtHostNotebookKernelsShape { $acceptSelection(handle: number, uri: UriComponents, value: boolean): void; $executeCells(handle: number, uri: UriComponents, handles: number[]): Promise; $cancelCells(handle: number, uri: UriComponents, handles: number[]): Promise; - $acceptRendererMessage(handle: number, editorId: string, message: any): void; + $acceptKernelMessageFromRenderer(handle: number, editorId: string, message: any): void; } export interface ExtHostStorageShape { diff --git a/src/vs/workbench/api/common/extHostNotebookKernels.ts b/src/vs/workbench/api/common/extHostNotebookKernels.ts index add0d63d666..f2835cd5b86 100644 --- a/src/vs/workbench/api/common/extHostNotebookKernels.ts +++ b/src/vs/workbench/api/common/extHostNotebookKernels.ts @@ -242,7 +242,7 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { } } - $acceptRendererMessage(handle: number, editorId: string, message: any): void { + $acceptKernelMessageFromRenderer(handle: number, editorId: string, message: any): void { const obj = this._kernelData.get(handle); if (!obj) { // extension can dispose kernels in the meantime diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 21a1114a1bf..72c33698571 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -503,7 +503,7 @@ export interface INotebookEditor extends ICommonNotebookEditor { /** * Send message to the webview for outputs. */ - postMessage(forRendererId: string | undefined, message: any): void; + postMessage(message: any): void; /** * Remove class name on the notebook editor root DOM node. diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 7a8b24065d9..a1a01c70801 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -1691,7 +1691,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return; } const { selected } = this.notebookKernelService.getMatchingKernel(this.viewModel.notebookDocument); - if (!selected || selected.preloadUris.length) { + if (!selected || !selected.preloadUris.length) { return; } if (!this._webview?.isResolved()) { @@ -2238,13 +2238,9 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor readonly onDidReceiveMessage: Event = this._onDidReceiveMessage.event; - postMessage(forRendererId: string | undefined, message: any) { + postMessage(message: any) { if (this._webview?.isResolved()) { - if (forRendererId === undefined) { - this._webview.webview.postMessage(message); - } else { - this._webview.postRendererMessage(forRendererId, message); - } + this._webview.postKernelMessage(message); } } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index 3cc75f4b805..6c2dac4a8f7 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -205,7 +205,7 @@ export interface ICreationRequestMessage { left: number; requiredPreloads: ReadonlyArray; readonly initiallyHidden?: boolean; - apiNamespace?: string | undefined; + rendererId?: string | undefined; } export interface IContentWidgetTopRequest { @@ -234,7 +234,7 @@ export interface IClearOutputRequestMessage { cellId: string; outputId: string; cellUri: string; - apiNamespace: string | undefined; + rendererId: string | undefined; } export interface IHideOutputMessage { @@ -266,12 +266,12 @@ export interface IAckOutputHeightMessage { export interface IPreloadResource { originalUri: string; uri: string; + source: 'kernel' | { rendererId: string }; } export interface IUpdatePreloadResourceMessage { type: 'preload'; resources: IPreloadResource[]; - source: 'renderer' | 'kernel'; } export interface IUpdateDecorationsMessage { @@ -281,9 +281,8 @@ export interface IUpdateDecorationsMessage { removedClassNames: string[]; } -export interface ICustomRendererMessage extends BaseToWebviewMessage { - type: 'customRendererMessage'; - rendererId: string; +export interface ICustomKernelMessage extends BaseToWebviewMessage { + type: 'customKernelMessage'; message: unknown; } @@ -337,7 +336,7 @@ export type FromWebviewMessage = | IWheelMessage | IScrollAckMessage | IBlurOutputMessage - | ICustomRendererMessage + | ICustomKernelMessage | IClickedDataUrlMessage | IClickMarkdownPreviewMessage | IContextMenuMarkdownPreviewMessage @@ -365,7 +364,7 @@ export type ToWebviewMessage = | IShowOutputMessage | IUpdatePreloadResourceMessage | IUpdateDecorationsMessage - | ICustomRendererMessage + | ICustomKernelMessage | ICreateMarkdownMessage | IDeleteMarkdownMessage | IShowMarkdownMessage @@ -393,7 +392,6 @@ function html(strings: TemplateStringsArray, ...values: any[]): string { export interface INotebookWebviewMessage { message: unknown; - forRenderer?: string; } export interface IResolvedBackLayerWebview { @@ -755,12 +753,11 @@ export class BackLayerWebView extends Disposable { }); } - postRendererMessage(rendererId: string, message: any) { + postKernelMessage(message: any) { this._sendMessageToWebview({ __vscode_notebook_message: true, - type: 'customRendererMessage', + type: 'customKernelMessage', message, - rendererId }); } @@ -886,7 +883,6 @@ var requirejs = (function() { } if (!data.__vscode_notebook_message) { - this._onMessage.fire({ message: data }); return; } @@ -990,9 +986,9 @@ var requirejs = (function() { this._onDidClickDataLink(data); break; } - case 'customRendererMessage': + case 'customKernelMessage': { - this._onMessage.fire({ message: data.message, forRenderer: data.rendererId }); + this._onMessage.fire({ message: data.message }); break; } case 'clickMarkdownPreview': @@ -1460,7 +1456,7 @@ var requirejs = (function() { message = { ...messageBase, outputId: output.outputId, - apiNamespace: content.renderer.id, + rendererId: content.renderer.id, requiredPreloads: await this.updateRendererPreloads([content.renderer]), content: { type: RenderOutputType.Extension, @@ -1502,7 +1498,7 @@ var requirejs = (function() { this._sendMessageToWebview({ type: 'clearOutput', - apiNamespace: outputCache.cachedCreation.apiNamespace, + rendererId: outputCache.cachedCreation.rendererId, cellUri: outputCache.cellInfo.cellUri.toString(), outputId: id, cellId: outputCache.cellInfo.cellId @@ -1589,7 +1585,7 @@ var requirejs = (function() { ? preload : asWebviewUri(this.environmentService, this.id, preload); if (!this._preloadsCache.has(uri.toString())) { - resources.push({ uri: uri.toString(), originalUri: preload.toString() }); + resources.push({ uri: uri.toString(), originalUri: preload.toString(), source: 'kernel' }); this._preloadsCache.add(uri.toString()); } } @@ -1599,7 +1595,7 @@ var requirejs = (function() { } this.kernelRootsCache = [...extensionLocations, ...this.kernelRootsCache]; - this._updatePreloads(resources, 'kernel'); + this._updatePreloads(resources); } async updateRendererPreloads(renderers: Iterable) { @@ -1616,7 +1612,12 @@ var requirejs = (function() { extensionLocations.push(rendererInfo.extensionLocation); for (const preload of [rendererInfo.entrypoint, ...rendererInfo.preloads]) { const uri = asWebviewUri(this.environmentService, this.id, preload); - const resource: IPreloadResource = { uri: uri.toString(), originalUri: preload.toString() }; + const resource: IPreloadResource = { + uri: uri.toString(), + originalUri: preload.toString(), + source: { rendererId: rendererInfo.id }, + }; + requiredPreloads.push(resource); if (!this._preloadsCache.has(uri.toString())) { @@ -1631,11 +1632,11 @@ var requirejs = (function() { } this.rendererRootsCache = extensionLocations; - this._updatePreloads(resources, 'renderer'); + this._updatePreloads(resources); return requiredPreloads; } - private _updatePreloads(resources: IPreloadResource[], source: 'renderer' | 'kernel') { + private _updatePreloads(resources: IPreloadResource[]) { if (!this.webview) { return; } @@ -1647,7 +1648,6 @@ var requirejs = (function() { this._sendMessageToWebview({ type: 'preload', resources: resources, - source: source }); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index 8941d52b574..5409bd69bbe 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -6,10 +6,10 @@ import type { Event } from 'vs/base/common/event'; import type { IDisposable } from 'vs/base/common/lifecycle'; import { RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { FromWebviewMessage, IBlurOutputMessage, ICellDropMessage, ICellDragMessage, ICellDragStartMessage, IClickedDataUrlMessage, ICustomRendererMessage, IDimensionMessage, IClickMarkdownPreviewMessage, IMouseEnterMarkdownPreviewMessage, IMouseEnterMessage, IMouseLeaveMarkdownPreviewMessage, IMouseLeaveMessage, IToggleMarkdownPreviewMessage, IWheelMessage, ToWebviewMessage, ICellDragEndMessage, IOutputFocusMessage, IOutputBlurMessage, DimensionUpdate, IContextMenuMarkdownPreviewMessage, ITelemetryFoundRenderedMarkdownMath, ITelemetryFoundUnrenderedMarkdownMath } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView'; +import type { FromWebviewMessage, IBlurOutputMessage, ICellDropMessage, ICellDragMessage, ICellDragStartMessage, IClickedDataUrlMessage, IDimensionMessage, IClickMarkdownPreviewMessage, IMouseEnterMarkdownPreviewMessage, IMouseEnterMessage, IMouseLeaveMarkdownPreviewMessage, IMouseLeaveMessage, IToggleMarkdownPreviewMessage, IWheelMessage, ToWebviewMessage, ICellDragEndMessage, IOutputFocusMessage, IOutputBlurMessage, DimensionUpdate, IContextMenuMarkdownPreviewMessage, ITelemetryFoundRenderedMarkdownMath, ITelemetryFoundUnrenderedMarkdownMath } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView'; // !! IMPORTANT !! everything must be in-line within the webviewPreloads -// function. Imports are not allowed. This is stringifies and injected into +// function. Imports are not allowed. This is stringified and injected into // the webview. declare module globalThis { @@ -413,35 +413,31 @@ async function webviewPreloads(markdownRendererModule: any, markdownDeps: any) { outputId: string; } - const onWillDestroyOutput = createEmitter<[string | undefined /* namespace */, IDestroyCellInfo | undefined /* cell uri */]>(); - const onDidCreateOutput = createEmitter<[string | undefined /* namespace */, ICreateCellInfo]>(); - const onDidReceiveMessage = createEmitter<[string, unknown]>(); + const onWillDestroyOutput = createEmitter<'all' | { rendererId: string, info: IDestroyCellInfo }>(); + const onDidCreateOutput = createEmitter<{ rendererId: string, info: ICreateCellInfo }>(); + const onDidReceiveKernelMessage = createEmitter(); - const matchesNs = (namespace: string, query: string | undefined) => namespace === '*' || query === namespace || query === 'undefined'; + const acquireNotebookRendererApi = (id: string) => ({ + setState(newState: T) { + vscode.setState({ ...vscode.getState(), [id]: newState }); + }, + getState(): T | undefined { + const state = vscode.getState(); + return typeof state === 'object' && state ? state[id] as T : undefined; + }, + onWillDestroyOutput: mapEmitter(onWillDestroyOutput, (evt) => { + if (evt === 'all') { + return undefined; + } + return evt.rendererId === id ? evt.info : dontEmit; + }), + onDidCreateOutput: mapEmitter(onDidCreateOutput, ({ rendererId, info }) => rendererId === id ? info : dontEmit), + }); - (window as any).acquireNotebookRendererApi = (namespace: string) => { - if (!namespace || typeof namespace !== 'string') { - throw new Error(`acquireNotebookRendererApi should be called your renderer type as a string, got: ${namespace}.`); - } - - return { - postMessage(message: unknown) { - postNotebookMessage('customRendererMessage', { - rendererId: namespace, - message, - }); - }, - setState(newState: T) { - vscode.setState({ ...vscode.getState(), [namespace]: newState }); - }, - getState(): T | undefined { - const state = vscode.getState(); - return typeof state === 'object' && state ? state[namespace] as T : undefined; - }, - onDidReceiveMessage: mapEmitter(onDidReceiveMessage, ([ns, data]) => ns === namespace ? data : dontEmit), - onWillDestroyOutput: mapEmitter(onWillDestroyOutput, ([ns, data]) => matchesNs(namespace, ns) ? data : dontEmit), - onDidCreateOutput: mapEmitter(onDidCreateOutput, ([ns, data]) => matchesNs(namespace, ns) ? data : dontEmit), - }; + const kernelPreloadGlobals = { + acquireVsCodeApi, + onDidReceiveKernelMessage: onDidReceiveKernelMessage.event, + postKernelMessage: (data: unknown) => postNotebookMessage('customKernelMessage', { message: data }), }; const enum PreloadState { @@ -638,28 +634,16 @@ async function webviewPreloads(markdownRendererModule: any, markdownDeps: any) { cellOutputContainer.appendChild(outputContainer); outputContainer.appendChild(outputNode); } else { - const { metadata, mimeType, value } = content; - onDidCreateOutput.fire([data.apiNamespace, { - element: outputNode, - outputId, - mime: content.mimeType, - value: content.value, - metadata: content.metadata, - - get mimeType() { - console.warn(`event.mimeType is deprecated, use 'mime' instead`); - return mimeType; - }, - - get output() { - console.warn(`event.output is deprecated, use properties directly instead`); - return { - metadata: { [mimeType]: metadata }, - data: { [mimeType]: value }, - outputId, - }; - }, - } as ICreateCellInfo]); + onDidCreateOutput.fire({ + rendererId: data.rendererId!, + info: { + element: outputNode, + outputId, + mime: content.mimeType, + value: content.value, + metadata: content.metadata, + } + }); cellOutputContainer.appendChild(outputContainer); outputContainer.appendChild(outputNode); } @@ -715,7 +699,7 @@ async function webviewPreloads(markdownRendererModule: any, markdownDeps: any) { } case 'clear': queuedOuputActions.clear(); // stop all loading outputs - onWillDestroyOutput.fire([undefined, undefined]); + onWillDestroyOutput.fire('all'); document.getElementById('container')!.innerText = ''; focusTrackers.forEach(ft => { @@ -723,14 +707,20 @@ async function webviewPreloads(markdownRendererModule: any, markdownDeps: any) { }); focusTrackers.clear(); break; - case 'clearOutput': + case 'clearOutput': { const output = document.getElementById(event.data.outputId); - queuedOuputActions.delete(event.data.outputId); // stop any in-progress rendering + const { rendererId, outputId } = event.data; + + queuedOuputActions.delete(outputId); // stop any in-progress rendering if (output && output.parentNode) { - onWillDestroyOutput.fire([event.data.apiNamespace, { outputId: event.data.outputId }]); + if (rendererId) { + onWillDestroyOutput.fire({ rendererId, info: { outputId } }); + } output.parentNode.removeChild(output); } + break; + } case 'hideOutput': enqueueOutputAction(event.data, ({ outputId }) => { const container = document.getElementById(outputId)?.parentElement?.parentElement; @@ -764,9 +754,12 @@ async function webviewPreloads(markdownRendererModule: any, markdownDeps: any) { } case 'preload': const resources = event.data.resources; - const globals = event.data.type === 'preload' ? { acquireVsCodeApi } : {}; let queue: Promise = Promise.resolve({ state: PreloadState.Ok }); - for (const { uri, originalUri } of resources) { + for (const { uri, originalUri, source } of resources) { + const globals = source === 'kernel' + ? kernelPreloadGlobals + : { acquireNotebookRendererApi: () => acquireNotebookRendererApi(source.rendererId) }; + // create the promise so that the scripts download in parallel, but // only invoke them in series within the queue const promise = runScript(uri, originalUri, globals); @@ -792,8 +785,8 @@ async function webviewPreloads(markdownRendererModule: any, markdownDeps: any) { } break; - case 'customRendererMessage': - onDidReceiveMessage.fire([event.data.rendererId, event.data.message]); + case 'customKernelMessage': + onDidReceiveKernelMessage.fire(event.data.message); break; } });