Fix #3734 Notebook cells are shown empty some times even when there is content (#3878)

- Editor layout gets called sometimes when other events happen (and Notebook isn't visible)
- Add in a layout call on re-setting input so the cell is updated. This fixes the problem by laying out once the UI is visible again.

Note: long term, should really be destroying the UI (while preserving the model), then restoring it including scroll selection etc. and hooking back up to the model. That is... much more work, but something we'll need long term to avoid issues where we have many Notebooks open at once. Not in scope for this PR
This commit is contained in:
Kevin Cunnane
2019-02-01 10:11:45 -08:00
committed by GitHub
parent 9504ede1f3
commit 5132e62045
8 changed files with 43 additions and 11 deletions

View File

@@ -142,6 +142,7 @@ export class CodeComponent extends AngularDisposable implements OnInit, OnChange
this.cellModel.source = this._editorModel.getValue(); this.cellModel.source = this._editorModel.getValue();
this.onContentChanged.emit(); this.onContentChanged.emit();
})); }));
this._register(this.model.layoutChanged(this.layout, this));
this.layout(); this.layout();
} }

View File

@@ -276,6 +276,11 @@ export interface INotebookModel {
*/ */
readonly kernelChanged: Event<nb.IKernelChangedArgs>; readonly kernelChanged: Event<nb.IKernelChangedArgs>;
/**
* Fired on notifications that notebook components should be re-laid out.
*/
readonly layoutChanged: Event<void>;
/** /**
* Event fired on first initialization of the kernels and * Event fired on first initialization of the kernels and
* on subsequent change events * on subsequent change events
@@ -443,6 +448,8 @@ export interface INotebookModelOptions {
standardKernels: IStandardKernelWithProvider[]; standardKernels: IStandardKernelWithProvider[];
defaultKernel: nb.IKernelSpec; defaultKernel: nb.IKernelSpec;
layoutChanged: Event<void>;
notificationService: INotificationService; notificationService: INotificationService;
connectionService: IConnectionManagementService; connectionService: IConnectionManagementService;
capabilitiesService: ICapabilitiesService; capabilitiesService: ICapabilitiesService;

View File

@@ -44,6 +44,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
private _contextsChangedEmitter = new Emitter<void>(); private _contextsChangedEmitter = new Emitter<void>();
private _contentChangedEmitter = new Emitter<NotebookContentChange>(); private _contentChangedEmitter = new Emitter<NotebookContentChange>();
private _kernelsChangedEmitter = new Emitter<nb.IKernelSpec>(); private _kernelsChangedEmitter = new Emitter<nb.IKernelSpec>();
private _layoutChanged = new Emitter<void>();
private _inErrorState: boolean = false; private _inErrorState: boolean = false;
private _clientSessions: IClientSession[] = []; private _clientSessions: IClientSession[] = [];
private _activeClientSession: IClientSession; private _activeClientSession: IClientSession;
@@ -55,7 +56,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
private _cells: ICellModel[]; private _cells: ICellModel[];
private _defaultLanguageInfo: nb.ILanguageInfo; private _defaultLanguageInfo: nb.ILanguageInfo;
private onErrorEmitter = new Emitter<INotification>(); private _onErrorEmitter = new Emitter<INotification>();
private _savedKernelInfo: nb.IKernelInfo; private _savedKernelInfo: nb.IKernelInfo;
private readonly _nbformat: number = nbversion.MAJOR_VERSION; private readonly _nbformat: number = nbversion.MAJOR_VERSION;
private readonly _nbformatMinor: number = nbversion.MINOR_VERSION; private readonly _nbformatMinor: number = nbversion.MINOR_VERSION;
@@ -81,6 +82,9 @@ export class NotebookModel extends Disposable implements INotebookModel {
this._kernelDisplayNameToConnectionProviderIds.set(kernel.name, kernel.connectionProviderIds); this._kernelDisplayNameToConnectionProviderIds.set(kernel.name, kernel.connectionProviderIds);
this._kernelDisplayNameToNotebookProviderIds.set(kernel.name, kernel.notebookProvider); this._kernelDisplayNameToNotebookProviderIds.set(kernel.name, kernel.notebookProvider);
}); });
if (this.notebookOptions.layoutChanged) {
this.notebookOptions.layoutChanged(() => this._layoutChanged.fire());
}
this._defaultKernel = notebookOptions.defaultKernel; this._defaultKernel = notebookOptions.defaultKernel;
} }
@@ -139,6 +143,10 @@ export class NotebookModel extends Disposable implements INotebookModel {
return this._kernelsChangedEmitter.event; return this._kernelsChangedEmitter.event;
} }
public get layoutChanged(): Event<void> {
return this._layoutChanged.event;
}
public get defaultKernel(): nb.IKernelSpec { public get defaultKernel(): nb.IKernelSpec {
return this._defaultKernel; return this._defaultKernel;
} }
@@ -178,7 +186,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
} }
public get onError(): Event<INotification> { public get onError(): Event<INotification> {
return this.onErrorEmitter.event; return this._onErrorEmitter.event;
} }
public get trustedMode(): boolean { public get trustedMode(): boolean {
@@ -348,7 +356,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
} }
private notifyError(error: string): void { private notifyError(error: string): void {
this.onErrorEmitter.fire({ message: error, severity: Severity.Error }); this._onErrorEmitter.fire({ message: error, severity: Severity.Error });
} }
public backgroundStartSession(): void { public backgroundStartSession(): void {

View File

@@ -250,6 +250,7 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
standardKernels: this._notebookParams.input.standardKernels, standardKernels: this._notebookParams.input.standardKernels,
providerId: notebookUtils.sqlNotebooksEnabled(this.contextKeyService) ? 'sql' : 'jupyter', // this is tricky; really should also depend on the connection profile providerId: notebookUtils.sqlNotebooksEnabled(this.contextKeyService) ? 'sql' : 'jupyter', // this is tricky; really should also depend on the connection profile
defaultKernel: this._notebookParams.input.defaultKernel, defaultKernel: this._notebookParams.input.defaultKernel,
layoutChanged: this._notebookParams.input.layoutChanged,
capabilitiesService: this.capabilitiesService capabilitiesService: this.capabilitiesService
}, false, this.profile); }, false, this.profile);
model.onError((errInfo: INotification) => this.handleModelError(errInfo)); model.onError((errInfo: INotification) => this.handleModelError(errInfo));

View File

@@ -22,7 +22,6 @@ export class NotebookEditor extends BaseEditor {
public static ID: string = 'workbench.editor.notebookEditor'; public static ID: string = 'workbench.editor.notebookEditor';
private _notebookContainer: HTMLElement; private _notebookContainer: HTMLElement;
protected _input: NotebookInput;
constructor( constructor(
@ITelemetryService telemetryService: ITelemetryService, @ITelemetryService telemetryService: ITelemetryService,
@@ -32,8 +31,8 @@ export class NotebookEditor extends BaseEditor {
super(NotebookEditor.ID, telemetryService, themeService); super(NotebookEditor.ID, telemetryService, themeService);
} }
public get input(): NotebookInput { public get notebookInput(): NotebookInput {
return this._input; return this.input as NotebookInput;
} }
/** /**
@@ -53,6 +52,9 @@ export class NotebookEditor extends BaseEditor {
* To be called when the container of this editor changes size. * To be called when the container of this editor changes size.
*/ */
public layout(dimension: DOM.Dimension): void { public layout(dimension: DOM.Dimension): void {
if (this.notebookInput) {
this.notebookInput.doChangeLayout();
}
} }
public setInput(input: NotebookInput, options: EditorOptions): TPromise<void> { public setInput(input: NotebookInput, options: EditorOptions): TPromise<void> {
@@ -70,10 +72,11 @@ export class NotebookEditor extends BaseEditor {
let container = DOM.$<HTMLElement>('.notebookEditor'); let container = DOM.$<HTMLElement>('.notebookEditor');
container.style.height = '100%'; container.style.height = '100%';
this._notebookContainer = DOM.append(parentElement, container); this._notebookContainer = DOM.append(parentElement, container);
this.input.container = this._notebookContainer; input.container = this._notebookContainer;
return TPromise.wrap<void>(this.bootstrapAngular(input)); return TPromise.wrap<void>(this.bootstrapAngular(input));
} else { } else {
this._notebookContainer = DOM.append(parentElement, this.input.container); this._notebookContainer = DOM.append(parentElement, input.container);
input.doChangeLayout();
return TPromise.wrap<void>(null); return TPromise.wrap<void>(null);
} }
} }

View File

@@ -104,13 +104,12 @@ export class NotebookInputModel extends EditorModel {
export class NotebookInput extends EditorInput { export class NotebookInput extends EditorInput {
public static ID: string = 'workbench.editorinputs.notebookInput'; public static ID: string = 'workbench.editorinputs.notebookInput';
public hasBootstrapped = false; public hasBootstrapped = false;
// Holds the HTML content for the editor when the editor discards this input and loads another // Holds the HTML content for the editor when the editor discards this input and loads another
private _parentContainer: HTMLElement; private _parentContainer: HTMLElement;
private readonly _layoutChanged: Emitter<void> = this._register(new Emitter<void>());
constructor(private _title: string, constructor(private _title: string,
private _model: NotebookInputModel, private _model: NotebookInputModel,
@INotebookService private notebookService: INotebookService, @INotebookService private notebookService: INotebookService,
@@ -140,6 +139,14 @@ export class NotebookInput extends EditorInput {
return this._model.defaultKernel; return this._model.defaultKernel;
} }
get layoutChanged(): Event<void> {
return this._layoutChanged.event;
}
doChangeLayout(): any {
this._layoutChanged.fire();
}
public getTypeId(): string { public getTypeId(): string {
return NotebookInput.ID; return NotebookInput.ID;
} }

View File

@@ -38,7 +38,11 @@ export class NotebookModelStub implements INotebookModel {
} }
get kernelsChanged(): Event<nb.IKernelSpec> { get kernelsChanged(): Event<nb.IKernelSpec> {
throw new Error('method not implemented.'); throw new Error('method not implemented.');
} get defaultKernel(): nb.IKernelSpec { }
get layoutChanged(): Event<void> {
throw new Error('method not implemented.');
}
get defaultKernel(): nb.IKernelSpec {
throw new Error('method not implemented.'); throw new Error('method not implemented.');
} }
get contextsChanged(): Event<void> { get contextsChanged(): Event<void> {

View File

@@ -97,6 +97,7 @@ describe('notebook model', function(): void {
providerId: 'SQL', providerId: 'SQL',
standardKernels: [{ name: 'SQL', connectionProviderIds: ['MSSQL'], notebookProvider: 'sql' }], standardKernels: [{ name: 'SQL', connectionProviderIds: ['MSSQL'], notebookProvider: 'sql' }],
defaultKernel: undefined, defaultKernel: undefined,
layoutChanged: undefined,
capabilitiesService: capabilitiesService.object capabilitiesService: capabilitiesService.object
}; };
mockClientSession = TypeMoq.Mock.ofType(ClientSession, undefined, defaultModelOptions); mockClientSession = TypeMoq.Mock.ofType(ClientSession, undefined, defaultModelOptions);