Notebooks: re-factor grid streaming (#12937)

* refactor grid streaming (convert to data first)

* change convertRowsToHtml method to return value

* remove griddataconversioncomplete checks

* send row data from STS to gridoutput component

* clean up code

* send data updates to cell model

* serialize cell output at the end of cell execution

* remove unused parameters

* update output contents instead of output reference

* remove unnecessary promise

* move azdata changes to proposed

* update comment
This commit is contained in:
Lucy Zhang
2020-10-28 09:08:15 -07:00
committed by GitHub
parent 42e16b1752
commit 86357b45b0
18 changed files with 266 additions and 412 deletions

View File

@@ -12,7 +12,7 @@ import { localize } from 'vs/nls';
import * as notebookUtils from 'sql/workbench/services/notebook/browser/models/notebookUtils';
import { CellTypes, CellType, NotebookChangeType } from 'sql/workbench/services/notebook/common/contracts';
import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/notebookModel';
import { ICellModel, IOutputChangedEvent, CellExecutionState, ICellModelOptions } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
import { ICellModel, IOutputChangedEvent, CellExecutionState, ICellModelOptions, ITableUpdatedEvent } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
@@ -28,6 +28,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands';
import { tryMatchCellMagic, extractCellMagicCommandPlusArgs } from 'sql/workbench/services/notebook/browser/utils';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { Disposable } from 'vs/base/common/lifecycle';
import { ResultSetSummary } from 'sql/workbench/services/query/common/query';
let modelId = 0;
const ads_execute_command = 'ads_execute_command';
@@ -44,6 +45,7 @@ export class CellModel extends Disposable implements ICellModel {
private _renderedOutputTextContent: string[] = [];
private _isEditMode: boolean;
private _onOutputsChanged = new Emitter<IOutputChangedEvent>();
private _onTableUpdated = new Emitter<ITableUpdatedEvent>();
private _onCellModeChanged = new Emitter<boolean>();
private _onExecutionStateChanged = new Emitter<CellExecutionState>();
private _isTrusted: boolean;
@@ -66,7 +68,6 @@ export class CellModel extends Disposable implements ICellModel {
private _showPreview: boolean = true;
private _showMarkdown: boolean = false;
private _cellSourceChanged: boolean = false;
private _gridDataConversionComplete: Promise<void>[] = [];
private _defaultToWYSIWYG: boolean;
private _isParameter: boolean;
private _onParameterStateChanged = new Emitter<boolean>();
@@ -113,6 +114,10 @@ export class CellModel extends Disposable implements ICellModel {
return this._onOutputsChanged.event;
}
public get onTableUpdated(): Event<ITableUpdatedEvent> {
return this._onTableUpdated.event;
}
public get onCellModeChanged(): Event<boolean> {
return this._onCellModeChanged.event;
}
@@ -441,8 +446,6 @@ export class CellModel extends Disposable implements ICellModel {
public async runCell(notificationService?: INotificationService, connectionManagementService?: IConnectionManagementService): Promise<boolean> {
try {
// Clear grid data conversion promises from previous execution results
this._gridDataConversionComplete = [];
if (!this.active && this !== this.notebookModel.activeCell) {
this.notebookModel.updateActiveCell(this);
this.active = true;
@@ -536,6 +539,8 @@ export class CellModel extends Disposable implements ICellModel {
} finally {
this.disposeFuture();
this.fireExecutionStateChanged();
// Serialize cell output once the cell is done executing
this.sendChangeToNotebook(NotebookChangeType.CellOutputUpdated);
this.notifyExecutionComplete();
}
@@ -609,9 +614,7 @@ export class CellModel extends Disposable implements ICellModel {
shouldScroll: !!shouldScroll
};
this._onOutputsChanged.fire(outputEvent);
if (this.outputs.length !== 0) {
this.sendChangeToNotebook(NotebookChangeType.CellOutputUpdated);
} else {
if (this.outputs.length === 0) {
this.sendChangeToNotebook(NotebookChangeType.CellOutputCleared);
}
}
@@ -626,25 +629,6 @@ export class CellModel extends Disposable implements ICellModel {
return this._outputs;
}
public updateOutputData(batchId: number, id: number, data: any) {
for (let i = 0; i < this._outputs.length; i++) {
if (this._outputs[i].output_type === 'execute_result'
&& (<nb.IExecuteResult>this._outputs[i]).batchId === batchId
&& (<nb.IExecuteResult>this._outputs[i]).id === id) {
(<nb.IExecuteResult>this._outputs[i]).data = data;
break;
}
}
}
public get gridDataConversionComplete(): Promise<void> {
return Promise.all(this._gridDataConversionComplete).then();
}
public addGridDataConversionPromise(complete: Promise<void>): void {
this._gridDataConversionComplete.push(complete);
}
public get renderedOutputTextContent(): string[] {
return this._renderedOutputTextContent;
}
@@ -665,9 +649,49 @@ export class CellModel extends Disposable implements ICellModel {
private handleIOPub(msg: nb.IIOPubMessage): void {
let msgType = msg.header.msg_type;
let output: nb.ICellOutput;
let added = false;
switch (msgType) {
case 'execute_result':
output = msg.content as nb.ICellOutput;
output.output_type = msgType;
// Check if the table already exists
for (let i = 0; i < this._outputs.length; i++) {
if (this._outputs[i].output_type === 'execute_result') {
let resultSet: ResultSetSummary = this._outputs[i].metadata.resultSet;
let newResultSet: ResultSetSummary = output.metadata.resultSet;
if (resultSet.batchId === newResultSet.batchId && resultSet.id === newResultSet.id) {
// If it does, update output with data resource and html table
(<nb.IExecuteResult>this._outputs[i]).data = (<nb.IExecuteResult>output).data;
this._outputs[i].metadata = output.metadata;
added = true;
break;
}
}
}
break;
case 'execute_result_update':
let update = msg.content as nb.IExecuteResultUpdate;
// Send update to gridOutput component
this._onTableUpdated.fire({
resultSet: update.resultSet,
rows: update.data
});
break;
case 'display_data':
output = msg.content as nb.ICellOutput;
output.output_type = msgType;
// Display message outputs before grid outputs
if (this._outputs.length > 0) {
for (let i = 0; i < this._outputs.length; i++) {
if (this._outputs[i].output_type === 'execute_result') {
this._outputs.splice(i, 0, this.rewriteOutputUrls(output));
this.fireOutputsChanged();
added = true;
break;
}
}
}
break;
case 'stream':
case 'error':
output = msg.content as nb.ICellOutput;
@@ -698,25 +722,10 @@ export class CellModel extends Disposable implements ICellModel {
// targets.push(model.length - 1);
// this._displayIdMap.set(displayId, targets);
// }
if (output) {
if (output && !added) {
// deletes transient node in the serialized JSON
delete output['transient'];
// display message outputs before grid outputs
if (output.output_type === 'display_data' && this._outputs.length > 0) {
let added = false;
for (let i = 0; i < this._outputs.length; i++) {
if (this._outputs[i].output_type === 'execute_result') {
this._outputs.splice(i, 0, this.rewriteOutputUrls(output));
added = true;
break;
}
}
if (!added) {
this._outputs.push(this.rewriteOutputUrls(output));
}
} else {
this._outputs.push(this.rewriteOutputUrls(output));
}
this._outputs.push(this.rewriteOutputUrls(output));
// Only scroll on 1st output being added
let shouldScroll = this._outputs.length === 1;
this.fireOutputsChanged(shouldScroll);

View File

@@ -21,6 +21,7 @@ import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilit
import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/notebookModel';
import { IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents';
import type { FutureInternal } from 'sql/workbench/services/notebook/browser/interfaces';
import { ICellValue, ResultSetSummary } from 'sql/workbench/services/query/common/query';
export interface ICellRange {
readonly start: number;
@@ -237,10 +238,6 @@ export interface INotebookModel {
* Promise indicating when client session is ready to use.
*/
readonly sessionLoadFinished: Promise<void>;
/**
* Promise indicating when output grid data is converted to mimeType and html.
*/
gridDataConversionComplete: Promise<any>;
/**
* LanguageInfo saved in the notebook
*/
@@ -449,6 +446,11 @@ export interface IOutputChangedEvent {
shouldScroll: boolean;
}
export interface ITableUpdatedEvent {
resultSet: ResultSetSummary;
rows: ICellValue[][];
}
export interface ICellModel {
cellUri: URI;
id: string;
@@ -464,6 +466,7 @@ export interface ICellModel {
readonly outputs: ReadonlyArray<nb.ICellOutput>;
renderedOutputTextContent?: string[];
readonly onOutputsChanged: Event<IOutputChangedEvent>;
readonly onTableUpdated: Event<ITableUpdatedEvent>;
readonly onExecutionStateChange: Event<CellExecutionState>;
readonly executionState: CellExecutionState;
readonly notebookModel: NotebookModel;
@@ -491,9 +494,6 @@ export interface ICellModel {
readonly onCellMarkdownModeChanged: Event<boolean>;
sendChangeToNotebook(change: NotebookChangeType): void;
cellSourceChanged: boolean;
gridDataConversionComplete: Promise<void>;
addGridDataConversionPromise(complete: Promise<void>): void;
updateOutputData(batchId: number, id: number, data: any): void;
}
export interface IModelFactory {

View File

@@ -270,17 +270,6 @@ export class NotebookModel extends Disposable implements INotebookModel {
return this._sessionLoadFinished;
}
/**
* Indicates all result grid output has been converted to mimeType and html.
*/
public get gridDataConversionComplete(): Promise<any> {
let promises = [];
for (let cell of this._cells) {
promises.push(cell.gridDataConversionComplete);
}
return Promise.all(promises);
}
/**
* Notifies when the client session is ready for use
*/