Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Jupyter to new notebook preload message api #5775

Merged
merged 10 commits into from
May 6, 2021
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/3 Code Health/5753.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Update to new message API for native notebook preloads.
32,605 changes: 401 additions & 32,204 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2056,7 +2056,7 @@
"@types/untildify": "^3.0.0",
"@types/uuid": "^3.4.3",
"@types/vscode": "^1.53.0",
"@types/vscode-notebook-renderer": "^1.55.0",
"@types/vscode-notebook-renderer": "^1.57.0",
"@types/webpack-bundle-analyzer": "^2.13.0",
"@types/winreg": "^1.2.30",
"@types/ws": "^6.0.1",
Expand Down
24 changes: 12 additions & 12 deletions src/client/datascience/notebook/vscodeNotebookController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,8 @@ export class VSCodeNotebookController implements Disposable {
const codeSpaceScripts =
env.uiKind === UIKind.Web
? [
{
uri: Uri.file(
new NotebookKernelPreload(
Uri.file(
join(
this.context.extensionPath,
'out',
Expand All @@ -170,22 +170,22 @@ export class VSCodeNotebookController implements Disposable {
'require.js'
)
)
}
)
]
: [];
return [
...codeSpaceScripts,
{ uri: Uri.file(join(this.context.extensionPath, 'out', 'ipywidgets', 'dist', 'ipywidgets.js')) },
{
uri: Uri.file(
new NotebookKernelPreload(
Uri.file(join(this.context.extensionPath, 'out', 'ipywidgets', 'dist', 'ipywidgets.js'))
),
new NotebookKernelPreload(
Uri.file(
join(this.context.extensionPath, 'out', 'datascience-ui', 'ipywidgetsKernel', 'ipywidgetsKernel.js')
)
},
{
uri: Uri.file(
join(this.context.extensionPath, 'out', 'datascience-ui', 'notebook', 'fontAwesomeLoader.js')
)
}
),
new NotebookKernelPreload(
Uri.file(join(this.context.extensionPath, 'out', 'datascience-ui', 'notebook', 'fontAwesomeLoader.js'))
)
];
}

Expand Down
3 changes: 1 addition & 2 deletions src/datascience-ui/ipywidgets/renderer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

import './styles.css';
import { NotebookOutputEventParams, NotebookRendererApi } from 'vscode-notebook-renderer';
const JupyterIPyWidgetNotebookRenderer = 'jupyter-ipywidget-renderer';

/* eslint-disable @typescript-eslint/no-explicit-any, no-console */
function renderOutput(e: NotebookOutputEventParams) {
Expand All @@ -30,4 +29,4 @@ function initialize(api: NotebookRendererApi<any>) {
api.onWillDestroyOutput(disposeOutput);
}

initialize(acquireNotebookRendererApi(JupyterIPyWidgetNotebookRenderer));
initialize(acquireNotebookRendererApi());
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another change from proposed, correct name is automatically passed.

170 changes: 132 additions & 38 deletions src/datascience-ui/react-common/postOffice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import { VSCodeEvent } from 'vscode-notebook-renderer';
import { WebviewMessage } from '../../client/common/application/types';
import { IDisposable } from '../../client/common/types';
import { logMessage } from './logger';
Expand All @@ -23,16 +24,117 @@ export interface IMessageHandler {
dispose?(): void;
}

interface IMessageApi {
register(msgCallback: (msg: WebviewMessage) => Promise<void>): void;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
sendMessage(type: string, payload?: any): void;
dispose(): void;
}

// This special function talks to vscode from a web panel
export declare function acquireVsCodeApi(): IVsCodeApi;
// Provides support for messaging when using the vscode webview messaging api
class VsCodeMessageApi implements IMessageApi {
private messageCallback: ((msg: WebviewMessage) => Promise<void>) | undefined;
private vscodeApi: IVsCodeApi | undefined;
private registered: boolean = false;
private baseHandler = this.handleVSCodeApiMessages.bind(this);

public register(msgCallback: (msg: WebviewMessage) => Promise<void>) {
this.messageCallback = msgCallback;

// Only do this once as it crashes if we ask more than once
// eslint-disable-next-line
if (!this.vscodeApi && typeof acquireVsCodeApi !== 'undefined') {
this.vscodeApi = acquireVsCodeApi(); // NOSONAR
// eslint-disable-next-line @typescript-eslint/no-explicit-any,
} else if (!this.vscodeApi && typeof (window as any).acquireVsCodeApi !== 'undefined') {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.vscodeApi = (window as any).acquireVsCodeApi();
}
if (!this.registered) {
this.registered = true;
window.addEventListener('message', this.baseHandler);

try {
// For testing, we might use a browser to load the stuff.
// In such instances the `acquireVSCodeApi` will return the event handler to get messages from extension.
// See ./src/datascience-ui/native-editor/index.html
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const api = (this.vscodeApi as any) as undefined | { handleMessage?: Function };
if (api && api.handleMessage) {
api.handleMessage(this.handleVSCodeApiMessages.bind(this));
}
} catch {
// Ignore.
}
}
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
public sendMessage(type: string, payload?: any) {
if (this.vscodeApi) {
logMessage(`UI PostOffice Sent ${type}`);
this.vscodeApi.postMessage({ type: type, payload });
} else {
logMessage(`No vscode API to post message ${type}`);
}
}

public dispose() {
if (this.registered) {
this.registered = false;
window.removeEventListener('message', this.baseHandler);
}
}

private async handleVSCodeApiMessages(ev: MessageEvent) {
const msg = ev.data as WebviewMessage;
if (msg && this.messageCallback) {
await this.messageCallback(msg);
}
}
}

// Provides support for messaging when hosted via a native notebook preload
class KernelMessageApi implements IMessageApi {
private messageCallback: ((msg: WebviewMessage) => Promise<void>) | undefined;
private kernelHandler: IDisposable | undefined;

public register(msgCallback: (msg: WebviewMessage) => Promise<void>) {
this.messageCallback = msgCallback;
if (!this.kernelHandler) {
this.kernelHandler = onDidReceiveKernelMessage(this.handleKernelMessage.bind(this));
}
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
public sendMessage(type: string, payload?: any) {
postKernelMessage({ type: type, payload });
}

public dispose() {
if (this.kernelHandler) {
this.kernelHandler.dispose();
}
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
private async handleKernelMessage(ev: VSCodeEvent<any>) {
const msg = (ev as unknown) as WebviewMessage;
if (msg && this.messageCallback) {
await this.messageCallback(msg);
}
}
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type PostOfficeMessage = { type: string; payload?: any };

// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class PostOffice implements IDisposable {
private registered: boolean = false;
private vscodeApi: IVsCodeApi | undefined;
private messageApi: IMessageApi | undefined;
private handlers: IMessageHandler[] = [];
private baseHandler = this.handleMessages.bind(this);
private readonly subject = new Subject<PostOfficeMessage>();
private readonly observable: Observable<PostOfficeMessage>;
constructor() {
Expand All @@ -42,9 +144,8 @@ export class PostOffice implements IDisposable {
return this.observable;
}
public dispose() {
if (this.registered) {
this.registered = false;
window.removeEventListener('message', this.baseHandler);
if (this.messageApi) {
this.messageApi.dispose();
}
}

Expand All @@ -54,12 +155,10 @@ export class PostOffice implements IDisposable {

// eslint-disable-next-line @typescript-eslint/no-explicit-any
public sendUnsafeMessage(type: string, payload?: any) {
const api = this.acquireApi();
if (api) {
logMessage(`UI PostOffice Sent ${type}`);
api.postMessage({ type: type, payload });
if (this.messageApi) {
this.messageApi.sendMessage(type, payload);
} else {
logMessage(`No vscode API to post message ${type}`);
logMessage(`No message API to post message ${type}`);
}
}

Expand All @@ -73,39 +172,34 @@ export class PostOffice implements IDisposable {
this.handlers = this.handlers.filter((f) => f !== handler);
}

public acquireApi(): IVsCodeApi | undefined {
// Only do this once as it crashes if we ask more than once
// eslint-disable-next-line
if (!this.vscodeApi && typeof acquireVsCodeApi !== 'undefined') {
this.vscodeApi = acquireVsCodeApi(); // NOSONAR
// eslint-disable-next-line @typescript-eslint/no-explicit-any,
} else if (!this.vscodeApi && typeof (window as any).acquireVsCodeApi !== 'undefined') {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.vscodeApi = (window as any).acquireVsCodeApi();
// Hook up to our messaging API
public acquireApi() {
if (this.messageApi) {
return;
}
if (!this.registered) {
this.registered = true;
window.addEventListener('message', this.baseHandler);

try {
// For testing, we might use a browser to load the stuff.
// In such instances the `acquireVSCodeApi` will return the event handler to get messages from extension.
// See ./src/datascience-ui/native-editor/index.html
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const api = (this.vscodeApi as any) as undefined | { handleMessage?: Function };
if (api && api.handleMessage) {
api.handleMessage(this.handleMessages.bind(this));
}
} catch {
// Ignore.
}
// If the kernel message API is available use that if not use the VS Code webview messaging API
if (this.useKernelMessageApi()) {
this.messageApi = new KernelMessageApi();
} else {
this.messageApi = new VsCodeMessageApi();
}

this.messageApi.register(this.handleMessage.bind(this));
}

// Check to see if global kernel message API is supported, if so use that
// instead of the VSCodeAPI which is not available in NativeNotebooks
private useKernelMessageApi(): boolean {
if (typeof postKernelMessage !== 'undefined') {
return true;
}

return this.vscodeApi;
return false;
}
private async handleMessages(ev: MessageEvent) {

private async handleMessage(msg: WebviewMessage) {
if (this.handlers) {
const msg = ev.data as WebviewMessage;
if (msg) {
if ('type' in msg && typeof msg.type === 'string') {
logMessage(`UI PostOffice Received ${msg.type}`);
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"resolveJsonModule": true,
"removeComments": true
"removeComments": true,
"types": ["@types/vscode-notebook-renderer/preload"]
},
"exclude": [
"node_modules",
Expand Down
Loading