lazy load webview if there is no webview outputs or not kernel dependencies

This commit is contained in:
rebornix 2020-07-21 11:06:53 -07:00
parent cf9d6d7667
commit 9c467b969c
3 changed files with 97 additions and 43 deletions

View file

@ -933,7 +933,7 @@ suite('webview', () => {
// await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
// assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first');
// const uri = vscode.notebook.activeNotebookEditor!.asWebviewUri(vscode.Uri.file('./hello.png'));
// assert.equal(uri.scheme, 'vscode-resource');
// assert.equal(uri.scheme, 'vscode-webview-resource');
// await vscode.commands.executeCommand('workbench.action.closeAllEditors');
// });

View file

@ -75,6 +75,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
private _overlayContainer!: HTMLElement;
private _body!: HTMLElement;
private _webview: BackLayerWebView | null = null;
private _webviewResolved: boolean = false;
private _webviewResolvePromise: Promise<BackLayerWebView | null> | null = null;
private _webviewTransparentCover: HTMLElement | null = null;
private _list: INotebookCellList | undefined;
private _dndController: CellDragAndDropController | null = null;
@ -591,7 +593,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
// @deprecated
if (provider && provider.kernel) {
// it has a builtin kernel, don't automatically choose a kernel
this._loadKernelPreloads(provider.providerExtensionLocation, provider.kernel);
await this._loadKernelPreloads(provider.providerExtensionLocation, provider.kernel);
tokenSource.dispose();
return;
}
@ -610,7 +612,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
// the provider doesn't have a builtin kernel, choose a kernel
this.activeKernel = availableKernels[0];
if (this.activeKernel) {
this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel);
await this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel);
}
tokenSource.dispose();
@ -630,7 +632,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
}
if (this.activeKernel) {
this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel);
await this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel);
await this.activeKernel.resolve(this.viewModel!.uri, this.getId(), tokenSource.token);
}
@ -643,7 +645,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
if (kernelsFromSameExtension.length) {
const preferedKernel = kernelsFromSameExtension.find(kernel => kernel.isPreferred) || kernelsFromSameExtension[0];
this.activeKernel = preferedKernel;
this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel);
await this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel);
await preferedKernel.resolve(this.viewModel!.uri, this.getId(), tokenSource.token);
tokenSource.dispose();
return;
@ -652,15 +654,16 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
// the provider doesn't have a builtin kernel, choose a kernel
this.activeKernel = kernels[0];
if (this.activeKernel) {
this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel);
await this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel);
await this.activeKernel.resolve(this.viewModel!.uri, this.getId(), tokenSource.token);
}
tokenSource.dispose();
}
private _loadKernelPreloads(extensionLocation: URI, kernel: INotebookKernelInfoDto) {
if (kernel.preloads) {
private async _loadKernelPreloads(extensionLocation: URI, kernel: INotebookKernelInfoDto) {
if (kernel.preloads && kernel.preloads.length) {
await this._resolveWebview();
this._webview?.updateKernelPreloads([extensionLocation], kernel.preloads.map(preload => URI.revive(preload)));
}
}
@ -675,34 +678,63 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
this._notebookExecuting?.set(notebookMetadata.runState === NotebookRunState.Running);
}
private async _resolveWebview(): Promise<BackLayerWebView | null> {
if (!this.textModel) {
return null;
}
if (this._webviewResolvePromise) {
return this._webviewResolvePromise;
}
if (!this._webview) {
this._webview = this.instantiationService.createInstance(BackLayerWebView, this, this.getId(), this.textModel!.uri);
// attach the webview container to the DOM tree first
this._list?.rowsContainer.insertAdjacentElement('afterbegin', this._webview.element);
}
this._webviewResolvePromise = new Promise(async resolve => {
await this._webview!.createWebview();
this._webview!.webview!.onDidBlur(() => {
this._outputFocus?.set(false);
this.updateEditorFocus();
if (this._overlayContainer.contains(document.activeElement)) {
this._webiewFocused = false;
}
});
this._webview!.webview!.onDidFocus(() => {
this._outputFocus?.set(true);
this.updateEditorFocus();
this._onDidFocusEmitter.fire();
if (this._overlayContainer.contains(document.activeElement)) {
this._webiewFocused = true;
}
});
this._localStore.add(this._webview!.onMessage(({ message, forRenderer }) => {
if (this.viewModel) {
this.notebookService.onDidReceiveMessage(this.viewModel.viewType, this.getId(), forRenderer, message);
}
}));
if (this.viewModel && this.viewModel!.renderers.size) {
this._webview?.updateRendererPreloads(this.viewModel!.renderers);
}
this._webviewResolved = true;
resolve(this._webview!);
});
return this._webviewResolvePromise;
}
private async _createWebview(id: string, resource: URI): Promise<void> {
this._webview = this.instantiationService.createInstance(BackLayerWebView, this, id, resource);
// attach the webview container to the DOM tree first
this._list?.rowsContainer.insertAdjacentElement('afterbegin', this._webview.element);
await this._webview.createWebview();
this._webview.webview.onDidBlur(() => {
this._outputFocus?.set(false);
this.updateEditorFocus();
if (this._overlayContainer.contains(document.activeElement)) {
this._webiewFocused = false;
}
});
this._webview.webview.onDidFocus(() => {
this._outputFocus?.set(true);
this.updateEditorFocus();
this._onDidFocusEmitter.fire();
if (this._overlayContainer.contains(document.activeElement)) {
this._webiewFocused = true;
}
});
this._localStore.add(this._webview.onMessage(({ message, forRenderer }) => {
if (this.viewModel) {
this.notebookService.onDidReceiveMessage(this.viewModel.viewType, this.getId(), forRenderer, message);
}
}));
}
private async _attachModel(textModel: NotebookTextModel, viewState: INotebookEditorViewState | undefined) {
@ -736,10 +768,17 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
}
}
this._webview?.updateRendererPreloads(this.viewModel.renderers);
if (this.viewModel.renderers.size) {
await this._resolveWebview();
this._webview?.updateRendererPreloads(this.viewModel.renderers);
}
this._localStore.add(this._list!.onWillScroll(e => {
this._webview!.updateViewScrollTop(-e.scrollTop, []);
if (!this._webviewResolved) {
return;
}
this._webview?.updateViewScrollTop(-e.scrollTop, []);
this._webviewTransparentCover!.style.top = `${e.scrollTop}px`;
}));
@ -751,6 +790,11 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
const scrollTop = this._list?.scrollTop || 0;
const scrollHeight = this._list?.scrollHeight || 0;
if (!this._webviewResolved) {
return;
}
this._webview!.element.style.height = `${scrollHeight}px`;
if (this._webview?.insetMapping) {
@ -1375,6 +1419,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
return;
}
await this._resolveWebview();
let preloads = this._notebookViewModel!.renderers;
if (!this._webview!.insetMapping.has(output)) {
@ -1389,7 +1435,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
}
removeInset(output: IProcessedOutput) {
if (!this._webview) {
if (!this._webview || !this._webviewResolved) {
return;
}
@ -1397,7 +1443,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
}
hideInset(output: IProcessedOutput) {
if (!this._webview) {
if (!this._webview || !this._webviewResolved) {
return;
}
@ -1409,10 +1455,14 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
}
postMessage(forRendererId: string | undefined, message: any) {
if (!this._webview || !this._webviewResolved) {
return;
}
if (forRendererId === undefined) {
this._webview?.webview.postMessage(message);
this._webview.webview?.postMessage(message);
} else {
this._webview?.postRendererMessage(forRendererId, message);
this._webview.postRendererMessage(forRendererId, message);
}
}

View file

@ -219,7 +219,7 @@ export interface INotebookWebviewMessage {
let version = 0;
export class BackLayerWebView extends Disposable {
element: HTMLElement;
webview!: WebviewElement;
webview: WebviewElement | undefined = undefined;
insetMapping: Map<IProcessedOutput, ICachedInset> = new Map();
hiddenInsetMapping: Set<IProcessedOutput> = new Set();
reversedInsetMapping: Map<string, IProcessedOutput> = new Map();
@ -714,7 +714,7 @@ ${loaderJs}
return;
}
this.webview.focus();
this.webview?.focus();
}
focusOutput(cellId: string) {
@ -722,7 +722,7 @@ ${loaderJs}
return;
}
this.webview.focus();
this.webview?.focus();
setTimeout(() => { // Need this, or focus decoration is not shown. No clue.
this._sendMessageToWebview({
type: 'focus-output',
@ -814,6 +814,10 @@ ${loaderJs}
}
private _updatePreloads(resources: IPreloadResource[], source: 'renderer' | 'kernel') {
if (!this.webview) {
return;
}
const mixedResourceRoots = [...(this.localResourceRootsCache || []), ...this.rendererRootsCache, ...this.kernelRootsCache];
this.webview.localResourcesRoot = mixedResourceRoots;
@ -830,7 +834,7 @@ ${loaderJs}
return;
}
this.webview.postMessage(message);
this.webview?.postMessage(message);
}
clearPreloadsCache() {
@ -839,7 +843,7 @@ ${loaderJs}
dispose() {
this._disposed = true;
this.webview.dispose();
this.webview?.dispose();
super.dispose();
}
}