Debounce cell output edits from EH

Fix #119832
This commit is contained in:
Rob Lourens
2021-04-22 12:48:38 -07:00
parent ddbc369b11
commit 8e51485f45
3 changed files with 52 additions and 23 deletions

View File

@@ -3,31 +3,32 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { timeout } from 'vs/base/common/async';
import { VSBuffer } from 'vs/base/common/buffer';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { Emitter, Event } from 'vs/base/common/event';
import { IRelativePattern } from 'vs/base/common/glob';
import { hash } from 'vs/base/common/hash';
import { IdGenerator } from 'vs/base/common/idGenerator';
import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { ResourceMap } from 'vs/base/common/map';
import { isFalsyOrWhitespace } from 'vs/base/common/strings';
import { assertIsDefined } from 'vs/base/common/types';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ExtHostNotebookShape, IMainContext, IModelAddedData, INotebookCellStatusBarListDto, INotebookDocumentPropertiesChangeData, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookEditorAddData, INotebookEditorPropertiesChangeData, INotebookEditorViewColumnInfo, MainContext, MainThreadNotebookDocumentsShape, MainThreadNotebookEditorsShape, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol';
import { ILogService } from 'vs/platform/log/common/log';
import { Cache } from 'vs/workbench/api/common/cache';
import { ExtHostNotebookShape, IMainContext, IModelAddedData, INotebookCellStatusBarListDto, INotebookDocumentPropertiesChangeData, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookEditorAddData, INotebookEditorPropertiesChangeData, INotebookEditorViewColumnInfo, MainContext, MainThreadNotebookDocumentsShape, MainThreadNotebookEditorsShape, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol';
import { CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
import { CellEditType, INotebookExclusiveDocumentFilter, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookDataDto, NullablePartialNotebookCellMetadata, IImmediateCellEditOperation } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellEditType, IImmediateCellEditOperation, INotebookExclusiveDocumentFilter, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookDataDto, NullablePartialNotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import type * as vscode from 'vscode';
import { ResourceMap } from 'vs/base/common/map';
import { ExtHostCell, ExtHostNotebookDocument } from './extHostNotebookDocument';
import { ExtHostNotebookEditor } from './extHostNotebookEditor';
import { IdGenerator } from 'vs/base/common/idGenerator';
import { IRelativePattern } from 'vs/base/common/glob';
import { assertIsDefined } from 'vs/base/common/types';
import { VSBuffer } from 'vs/base/common/buffer';
import { hash } from 'vs/base/common/hash';
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
import { Cache } from 'vs/workbench/api/common/cache';
import { isFalsyOrWhitespace } from 'vs/base/common/strings';
export class NotebookEditorDecorationType {
@@ -681,7 +682,9 @@ class NotebookCellExecutionTask extends Disposable {
private _state = NotebookCellExecutionTaskState.Init;
get state(): NotebookCellExecutionTaskState { return this._state; }
private readonly _tokenSource: CancellationTokenSource;
private readonly _tokenSource = this._register(new CancellationTokenSource());
private readonly _collector: TimeoutBasedCollector<IImmediateCellEditOperation>;
private _executionOrder: number | undefined;
@@ -691,7 +694,8 @@ class NotebookCellExecutionTask extends Disposable {
private readonly _cell: ExtHostCell,
private readonly _proxy: MainThreadNotebookDocumentsShape) {
super();
this._tokenSource = this._register(new CancellationTokenSource());
this._collector = new TimeoutBasedCollector(10, edits => this.applyEdits(edits));
this._executionOrder = _cell.internalMetadata.executionOrder;
this.mixinMetadata({
@@ -704,6 +708,10 @@ class NotebookCellExecutionTask extends Disposable {
this._tokenSource.cancel();
}
private async applyEditSoon(edit: IImmediateCellEditOperation): Promise<void> {
await this._collector.addItem(edit);
}
private async applyEdits(edits: IImmediateCellEditOperation[]): Promise<void> {
return this._proxy.$applyEdits(this._uri, edits, false);
}
@@ -719,10 +727,8 @@ class NotebookCellExecutionTask extends Disposable {
}
private mixinMetadata(mixinMetadata: NullablePartialNotebookCellMetadata) {
const edits: IImmediateCellEditOperation[] = [
{ editType: CellEditType.PartialMetadata, handle: this._cell.handle, metadata: mixinMetadata }
];
this.applyEdits(edits);
const edit: IImmediateCellEditOperation = { editType: CellEditType.PartialMetadata, handle: this._cell.handle, metadata: mixinMetadata };
this.applyEdits([edit]);
}
private cellIndexToHandle(cellIndex: number | undefined): number | undefined {
@@ -790,7 +796,7 @@ class NotebookCellExecutionTask extends Disposable {
}
outputs = Array.isArray(outputs) ? outputs : [outputs];
return that.applyEdits([{ editType: CellEditType.Output, handle, append: true, outputs: outputs.map(typeConverters.NotebookCellOutput.from) }]);
return that.applyEditSoon({ editType: CellEditType.Output, handle, append: true, outputs: outputs.map(typeConverters.NotebookCellOutput.from) });
},
async replaceOutput(outputs: vscode.NotebookCellOutput | vscode.NotebookCellOutput[], cellIndex?: number): Promise<void> {
@@ -801,22 +807,45 @@ class NotebookCellExecutionTask extends Disposable {
}
outputs = Array.isArray(outputs) ? outputs : [outputs];
return that.applyEdits([{ editType: CellEditType.Output, handle, outputs: outputs.map(typeConverters.NotebookCellOutput.from) }]);
return that.applyEditSoon({ editType: CellEditType.Output, handle, outputs: outputs.map(typeConverters.NotebookCellOutput.from) });
},
async appendOutputItems(items: vscode.NotebookCellOutputItem | vscode.NotebookCellOutputItem[], outputId: string): Promise<void> {
that.verifyStateForOutput();
items = Array.isArray(items) ? items : [items];
return that.applyEdits([{ editType: CellEditType.OutputItems, append: true, items: items.map(typeConverters.NotebookCellOutputItem.from), outputId }]);
return that.applyEditSoon({ editType: CellEditType.OutputItems, append: true, items: items.map(typeConverters.NotebookCellOutputItem.from), outputId });
},
async replaceOutputItems(items: vscode.NotebookCellOutputItem | vscode.NotebookCellOutputItem[], outputId: string): Promise<void> {
that.verifyStateForOutput();
items = Array.isArray(items) ? items : [items];
return that.applyEdits([{ editType: CellEditType.OutputItems, items: items.map(typeConverters.NotebookCellOutputItem.from), outputId }]);
return that.applyEditSoon({ editType: CellEditType.OutputItems, items: items.map(typeConverters.NotebookCellOutputItem.from), outputId });
},
token: that._tokenSource.token
});
}
}
class TimeoutBasedCollector<T> {
private batch: T[] = [];
private waitPromise: Promise<void> | undefined;
constructor(
private readonly delay: number,
private readonly callback: (items: T[]) => Promise<void>) { }
addItem(item: T): Promise<void> {
this.batch.push(item);
if (!this.waitPromise) {
this.waitPromise = timeout(this.delay).then(() => {
this.waitPromise = undefined;
const batch = this.batch;
this.batch = [];
return this.callback(batch);
});
}
return this.waitPromise;
}
}