From 0edc352ebc037c0bd179aa2ef48decbcfd472fdb Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Mon, 10 Jul 2023 11:16:53 -0700 Subject: [PATCH] test for ouput compression --- .../model/notebookCellOutputTextModel.ts | 8 +++- .../contrib/notebook/common/notebookCommon.ts | 14 ++++-- .../test/browser/notebookTextModel.test.ts | 44 ++++++++++++++++++- 3 files changed, 60 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookCellOutputTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookCellOutputTextModel.ts index 7b989ef0076..74ed0aaf158 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookCellOutputTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookCellOutputTextModel.ts @@ -96,11 +96,15 @@ export class NotebookCellOutputTextModel extends Disposable implements ICellOutp }); this.outputs.length = 0; mimeTypes.forEach(mime => { - const compressed = compressOutputItemStreams(mimeOutputs.get(mime)!); + const compressionResult = compressOutputItemStreams(mimeOutputs.get(mime)!); this.outputs.push({ mime, - data: compressed + data: compressionResult.data }); + if (compressionResult.didCompression) { + // we can't rely on knowing buffer lengths if we've erased previous lines + this.versionedBufferLengths = {}; + } }); } } diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index c2c0e5a9f08..d031650968a 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -996,6 +996,7 @@ const textDecoder = new TextDecoder(); * Given a stream of individual stdout outputs, this function will return the compressed lines, escaping some of the common terminal escape codes. * E.g. some terminal escape codes would result in the previous line getting cleared, such if we had 3 lines and * last line contained such a code, then the result string would be just the first two lines. + * @returns a single VSBuffer with the concatenated and compressed data, and whether any compression was done. */ export function compressOutputItemStreams(outputs: Uint8Array[]) { const buffers: Uint8Array[] = []; @@ -1008,13 +1009,17 @@ export function compressOutputItemStreams(outputs: Uint8Array[]) { startAppending = true; } } - compressStreamBuffer(buffers); - return formatStreamText(VSBuffer.concat(buffers.map(buffer => VSBuffer.wrap(buffer)))); + + const didCompression = compressStreamBuffer(buffers); + const data = formatStreamText(VSBuffer.concat(buffers.map(buffer => VSBuffer.wrap(buffer)))); + return { data, didCompression }; } -const MOVE_CURSOR_1_LINE_COMMAND = `${String.fromCharCode(27)}[A`; + +export const MOVE_CURSOR_1_LINE_COMMAND = `${String.fromCharCode(27)}[A`; const MOVE_CURSOR_1_LINE_COMMAND_BYTES = MOVE_CURSOR_1_LINE_COMMAND.split('').map(c => c.charCodeAt(0)); const LINE_FEED = 10; function compressStreamBuffer(streams: Uint8Array[]) { + let didCompress = false; streams.forEach((stream, index) => { if (index === 0 || stream.length < MOVE_CURSOR_1_LINE_COMMAND.length) { return; @@ -1029,10 +1034,13 @@ function compressStreamBuffer(streams: Uint8Array[]) { if (lastIndexOfLineFeed === -1) { return; } + + didCompress = true; streams[index - 1] = previousStream.subarray(0, lastIndexOfLineFeed); streams[index] = stream.subarray(MOVE_CURSOR_1_LINE_COMMAND.length); } }); + return didCompress; } diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookTextModel.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookTextModel.test.ts index a0759f0043d..c2cce9c8e78 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookTextModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookTextModel.test.ts @@ -11,7 +11,7 @@ import { ILanguageService } from 'vs/editor/common/languages/language'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CellEditType, CellKind, ICellEditOperation, NotebookTextModelChangedEvent, NotebookTextModelWillAddRemoveEvent, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellEditType, CellKind, ICellEditOperation, MOVE_CURSOR_1_LINE_COMMAND, NotebookTextModelChangedEvent, NotebookTextModelWillAddRemoveEvent, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { setupInstantiationService, TestCell, valueBytesFromString, withTestNotebook } from 'vs/workbench/contrib/notebook/test/browser/testNotebookEditor'; suite('NotebookTextModel', () => { @@ -436,6 +436,48 @@ suite('NotebookTextModel', () => { ); }); + test('appending streaming outputs with compression', async function () { + + await withTestNotebook( + [ + ['var a = 1;', 'javascript', CellKind.Code, [], {}], + ], + (editor) => { + const textModel = editor.textModel; + + textModel.applyEdits([ + { + index: 0, + editType: CellEditType.Output, + append: true, + outputs: [{ + outputId: 'append1', + outputs: [ + { mime: stdOutMime, data: valueBytesFromString('append 1') }, + { mime: stdOutMime, data: valueBytesFromString('\nappend 1') }] + }] + }], true, undefined, () => undefined, undefined, true); + const [output] = textModel.cells[0].outputs; + assert.strictEqual(output.versionId, 0, 'initial output version should be 0'); + + textModel.applyEdits([ + { + editType: CellEditType.OutputItems, + append: true, + outputId: 'append1', + items: [{ + mime: stdOutMime, data: valueBytesFromString(MOVE_CURSOR_1_LINE_COMMAND + '\nappend 2') + }] + }], true, undefined, () => undefined, undefined, true); + assert.strictEqual(output.versionId, 1, 'version should bump per append'); + + assert.strictEqual(output.outputs[0].data.toString(), 'append 1\nappend 2'); + assert.strictEqual(output.appendedSinceVersion(0, stdOutMime), undefined, + 'compressing outputs should clear out previous versioned output buffers'); + } + ); + }); + test('appending multiple different mime streaming outputs', async function () { await withTestNotebook( [