Path: blob/main/src/vs/workbench/contrib/notebook/test/browser/notebookTextModel.test.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import assert from 'assert';6import { VSBuffer } from '../../../../../base/common/buffer.js';7import { DisposableStore } from '../../../../../base/common/lifecycle.js';8import { Mimes } from '../../../../../base/common/mime.js';9import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';10import { Position } from '../../../../../editor/common/core/position.js';11import { ILanguageService } from '../../../../../editor/common/languages/language.js';12import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js';13import { IUndoRedoService } from '../../../../../platform/undoRedo/common/undoRedo.js';14import { NotebookTextModel } from '../../common/model/notebookTextModel.js';15import { CellEditType, CellKind, ICellEditOperation, MOVE_CURSOR_1_LINE_COMMAND, NotebookTextModelChangedEvent, NotebookTextModelWillAddRemoveEvent, SelectionStateType } from '../../common/notebookCommon.js';16import { setupInstantiationService, TestCell, valueBytesFromString, withTestNotebook } from './testNotebookEditor.js';1718suite('NotebookTextModel', () => {19let disposables: DisposableStore;20let instantiationService: TestInstantiationService;21let languageService: ILanguageService;2223ensureNoDisposablesAreLeakedInTestSuite();2425suiteSetup(() => {26disposables = new DisposableStore();27instantiationService = setupInstantiationService(disposables);28languageService = instantiationService.get(ILanguageService);29instantiationService.spy(IUndoRedoService, 'pushElement');30});3132suiteTeardown(() => disposables.dispose());3334test('insert', async function () {35await withTestNotebook(36[37['var a = 1;', 'javascript', CellKind.Code, [], {}],38['var b = 2;', 'javascript', CellKind.Code, [], {}],39['var c = 3;', 'javascript', CellKind.Code, [], {}],40['var d = 4;', 'javascript', CellKind.Code, [], {}]41],42(editor, _viewModel, ds) => {43const textModel = editor.textModel;44textModel.applyEdits([45{ editType: CellEditType.Replace, index: 1, count: 0, cells: [ds.add(new TestCell(textModel.viewType, 5, 'var e = 5;', 'javascript', CellKind.Code, [], languageService))] },46{ editType: CellEditType.Replace, index: 3, count: 0, cells: [ds.add(new TestCell(textModel.viewType, 6, 'var f = 6;', 'javascript', CellKind.Code, [], languageService))] },47], true, undefined, () => undefined, undefined, true);4849assert.strictEqual(textModel.cells.length, 6);5051assert.strictEqual(textModel.cells[1].getValue(), 'var e = 5;');52assert.strictEqual(textModel.cells[4].getValue(), 'var f = 6;');53}54);55});5657test('multiple inserts at same position', async function () {58await withTestNotebook(59[60['var a = 1;', 'javascript', CellKind.Code, [], {}],61['var b = 2;', 'javascript', CellKind.Code, [], {}],62['var c = 3;', 'javascript', CellKind.Code, [], {}],63['var d = 4;', 'javascript', CellKind.Code, [], {}]64],65(editor, _viewModel, ds) => {66const textModel = editor.textModel;67textModel.applyEdits([68{ editType: CellEditType.Replace, index: 1, count: 0, cells: [ds.add(new TestCell(textModel.viewType, 5, 'var e = 5;', 'javascript', CellKind.Code, [], languageService))] },69{ editType: CellEditType.Replace, index: 1, count: 0, cells: [ds.add(new TestCell(textModel.viewType, 6, 'var f = 6;', 'javascript', CellKind.Code, [], languageService))] },70], true, undefined, () => undefined, undefined, true);7172assert.strictEqual(textModel.cells.length, 6);7374assert.strictEqual(textModel.cells[1].getValue(), 'var e = 5;');75assert.strictEqual(textModel.cells[2].getValue(), 'var f = 6;');76}77);78});7980test('delete', async function () {81await withTestNotebook(82[83['var a = 1;', 'javascript', CellKind.Code, [], {}],84['var b = 2;', 'javascript', CellKind.Code, [], {}],85['var c = 3;', 'javascript', CellKind.Code, [], {}],86['var d = 4;', 'javascript', CellKind.Code, [], {}]87],88(editor) => {89const textModel = editor.textModel;90textModel.applyEdits([91{ editType: CellEditType.Replace, index: 1, count: 1, cells: [] },92{ editType: CellEditType.Replace, index: 3, count: 1, cells: [] },93], true, undefined, () => undefined, undefined, true);9495assert.strictEqual(textModel.cells[0].getValue(), 'var a = 1;');96assert.strictEqual(textModel.cells[1].getValue(), 'var c = 3;');97}98);99});100101test('delete + insert', async function () {102await withTestNotebook(103[104['var a = 1;', 'javascript', CellKind.Code, [], {}],105['var b = 2;', 'javascript', CellKind.Code, [], {}],106['var c = 3;', 'javascript', CellKind.Code, [], {}],107['var d = 4;', 'javascript', CellKind.Code, [], {}]108],109(editor, _viewModel, ds) => {110const textModel = editor.textModel;111textModel.applyEdits([112{ editType: CellEditType.Replace, index: 1, count: 1, cells: [] },113{ editType: CellEditType.Replace, index: 3, count: 0, cells: [ds.add(new TestCell(textModel.viewType, 5, 'var e = 5;', 'javascript', CellKind.Code, [], languageService))] },114], true, undefined, () => undefined, undefined, true);115assert.strictEqual(textModel.cells.length, 4);116117assert.strictEqual(textModel.cells[0].getValue(), 'var a = 1;');118assert.strictEqual(textModel.cells[2].getValue(), 'var e = 5;');119}120);121});122123test('delete + insert at same position', async function () {124await withTestNotebook(125[126['var a = 1;', 'javascript', CellKind.Code, [], {}],127['var b = 2;', 'javascript', CellKind.Code, [], {}],128['var c = 3;', 'javascript', CellKind.Code, [], {}],129['var d = 4;', 'javascript', CellKind.Code, [], {}]130],131(editor, _viewModel, ds) => {132const textModel = editor.textModel;133textModel.applyEdits([134{ editType: CellEditType.Replace, index: 1, count: 1, cells: [] },135{ editType: CellEditType.Replace, index: 1, count: 0, cells: [ds.add(new TestCell(textModel.viewType, 5, 'var e = 5;', 'javascript', CellKind.Code, [], languageService))] },136], true, undefined, () => undefined, undefined, true);137138assert.strictEqual(textModel.cells.length, 4);139assert.strictEqual(textModel.cells[0].getValue(), 'var a = 1;');140assert.strictEqual(textModel.cells[1].getValue(), 'var e = 5;');141assert.strictEqual(textModel.cells[2].getValue(), 'var c = 3;');142}143);144});145146test('(replace) delete + insert at same position', async function () {147await withTestNotebook(148[149['var a = 1;', 'javascript', CellKind.Code, [], {}],150['var b = 2;', 'javascript', CellKind.Code, [], {}],151['var c = 3;', 'javascript', CellKind.Code, [], {}],152['var d = 4;', 'javascript', CellKind.Code, [], {}]153],154(editor, _viewModel, ds) => {155const textModel = editor.textModel;156textModel.applyEdits([157{ editType: CellEditType.Replace, index: 1, count: 1, cells: [ds.add(new TestCell(textModel.viewType, 5, 'var e = 5;', 'javascript', CellKind.Code, [], languageService))] },158], true, undefined, () => undefined, undefined, true);159160assert.strictEqual(textModel.cells.length, 4);161assert.strictEqual(textModel.cells[0].getValue(), 'var a = 1;');162assert.strictEqual(textModel.cells[1].getValue(), 'var e = 5;');163assert.strictEqual(textModel.cells[2].getValue(), 'var c = 3;');164}165);166});167168test('output', async function () {169await withTestNotebook(170[171['var a = 1;', 'javascript', CellKind.Code, [], {}],172],173(editor) => {174const textModel = editor.textModel;175176// invalid index 1177assert.throws(() => {178textModel.applyEdits([{179index: Number.MAX_VALUE,180editType: CellEditType.Output,181outputs: []182}], true, undefined, () => undefined, undefined, true);183});184185// invalid index 2186assert.throws(() => {187textModel.applyEdits([{188index: -1,189editType: CellEditType.Output,190outputs: []191}], true, undefined, () => undefined, undefined, true);192});193194textModel.applyEdits([{195index: 0,196editType: CellEditType.Output,197outputs: [{198outputId: 'someId',199outputs: [{ mime: Mimes.markdown, data: valueBytesFromString('_Hello_') }]200}]201}], true, undefined, () => undefined, undefined, true);202203assert.strictEqual(textModel.cells.length, 1);204assert.strictEqual(textModel.cells[0].outputs.length, 1);205206// append207textModel.applyEdits([{208index: 0,209editType: CellEditType.Output,210append: true,211outputs: [{212outputId: 'someId2',213outputs: [{ mime: Mimes.markdown, data: valueBytesFromString('_Hello2_') }]214}]215}], true, undefined, () => undefined, undefined, true);216217assert.strictEqual(textModel.cells.length, 1);218assert.strictEqual(textModel.cells[0].outputs.length, 2);219let [first, second] = textModel.cells[0].outputs;220assert.strictEqual(first.outputId, 'someId');221assert.strictEqual(second.outputId, 'someId2');222223// replace all224textModel.applyEdits([{225index: 0,226editType: CellEditType.Output,227outputs: [{228outputId: 'someId3',229outputs: [{ mime: Mimes.text, data: valueBytesFromString('Last, replaced output') }]230}]231}], true, undefined, () => undefined, undefined, true);232233assert.strictEqual(textModel.cells.length, 1);234assert.strictEqual(textModel.cells[0].outputs.length, 1);235[first] = textModel.cells[0].outputs;236assert.strictEqual(first.outputId, 'someId3');237}238);239});240241test('multiple append output in one position', async function () {242await withTestNotebook(243[244['var a = 1;', 'javascript', CellKind.Code, [], {}],245],246(editor) => {247const textModel = editor.textModel;248249// append250textModel.applyEdits([251{252index: 0,253editType: CellEditType.Output,254append: true,255outputs: [{256outputId: 'append1',257outputs: [{ mime: Mimes.markdown, data: valueBytesFromString('append 1') }]258}]259},260{261index: 0,262editType: CellEditType.Output,263append: true,264outputs: [{265outputId: 'append2',266outputs: [{ mime: Mimes.markdown, data: valueBytesFromString('append 2') }]267}]268}269], true, undefined, () => undefined, undefined, true);270271assert.strictEqual(textModel.cells.length, 1);272assert.strictEqual(textModel.cells[0].outputs.length, 2);273const [first, second] = textModel.cells[0].outputs;274assert.strictEqual(first.outputId, 'append1');275assert.strictEqual(second.outputId, 'append2');276}277);278});279280test('append to output created in same batch', async function () {281await withTestNotebook(282[283['var a = 1;', 'javascript', CellKind.Code, [], {}],284],285(editor) => {286const textModel = editor.textModel;287288textModel.applyEdits([289{290index: 0,291editType: CellEditType.Output,292append: true,293outputs: [{294outputId: 'append1',295outputs: [{ mime: Mimes.markdown, data: valueBytesFromString('append 1') }]296}]297},298{299editType: CellEditType.OutputItems,300append: true,301outputId: 'append1',302items: [{303mime: Mimes.markdown, data: valueBytesFromString('append 2')304}]305}306], true, undefined, () => undefined, undefined, true);307308assert.strictEqual(textModel.cells.length, 1);309assert.strictEqual(textModel.cells[0].outputs.length, 1, 'has 1 output');310const [first] = textModel.cells[0].outputs;311assert.strictEqual(first.outputId, 'append1');312assert.strictEqual(first.outputs.length, 2, 'has 2 items');313}314);315});316317const stdOutMime = 'application/vnd.code.notebook.stdout';318const stdErrMime = 'application/vnd.code.notebook.stderr';319320test('appending streaming outputs', async function () {321await withTestNotebook(322[323['var a = 1;', 'javascript', CellKind.Code, [], {}],324],325(editor) => {326const textModel = editor.textModel;327328textModel.applyEdits([329{330index: 0,331editType: CellEditType.Output,332append: true,333outputs: [{334outputId: 'append1',335outputs: [{ mime: stdOutMime, data: valueBytesFromString('append 1') }]336}]337}], true, undefined, () => undefined, undefined, true);338const [output] = textModel.cells[0].outputs;339assert.strictEqual(output.versionId, 0, 'initial output version should be 0');340341textModel.applyEdits([342{343editType: CellEditType.OutputItems,344append: true,345outputId: 'append1',346items: [347{ mime: stdOutMime, data: valueBytesFromString('append 2') },348{ mime: stdOutMime, data: valueBytesFromString('append 3') }349]350}], true, undefined, () => undefined, undefined, true);351assert.strictEqual(output.versionId, 1, 'version should bump per append');352353textModel.applyEdits([354{355editType: CellEditType.OutputItems,356append: true,357outputId: 'append1',358items: [359{ mime: stdOutMime, data: valueBytesFromString('append 4') },360{ mime: stdOutMime, data: valueBytesFromString('append 5') }361]362}], true, undefined, () => undefined, undefined, true);363assert.strictEqual(output.versionId, 2, 'version should bump per append');364365assert.strictEqual(textModel.cells.length, 1);366assert.strictEqual(textModel.cells[0].outputs.length, 1, 'has 1 output');367assert.strictEqual(output.outputId, 'append1');368assert.strictEqual(output.outputs.length, 1, 'outputs are compressed');369assert.strictEqual(output.outputs[0].data.toString(), 'append 1append 2append 3append 4append 5');370assert.strictEqual(output.appendedSinceVersion(0, stdOutMime)?.toString(), 'append 2append 3append 4append 5');371assert.strictEqual(output.appendedSinceVersion(1, stdOutMime)?.toString(), 'append 4append 5');372assert.strictEqual(output.appendedSinceVersion(2, stdOutMime), undefined);373assert.strictEqual(output.appendedSinceVersion(2, stdErrMime), undefined);374}375);376});377378test('replacing streaming outputs', async function () {379await withTestNotebook(380[381['var a = 1;', 'javascript', CellKind.Code, [], {}],382],383(editor) => {384const textModel = editor.textModel;385386textModel.applyEdits([387{388index: 0,389editType: CellEditType.Output,390append: true,391outputs: [{392outputId: 'append1',393outputs: [{ mime: stdOutMime, data: valueBytesFromString('append 1') }]394}]395}], true, undefined, () => undefined, undefined, true);396const [output] = textModel.cells[0].outputs;397assert.strictEqual(output.versionId, 0, 'initial output version should be 0');398399textModel.applyEdits([400{401editType: CellEditType.OutputItems,402append: true,403outputId: 'append1',404items: [{405mime: stdOutMime, data: valueBytesFromString('append 2')406}]407}], true, undefined, () => undefined, undefined, true);408assert.strictEqual(output.versionId, 1, 'version should bump per append');409410textModel.applyEdits([411{412editType: CellEditType.OutputItems,413append: false,414outputId: 'append1',415items: [{416mime: stdOutMime, data: valueBytesFromString('replace 3')417}]418}], true, undefined, () => undefined, undefined, true);419assert.strictEqual(output.versionId, 2, 'version should bump per replace');420421textModel.applyEdits([422{423editType: CellEditType.OutputItems,424append: true,425outputId: 'append1',426items: [{427mime: stdOutMime, data: valueBytesFromString('append 4')428}]429}], true, undefined, () => undefined, undefined, true);430assert.strictEqual(output.versionId, 3, 'version should bump per append');431432assert.strictEqual(output.outputs[0].data.toString(), 'replace 3append 4');433assert.strictEqual(output.appendedSinceVersion(0, stdOutMime), undefined,434'replacing output should clear out previous versioned output buffers');435assert.strictEqual(output.appendedSinceVersion(1, stdOutMime), undefined,436'replacing output should clear out previous versioned output buffers');437assert.strictEqual(output.appendedSinceVersion(2, stdOutMime)?.toString(), 'append 4');438}439);440});441442test('appending streaming outputs with move cursor compression', async function () {443444await withTestNotebook(445[446['var a = 1;', 'javascript', CellKind.Code, [], {}],447],448(editor) => {449const textModel = editor.textModel;450451textModel.applyEdits([452{453index: 0,454editType: CellEditType.Output,455append: true,456outputs: [{457outputId: 'append1',458outputs: [459{ mime: stdOutMime, data: valueBytesFromString('append 1') },460{ mime: stdOutMime, data: valueBytesFromString('\nappend 1') }]461}]462}], true, undefined, () => undefined, undefined, true);463const [output] = textModel.cells[0].outputs;464assert.strictEqual(output.versionId, 0, 'initial output version should be 0');465466textModel.applyEdits([467{468editType: CellEditType.OutputItems,469append: true,470outputId: 'append1',471items: [{472mime: stdOutMime, data: valueBytesFromString(MOVE_CURSOR_1_LINE_COMMAND + '\nappend 2')473}]474}], true, undefined, () => undefined, undefined, true);475assert.strictEqual(output.versionId, 1, 'version should bump per append');476477assert.strictEqual(output.outputs[0].data.toString(), 'append 1\nappend 2');478assert.strictEqual(output.appendedSinceVersion(0, stdOutMime), undefined,479'compressing outputs should clear out previous versioned output buffers');480}481);482});483484test('appending streaming outputs with carraige return compression', async function () {485486await withTestNotebook(487[488['var a = 1;', 'javascript', CellKind.Code, [], {}],489],490(editor) => {491const textModel = editor.textModel;492493textModel.applyEdits([494{495index: 0,496editType: CellEditType.Output,497append: true,498outputs: [{499outputId: 'append1',500outputs: [501{ mime: stdOutMime, data: valueBytesFromString('append 1') },502{ mime: stdOutMime, data: valueBytesFromString('\nappend 1') }]503}]504}], true, undefined, () => undefined, undefined, true);505const [output] = textModel.cells[0].outputs;506assert.strictEqual(output.versionId, 0, 'initial output version should be 0');507508textModel.applyEdits([509{510editType: CellEditType.OutputItems,511append: true,512outputId: 'append1',513items: [{514mime: stdOutMime, data: valueBytesFromString('\rappend 2')515}]516}], true, undefined, () => undefined, undefined, true);517assert.strictEqual(output.versionId, 1, 'version should bump per append');518519assert.strictEqual(output.outputs[0].data.toString(), 'append 1\nappend 2');520assert.strictEqual(output.appendedSinceVersion(0, stdOutMime), undefined,521'compressing outputs should clear out previous versioned output buffers');522}523);524});525526test('appending multiple different mime streaming outputs', async function () {527await withTestNotebook(528[529['var a = 1;', 'javascript', CellKind.Code, [], {}],530],531(editor) => {532const textModel = editor.textModel;533534textModel.applyEdits([535{536index: 0,537editType: CellEditType.Output,538append: true,539outputs: [{540outputId: 'append1',541outputs: [542{ mime: stdOutMime, data: valueBytesFromString('stdout 1') },543{ mime: stdErrMime, data: valueBytesFromString('stderr 1') }544]545}]546}], true, undefined, () => undefined, undefined, true);547const [output] = textModel.cells[0].outputs;548assert.strictEqual(output.versionId, 0, 'initial output version should be 0');549550textModel.applyEdits([551{552editType: CellEditType.OutputItems,553append: true,554outputId: 'append1',555items: [556{ mime: stdOutMime, data: valueBytesFromString('stdout 2') },557{ mime: stdErrMime, data: valueBytesFromString('stderr 2') }558]559}], true, undefined, () => undefined, undefined, true);560assert.strictEqual(output.versionId, 1, 'version should bump per replace');561562assert.strictEqual(output.appendedSinceVersion(0, stdErrMime)?.toString(), 'stderr 2');563assert.strictEqual(output.appendedSinceVersion(0, stdOutMime)?.toString(), 'stdout 2');564}565);566});567568test('metadata', async function () {569await withTestNotebook(570[571['var a = 1;', 'javascript', CellKind.Code, [], {}],572],573(editor) => {574const textModel = editor.textModel;575576// invalid index 1577assert.throws(() => {578textModel.applyEdits([{579index: Number.MAX_VALUE,580editType: CellEditType.Metadata,581metadata: {}582}], true, undefined, () => undefined, undefined, true);583});584585// invalid index 2586assert.throws(() => {587textModel.applyEdits([{588index: -1,589editType: CellEditType.Metadata,590metadata: {}591}], true, undefined, () => undefined, undefined, true);592});593594textModel.applyEdits([{595index: 0,596editType: CellEditType.Metadata,597metadata: { customProperty: 15 },598}], true, undefined, () => undefined, undefined, true);599600textModel.applyEdits([{601index: 0,602editType: CellEditType.Metadata,603metadata: {},604}], true, undefined, () => undefined, undefined, true);605606assert.strictEqual(textModel.cells.length, 1);607assert.strictEqual(textModel.cells[0].metadata.customProperty, undefined);608}609);610});611612test('partial metadata', async function () {613await withTestNotebook(614[615['var a = 1;', 'javascript', CellKind.Code, [], {}],616],617(editor) => {618const textModel = editor.textModel;619620textModel.applyEdits([{621index: 0,622editType: CellEditType.PartialMetadata,623metadata: { customProperty: 15 },624}], true, undefined, () => undefined, undefined, true);625626textModel.applyEdits([{627index: 0,628editType: CellEditType.PartialMetadata,629metadata: {},630}], true, undefined, () => undefined, undefined, true);631632assert.strictEqual(textModel.cells.length, 1);633assert.strictEqual(textModel.cells[0].metadata.customProperty, 15);634}635);636});637638test('multiple inserts in one edit', async function () {639await withTestNotebook(640[641['var a = 1;', 'javascript', CellKind.Code, [], {}],642['var b = 2;', 'javascript', CellKind.Code, [], {}],643['var c = 3;', 'javascript', CellKind.Code, [], {}],644['var d = 4;', 'javascript', CellKind.Code, [], {}]645],646(editor, _viewModel, ds) => {647const textModel = editor.textModel;648let changeEvent: NotebookTextModelChangedEvent | undefined = undefined;649const eventListener = textModel.onDidChangeContent(e => {650changeEvent = e;651});652const willChangeEvents: NotebookTextModelWillAddRemoveEvent[] = [];653const willChangeListener = textModel.onWillAddRemoveCells(e => {654willChangeEvents.push(e);655});656const version = textModel.versionId;657658textModel.applyEdits([659{ editType: CellEditType.Replace, index: 1, count: 1, cells: [] },660{ editType: CellEditType.Replace, index: 1, count: 0, cells: [ds.add(new TestCell(textModel.viewType, 5, 'var e = 5;', 'javascript', CellKind.Code, [], languageService))] },661], true, undefined, () => ({ kind: SelectionStateType.Index, focus: { start: 0, end: 1 }, selections: [{ start: 0, end: 1 }] }), undefined, true);662663assert.strictEqual(textModel.cells.length, 4);664assert.strictEqual(textModel.cells[0].getValue(), 'var a = 1;');665assert.strictEqual(textModel.cells[1].getValue(), 'var e = 5;');666assert.strictEqual(textModel.cells[2].getValue(), 'var c = 3;');667668assert.notStrictEqual(changeEvent, undefined);669assert.strictEqual(changeEvent!.rawEvents.length, 2);670assert.deepStrictEqual(changeEvent!.endSelectionState?.selections, [{ start: 0, end: 1 }]);671assert.strictEqual(willChangeEvents.length, 2);672assert.strictEqual(textModel.versionId, version + 1);673eventListener.dispose();674willChangeListener.dispose();675}676);677});678679test('insert and metadata change in one edit', async function () {680await withTestNotebook(681[682['var a = 1;', 'javascript', CellKind.Code, [], {}],683['var b = 2;', 'javascript', CellKind.Code, [], {}],684['var c = 3;', 'javascript', CellKind.Code, [], {}],685['var d = 4;', 'javascript', CellKind.Code, [], {}]686],687(editor) => {688const textModel = editor.textModel;689let changeEvent: NotebookTextModelChangedEvent | undefined = undefined;690const eventListener = textModel.onDidChangeContent(e => {691changeEvent = e;692});693const willChangeEvents: NotebookTextModelWillAddRemoveEvent[] = [];694const willChangeListener = textModel.onWillAddRemoveCells(e => {695willChangeEvents.push(e);696});697698const version = textModel.versionId;699700textModel.applyEdits([701{ editType: CellEditType.Replace, index: 1, count: 1, cells: [] },702{703index: 0,704editType: CellEditType.Metadata,705metadata: {},706}707], true, undefined, () => ({ kind: SelectionStateType.Index, focus: { start: 0, end: 1 }, selections: [{ start: 0, end: 1 }] }), undefined, true);708709assert.notStrictEqual(changeEvent, undefined);710assert.strictEqual(changeEvent!.rawEvents.length, 2);711assert.deepStrictEqual(changeEvent!.endSelectionState?.selections, [{ start: 0, end: 1 }]);712assert.strictEqual(willChangeEvents.length, 1);713assert.strictEqual(textModel.versionId, version + 1);714eventListener.dispose();715willChangeListener.dispose();716}717);718});719720721test('Updating appending/updating output in Notebooks does not work as expected #117273', async function () {722await withTestNotebook([723['var a = 1;', 'javascript', CellKind.Code, [], {}]724], (editor) => {725const model = editor.textModel;726727assert.strictEqual(model.cells.length, 1);728assert.strictEqual(model.cells[0].outputs.length, 0);729730const success1 = model.applyEdits(731[{732editType: CellEditType.Output, index: 0, outputs: [733{ outputId: 'out1', outputs: [{ mime: 'application/x.notebook.stream', data: VSBuffer.wrap(new Uint8Array([1])) }] }734],735append: false736}], true, undefined, () => undefined, undefined, false737);738739assert.ok(success1);740assert.strictEqual(model.cells[0].outputs.length, 1);741742const success2 = model.applyEdits(743[{744editType: CellEditType.Output, index: 0, outputs: [745{ outputId: 'out2', outputs: [{ mime: 'application/x.notebook.stream', data: VSBuffer.wrap(new Uint8Array([1])) }] }746],747append: true748}], true, undefined, () => undefined, undefined, false749);750751assert.ok(success2);752assert.strictEqual(model.cells[0].outputs.length, 2);753});754});755756test('Clearing output of an empty notebook makes it dirty #119608', async function () {757await withTestNotebook([758['var a = 1;', 'javascript', CellKind.Code, [], {}],759['var b = 2;', 'javascript', CellKind.Code, [], {}]760], (editor, _, ds) => {761const model = editor.textModel;762763let event: NotebookTextModelChangedEvent | undefined;764765ds.add(model.onDidChangeContent(e => { event = e; }));766767{768// 1: add ouput -> event769const success = model.applyEdits(770[{771editType: CellEditType.Output, index: 0, outputs: [772{ outputId: 'out1', outputs: [{ mime: 'application/x.notebook.stream', data: VSBuffer.wrap(new Uint8Array([1])) }] }773],774append: false775}], true, undefined, () => undefined, undefined, false776);777778assert.ok(success);779assert.strictEqual(model.cells[0].outputs.length, 1);780assert.ok(event);781}782783{784// 2: clear all output w/ output -> event785event = undefined;786const success = model.applyEdits(787[{788editType: CellEditType.Output,789index: 0,790outputs: [],791append: false792}, {793editType: CellEditType.Output,794index: 1,795outputs: [],796append: false797}], true, undefined, () => undefined, undefined, false798);799assert.ok(success);800assert.ok(event);801}802803{804// 2: clear all output wo/ output -> NO event805event = undefined;806const success = model.applyEdits(807[{808editType: CellEditType.Output,809index: 0,810outputs: [],811append: false812}, {813editType: CellEditType.Output,814index: 1,815outputs: [],816append: false817}], true, undefined, () => undefined, undefined, false818);819820assert.ok(success);821assert.ok(event === undefined);822}823});824});825826test('Cell metadata/output change should update version id and alternative id #121807', async function () {827await withTestNotebook([828['var a = 1;', 'javascript', CellKind.Code, [], {}],829['var b = 2;', 'javascript', CellKind.Code, [], {}]830], async (editor, viewModel) => {831assert.strictEqual(editor.textModel.versionId, 0);832const firstAltVersion = '0_0,1;1,1';833assert.strictEqual(editor.textModel.alternativeVersionId, firstAltVersion);834editor.textModel.applyEdits([835{836index: 0,837editType: CellEditType.Metadata,838metadata: {839inputCollapsed: true840}841}842], true, undefined, () => undefined, undefined, true);843assert.strictEqual(editor.textModel.versionId, 1);844assert.notStrictEqual(editor.textModel.alternativeVersionId, firstAltVersion);845const secondAltVersion = '1_0,1;1,1';846assert.strictEqual(editor.textModel.alternativeVersionId, secondAltVersion);847848await viewModel.undo();849assert.strictEqual(editor.textModel.versionId, 2);850assert.strictEqual(editor.textModel.alternativeVersionId, firstAltVersion);851852await viewModel.redo();853assert.strictEqual(editor.textModel.versionId, 3);854assert.notStrictEqual(editor.textModel.alternativeVersionId, firstAltVersion);855assert.strictEqual(editor.textModel.alternativeVersionId, secondAltVersion);856857editor.textModel.applyEdits([858{859index: 1,860editType: CellEditType.Metadata,861metadata: {862inputCollapsed: true863}864}865], true, undefined, () => undefined, undefined, true);866assert.strictEqual(editor.textModel.versionId, 4);867assert.strictEqual(editor.textModel.alternativeVersionId, '4_0,1;1,1');868869await viewModel.undo();870assert.strictEqual(editor.textModel.versionId, 5);871assert.strictEqual(editor.textModel.alternativeVersionId, secondAltVersion);872873});874});875876test('metadata changes on newly added cells should combine their undo operations', async function () {877await withTestNotebook([878['var a = 1;', 'javascript', CellKind.Code, [], {}]879], async (editor, viewModel, ds) => {880const textModel = editor.textModel;881editor.textModel.applyEdits([882{883editType: CellEditType.Replace, index: 1, count: 0, cells: [884ds.add(new TestCell(textModel.viewType, 1, 'var e = 5;', 'javascript', CellKind.Code, [], languageService)),885ds.add(new TestCell(textModel.viewType, 2, 'var f = 6;', 'javascript', CellKind.Code, [], languageService))886]887},888], true, undefined, () => undefined, undefined, true);889890assert.strictEqual(textModel.cells.length, 3);891892editor.textModel.applyEdits([893{ editType: CellEditType.Metadata, index: 1, metadata: { id: '123' } },894], true, undefined, () => undefined, undefined, true);895896assert.strictEqual(textModel.cells[1].metadata.id, '123');897898await viewModel.undo();899900assert.strictEqual(textModel.cells.length, 1);901902await viewModel.redo();903904assert.strictEqual(textModel.cells.length, 3);905});906});907908test('changes with non-metadata edit should not combine their undo operations', async function () {909await withTestNotebook([910['var a = 1;', 'javascript', CellKind.Code, [], {}]911], async (editor, viewModel, ds) => {912const textModel = editor.textModel;913editor.textModel.applyEdits([914{915editType: CellEditType.Replace, index: 1, count: 0, cells: [916ds.add(new TestCell(textModel.viewType, 1, 'var e = 5;', 'javascript', CellKind.Code, [], languageService)),917ds.add(new TestCell(textModel.viewType, 2, 'var f = 6;', 'javascript', CellKind.Code, [], languageService))918]919},920], true, undefined, () => undefined, undefined, true);921922assert.strictEqual(textModel.cells.length, 3);923924editor.textModel.applyEdits([925{ editType: CellEditType.Metadata, index: 1, metadata: { id: '123' } },926{927editType: CellEditType.Output, handle: 0, append: true, outputs: [{928outputId: 'newOutput',929outputs: [{ mime: Mimes.text, data: valueBytesFromString('cba') }, { mime: 'application/foo', data: valueBytesFromString('cba') }]930}]931}932], true, undefined, () => undefined, undefined, true);933934assert.strictEqual(textModel.cells[1].metadata.id, '123');935936await viewModel.undo();937938assert.strictEqual(textModel.cells.length, 3);939940await viewModel.undo();941942assert.strictEqual(textModel.cells.length, 1);943});944});945946test('Destructive sorting in _doApplyEdits #121994', async function () {947await withTestNotebook([948['var a = 1;', 'javascript', CellKind.Code, [{ outputId: 'i42', outputs: [{ mime: 'm/ime', data: valueBytesFromString('test') }] }], {}]949], async (editor) => {950951const notebook = editor.textModel;952953assert.strictEqual(notebook.cells[0].outputs.length, 1);954assert.strictEqual(notebook.cells[0].outputs[0].outputs.length, 1);955assert.deepStrictEqual(notebook.cells[0].outputs[0].outputs[0].data, valueBytesFromString('test'));956957const edits: ICellEditOperation[] = [958{959editType: CellEditType.Output, handle: 0, outputs: []960},961{962editType: CellEditType.Output, handle: 0, append: true, outputs: [{963outputId: 'newOutput',964outputs: [{ mime: Mimes.text, data: valueBytesFromString('cba') }, { mime: 'application/foo', data: valueBytesFromString('cba') }]965}]966}967];968969editor.textModel.applyEdits(edits, true, undefined, () => undefined, undefined, true);970971assert.strictEqual(notebook.cells[0].outputs.length, 1);972assert.strictEqual(notebook.cells[0].outputs[0].outputs.length, 2);973});974});975976test('Destructive sorting in _doApplyEdits #121994. cell splice between output changes', async function () {977await withTestNotebook([978['var a = 1;', 'javascript', CellKind.Code, [{ outputId: 'i42', outputs: [{ mime: 'm/ime', data: valueBytesFromString('test') }] }], {}],979['var b = 2;', 'javascript', CellKind.Code, [{ outputId: 'i43', outputs: [{ mime: 'm/ime', data: valueBytesFromString('test') }] }], {}],980['var c = 3;', 'javascript', CellKind.Code, [{ outputId: 'i44', outputs: [{ mime: 'm/ime', data: valueBytesFromString('test') }] }], {}]981], async (editor) => {982const notebook = editor.textModel;983984const edits: ICellEditOperation[] = [985{986editType: CellEditType.Output, index: 0, outputs: []987},988{989editType: CellEditType.Replace, index: 1, count: 1, cells: []990},991{992editType: CellEditType.Output, index: 2, append: true, outputs: [{993outputId: 'newOutput',994outputs: [{ mime: Mimes.text, data: valueBytesFromString('cba') }, { mime: 'application/foo', data: valueBytesFromString('cba') }]995}]996}997];998999editor.textModel.applyEdits(edits, true, undefined, () => undefined, undefined, true);10001001assert.strictEqual(notebook.cells.length, 2);1002assert.strictEqual(notebook.cells[0].outputs.length, 0);1003assert.strictEqual(notebook.cells[1].outputs.length, 2);1004assert.strictEqual(notebook.cells[1].outputs[0].outputId, 'i44');1005assert.strictEqual(notebook.cells[1].outputs[1].outputId, 'newOutput');1006});1007});10081009test('Destructive sorting in _doApplyEdits #121994. cell splice between output changes 2', async function () {1010await withTestNotebook([1011['var a = 1;', 'javascript', CellKind.Code, [{ outputId: 'i42', outputs: [{ mime: 'm/ime', data: valueBytesFromString('test') }] }], {}],1012['var b = 2;', 'javascript', CellKind.Code, [{ outputId: 'i43', outputs: [{ mime: 'm/ime', data: valueBytesFromString('test') }] }], {}],1013['var c = 3;', 'javascript', CellKind.Code, [{ outputId: 'i44', outputs: [{ mime: 'm/ime', data: valueBytesFromString('test') }] }], {}]1014], async (editor) => {1015const notebook = editor.textModel;10161017const edits: ICellEditOperation[] = [1018{1019editType: CellEditType.Output, index: 1, append: true, outputs: [{1020outputId: 'newOutput',1021outputs: [{ mime: Mimes.text, data: valueBytesFromString('cba') }, { mime: 'application/foo', data: valueBytesFromString('cba') }]1022}]1023},1024{1025editType: CellEditType.Replace, index: 1, count: 1, cells: []1026},1027{1028editType: CellEditType.Output, index: 1, append: true, outputs: [{1029outputId: 'newOutput2',1030outputs: [{ mime: Mimes.text, data: valueBytesFromString('cba') }, { mime: 'application/foo', data: valueBytesFromString('cba') }]1031}]1032}1033];10341035editor.textModel.applyEdits(edits, true, undefined, () => undefined, undefined, true);10361037assert.strictEqual(notebook.cells.length, 2);1038assert.strictEqual(notebook.cells[0].outputs.length, 1);1039assert.strictEqual(notebook.cells[1].outputs.length, 1);1040assert.strictEqual(notebook.cells[1].outputs[0].outputId, 'i44');1041});1042});10431044test('Output edits splice', async function () {1045await withTestNotebook([1046['var a = 1;', 'javascript', CellKind.Code, [], {}]1047], (editor) => {1048const model = editor.textModel;10491050assert.strictEqual(model.cells.length, 1);1051assert.strictEqual(model.cells[0].outputs.length, 0);10521053const success1 = model.applyEdits(1054[{1055editType: CellEditType.Output, index: 0, outputs: [1056{ outputId: 'out1', outputs: [{ mime: 'application/x.notebook.stream', data: valueBytesFromString('1') }] },1057{ outputId: 'out2', outputs: [{ mime: 'application/x.notebook.stream', data: valueBytesFromString('2') }] },1058{ outputId: 'out3', outputs: [{ mime: 'application/x.notebook.stream', data: valueBytesFromString('3') }] },1059{ outputId: 'out4', outputs: [{ mime: 'application/x.notebook.stream', data: valueBytesFromString('4') }] }1060],1061append: false1062}], true, undefined, () => undefined, undefined, false1063);10641065assert.ok(success1);1066assert.strictEqual(model.cells[0].outputs.length, 4);10671068const success2 = model.applyEdits(1069[{1070editType: CellEditType.Output, index: 0, outputs: [1071{ outputId: 'out1', outputs: [{ mime: 'application/x.notebook.stream', data: valueBytesFromString('1') }] },1072{ outputId: 'out5', outputs: [{ mime: 'application/x.notebook.stream', data: valueBytesFromString('5') }] },1073{ outputId: 'out3', outputs: [{ mime: 'application/x.notebook.stream', data: valueBytesFromString('3') }] },1074{ outputId: 'out6', outputs: [{ mime: 'application/x.notebook.stream', data: valueBytesFromString('6') }] }1075],1076append: false1077}], true, undefined, () => undefined, undefined, false1078);10791080assert.ok(success2);1081assert.strictEqual(model.cells[0].outputs.length, 4);1082assert.strictEqual(model.cells[0].outputs[0].outputId, 'out1');1083assert.strictEqual(model.cells[0].outputs[1].outputId, 'out5');1084assert.strictEqual(model.cells[0].outputs[2].outputId, 'out3');1085assert.strictEqual(model.cells[0].outputs[3].outputId, 'out6');1086});1087});10881089test('computeEdits no insert', async function () {1090await withTestNotebook([1091['var a = 1;', 'javascript', CellKind.Code, [], {}]1092], (editor) => {1093const model = editor.textModel;1094const edits = NotebookTextModel.computeEdits(model, [1095{ source: 'var a = 1;', language: 'javascript', cellKind: CellKind.Code, mime: undefined, outputs: [], metadata: undefined }1096]);10971098assert.deepStrictEqual(edits, [1099{ editType: CellEditType.Metadata, index: 0, metadata: {} }1100]);1101});1102});11031104test('computeEdits cell content changed', async function () {1105await withTestNotebook([1106['var a = 1;', 'javascript', CellKind.Code, [], {}]1107], (editor) => {1108const model = editor.textModel;1109const cells = [1110{ source: 'var a = 2;', language: 'javascript', cellKind: CellKind.Code, mime: undefined, outputs: [], metadata: undefined }1111];1112const edits = NotebookTextModel.computeEdits(model, cells);11131114assert.deepStrictEqual(edits, [1115{ editType: CellEditType.Replace, index: 0, count: 1, cells },1116]);1117});1118});11191120test('computeEdits last cell content changed', async function () {1121await withTestNotebook([1122['var a = 1;', 'javascript', CellKind.Code, [], {}],1123['var b = 1;', 'javascript', CellKind.Code, [], {}]1124], (editor) => {1125const model = editor.textModel;1126const cells = [1127{ source: 'var a = 1;', language: 'javascript', cellKind: CellKind.Code, mime: undefined, outputs: [], metadata: undefined },1128{ source: 'var b = 2;', language: 'javascript', cellKind: CellKind.Code, mime: undefined, outputs: [], metadata: undefined }1129];1130const edits = NotebookTextModel.computeEdits(model, cells);11311132assert.deepStrictEqual(edits, [1133{ editType: CellEditType.Metadata, index: 0, metadata: {} },1134{ editType: CellEditType.Replace, index: 1, count: 1, cells: cells.slice(1) },1135]);1136});1137});1138test('computeEdits first cell content changed', async function () {1139await withTestNotebook([1140['var a = 1;', 'javascript', CellKind.Code, [], {}],1141['var b = 1;', 'javascript', CellKind.Code, [], {}]1142], (editor) => {1143const model = editor.textModel;1144const cells = [1145{ source: 'var a = 2;', language: 'javascript', cellKind: CellKind.Code, mime: undefined, outputs: [], metadata: undefined },1146{ source: 'var b = 1;', language: 'javascript', cellKind: CellKind.Code, mime: undefined, outputs: [], metadata: undefined }1147];1148const edits = NotebookTextModel.computeEdits(model, cells);11491150assert.deepStrictEqual(edits, [1151{ editType: CellEditType.Replace, index: 0, count: 1, cells: cells.slice(0, 1) },1152{ editType: CellEditType.Metadata, index: 1, metadata: {} },1153]);1154});1155});11561157test('computeEdits middle cell content changed', async function () {1158await withTestNotebook([1159['var a = 1;', 'javascript', CellKind.Code, [], {}],1160['var b = 1;', 'javascript', CellKind.Code, [], {}],1161['var c = 1;', 'javascript', CellKind.Code, [], {}],1162], (editor) => {1163const model = editor.textModel;1164const cells = [1165{ source: 'var a = 1;', language: 'javascript', cellKind: CellKind.Code, mime: undefined, outputs: [], metadata: undefined },1166{ source: 'var b = 2;', language: 'javascript', cellKind: CellKind.Code, mime: undefined, outputs: [], metadata: undefined },1167{ source: 'var c = 1;', language: 'javascript', cellKind: CellKind.Code, mime: undefined, outputs: [], metadata: undefined }1168];1169const edits = NotebookTextModel.computeEdits(model, cells);11701171assert.deepStrictEqual(edits, [1172{ editType: CellEditType.Metadata, index: 0, metadata: {} },1173{ editType: CellEditType.Replace, index: 1, count: 1, cells: cells.slice(1, 2) },1174{ editType: CellEditType.Metadata, index: 2, metadata: {} },1175]);1176});1177});11781179test('computeEdits cell metadata changed', async function () {1180await withTestNotebook([1181['var a = 1;', 'javascript', CellKind.Code, [], {}],1182['var b = 1;', 'javascript', CellKind.Code, [], {}]1183], (editor) => {1184const model = editor.textModel;1185const cells = [1186{ source: 'var a = 1;', language: 'javascript', cellKind: CellKind.Code, mime: undefined, outputs: [], metadata: { name: 'foo' } },1187{ source: 'var b = 1;', language: 'javascript', cellKind: CellKind.Code, mime: undefined, outputs: [], metadata: undefined }1188];1189const edits = NotebookTextModel.computeEdits(model, cells);11901191assert.deepStrictEqual(edits, [1192{ editType: CellEditType.Metadata, index: 0, metadata: { name: 'foo' } },1193{ editType: CellEditType.Metadata, index: 1, metadata: {} },1194]);1195});1196});11971198test('computeEdits cell language changed', async function () {1199await withTestNotebook([1200['var a = 1;', 'javascript', CellKind.Code, [], {}],1201['var b = 1;', 'javascript', CellKind.Code, [], {}]1202], (editor) => {1203const model = editor.textModel;1204const cells = [1205{ source: 'var a = 1;', language: 'typescript', cellKind: CellKind.Code, mime: undefined, outputs: [], metadata: undefined },1206{ source: 'var b = 1;', language: 'javascript', cellKind: CellKind.Code, mime: undefined, outputs: [], metadata: undefined }1207];1208const edits = NotebookTextModel.computeEdits(model, cells);12091210assert.deepStrictEqual(edits, [1211{ editType: CellEditType.Replace, index: 0, count: 1, cells: cells.slice(0, 1) },1212{ editType: CellEditType.Metadata, index: 1, metadata: {} },1213]);1214});1215});12161217test('computeEdits cell kind changed', async function () {1218await withTestNotebook([1219['var a = 1;', 'javascript', CellKind.Code, [], {}],1220['var b = 1;', 'javascript', CellKind.Code, [], {}]1221], (editor) => {1222const model = editor.textModel;1223const cells = [1224{ source: 'var a = 1;', language: 'javascript', cellKind: CellKind.Code, mime: undefined, outputs: [], metadata: undefined },1225{ source: 'var b = 1;', language: 'javascript', cellKind: CellKind.Markup, mime: undefined, outputs: [], metadata: undefined }1226];1227const edits = NotebookTextModel.computeEdits(model, cells);12281229assert.deepStrictEqual(edits, [1230{ editType: CellEditType.Metadata, index: 0, metadata: {} },1231{ editType: CellEditType.Replace, index: 1, count: 1, cells: cells.slice(1) },1232]);1233});1234});12351236test('computeEdits cell metadata & content changed', async function () {1237await withTestNotebook([1238['var a = 1;', 'javascript', CellKind.Code, [], {}],1239['var b = 1;', 'javascript', CellKind.Code, [], {}]1240], (editor) => {1241const model = editor.textModel;1242const cells = [1243{ source: 'var a = 1;', language: 'javascript', cellKind: CellKind.Code, mime: undefined, outputs: [], metadata: { name: 'foo' } },1244{ source: 'var b = 2;', language: 'javascript', cellKind: CellKind.Code, mime: undefined, outputs: [], metadata: { name: 'bar' } }1245];1246const edits = NotebookTextModel.computeEdits(model, cells);12471248assert.deepStrictEqual(edits, [1249{ editType: CellEditType.Metadata, index: 0, metadata: { name: 'foo' } },1250{ editType: CellEditType.Replace, index: 1, count: 1, cells: cells.slice(1) }1251]);1252});1253});12541255test('computeEdits cell content changed while executing', async function () {1256await withTestNotebook([1257['var a = 1;', 'javascript', CellKind.Code, [], {}],1258['var b = 1;', 'javascript', CellKind.Code, [], {}]1259], (editor) => {1260const model = editor.textModel;1261const cells = [1262{ source: 'var a = 1;', language: 'javascript', cellKind: CellKind.Code, mime: undefined, outputs: [], metadata: {} },1263{ source: 'var b = 2;', language: 'javascript', cellKind: CellKind.Code, mime: undefined, outputs: [], metadata: {} }1264];1265const edits = NotebookTextModel.computeEdits(model, cells, [model.cells[1].handle]);12661267assert.deepStrictEqual(edits, [1268{ editType: CellEditType.Metadata, index: 0, metadata: {} },1269{ editType: CellEditType.Replace, index: 1, count: 1, cells: cells.slice(1) }1270]);1271});1272});12731274test('computeEdits cell internal metadata changed', async function () {1275await withTestNotebook([1276['var a = 1;', 'javascript', CellKind.Code, [], {}],1277['var b = 1;', 'javascript', CellKind.Code, [], {}]1278], (editor) => {1279const model = editor.textModel;1280const cells = [1281{ source: 'var a = 1;', language: 'javascript', cellKind: CellKind.Code, mime: undefined, outputs: [], metadata: undefined, internalMetadata: { executionOrder: 1 } },1282{ source: 'var b = 1;', language: 'javascript', cellKind: CellKind.Code, mime: undefined, outputs: [], metadata: undefined }1283];1284const edits = NotebookTextModel.computeEdits(model, cells);12851286assert.deepStrictEqual(edits, [1287{ editType: CellEditType.Replace, index: 0, count: 1, cells: cells.slice(0, 1) },1288{ editType: CellEditType.Metadata, index: 1, metadata: {} },1289]);1290});1291});12921293test('computeEdits cell internal metadata changed while executing', async function () {1294await withTestNotebook([1295['var a = 1;', 'javascript', CellKind.Code, [], {}],1296['var b = 1;', 'javascript', CellKind.Code, [], {}]1297], (editor) => {1298const model = editor.textModel;1299const cells = [1300{ source: 'var a = 1;', language: 'javascript', cellKind: CellKind.Code, mime: undefined, outputs: [], metadata: {} },1301{ source: 'var b = 1;', language: 'javascript', cellKind: CellKind.Code, mime: undefined, outputs: [], metadata: {}, internalMetadata: { executionOrder: 1 } }1302];1303const edits = NotebookTextModel.computeEdits(model, cells, [model.cells[1].handle]);13041305assert.deepStrictEqual(edits, [1306{ editType: CellEditType.Metadata, index: 0, metadata: {} },1307{ editType: CellEditType.Metadata, index: 1, metadata: {} },1308]);1309});1310});13111312test('computeEdits cell insertion', async function () {1313await withTestNotebook([1314['var a = 1;', 'javascript', CellKind.Code, [], {}],1315['var b = 1;', 'javascript', CellKind.Code, [], {}]1316], (editor) => {1317const model = editor.textModel;1318const cells = [1319{ source: 'var a = 1;', language: 'javascript', cellKind: CellKind.Code, mime: undefined, outputs: [], metadata: undefined, },1320{ source: 'var c = 1;', language: 'javascript', cellKind: CellKind.Code, mime: undefined, outputs: [], metadata: undefined, },1321{ source: 'var b = 1;', language: 'javascript', cellKind: CellKind.Code, mime: undefined, outputs: [], metadata: { foo: 'bar' } }1322];1323const edits = NotebookTextModel.computeEdits(model, cells);13241325assert.deepStrictEqual(edits, [1326{ editType: CellEditType.Metadata, index: 0, metadata: {} },1327{ editType: CellEditType.Replace, index: 1, count: 0, cells: cells.slice(1, 2) },1328{ editType: CellEditType.Metadata, index: 1, metadata: { foo: 'bar' } },1329]);13301331model.applyEdits(edits, true, undefined, () => undefined, undefined, true);1332assert.equal(model.cells.length, 3);1333assert.equal(model.cells[1].getValue(), 'var c = 1;');1334assert.equal(model.cells[2].getValue(), 'var b = 1;');1335assert.deepStrictEqual(model.cells[2].metadata, { foo: 'bar' });1336});1337});13381339test('computeEdits output changed', async function () {1340await withTestNotebook([1341['var a = 1;', 'javascript', CellKind.Code, [], {}],1342['var b = 1;', 'javascript', CellKind.Code, [], {}]1343], (editor) => {1344const model = editor.textModel;1345const cells = [1346{1347source: 'var a = 1;', language: 'javascript', cellKind: CellKind.Code, mime: undefined, outputs: [{1348outputId: 'someId',1349outputs: [{ mime: Mimes.markdown, data: valueBytesFromString('_World_') }]1350}], metadata: undefined,1351},1352{ source: 'var b = 1;', language: 'javascript', cellKind: CellKind.Code, mime: undefined, outputs: [], metadata: { foo: 'bar' } }1353];1354const edits = NotebookTextModel.computeEdits(model, cells);13551356assert.deepStrictEqual(edits, [1357{ editType: CellEditType.Metadata, index: 0, metadata: {} },1358{1359editType: CellEditType.Output, index: 0, outputs: [{1360outputId: 'someId',1361outputs: [{ mime: Mimes.markdown, data: valueBytesFromString('_World_') }]1362}], append: false1363},1364{ editType: CellEditType.Metadata, index: 1, metadata: { foo: 'bar' } },1365]);13661367model.applyEdits(edits, true, undefined, () => undefined, undefined, true);1368assert.equal(model.cells.length, 2);1369assert.strictEqual(model.cells[0].outputs.length, 1);1370assert.equal(model.cells[0].outputs[0].outputId, 'someId');1371assert.equal(model.cells[0].outputs[0].outputs[0].data.toString(), '_World_');1372});1373});13741375test('computeEdits output items changed', async function () {1376await withTestNotebook([1377['var a = 1;', 'javascript', CellKind.Code, [{1378outputId: 'someId',1379outputs: [{ mime: Mimes.markdown, data: valueBytesFromString('_Hello_') }]1380}], {}],1381['var b = 1;', 'javascript', CellKind.Code, [], {}]1382], (editor) => {1383const model = editor.textModel;1384const cells = [1385{1386source: 'var a = 1;', language: 'javascript', cellKind: CellKind.Code, mime: undefined, outputs: [{1387outputId: 'someId',1388outputs: [{ mime: Mimes.markdown, data: valueBytesFromString('_World_') }]1389}], metadata: undefined,1390},1391{ source: 'var b = 1;', language: 'javascript', cellKind: CellKind.Code, mime: undefined, outputs: [], metadata: { foo: 'bar' } }1392];1393const edits = NotebookTextModel.computeEdits(model, cells);13941395assert.deepStrictEqual(edits, [1396{ editType: CellEditType.Metadata, index: 0, metadata: {} },1397{ editType: CellEditType.OutputItems, outputId: 'someId', items: [{ mime: Mimes.markdown, data: valueBytesFromString('_World_') }], append: false },1398{ editType: CellEditType.Metadata, index: 1, metadata: { foo: 'bar' } },1399]);14001401model.applyEdits(edits, true, undefined, () => undefined, undefined, true);1402assert.equal(model.cells.length, 2);1403assert.strictEqual(model.cells[0].outputs.length, 1);1404assert.equal(model.cells[0].outputs[0].outputId, 'someId');1405assert.equal(model.cells[0].outputs[0].outputs[0].data.toString(), '_World_');1406});1407});1408test('Append multiple text/plain output items', async function () {1409await withTestNotebook([1410['var a = 1;', 'javascript', CellKind.Code, [{1411outputId: '1',1412outputs: [{ mime: 'text/plain', data: valueBytesFromString('foo') }]1413}], {}]1414], (editor) => {1415const model = editor.textModel;1416const edits: ICellEditOperation[] = [1417{1418editType: CellEditType.OutputItems,1419outputId: '1',1420append: true,1421items: [{ mime: 'text/plain', data: VSBuffer.fromString('bar') }, { mime: 'text/plain', data: VSBuffer.fromString('baz') }]1422}1423];1424model.applyEdits(edits, true, undefined, () => undefined, undefined, true);1425assert.equal(model.cells.length, 1);1426assert.equal(model.cells[0].outputs.length, 1);1427assert.equal(model.cells[0].outputs[0].outputs.length, 3);1428assert.equal(model.cells[0].outputs[0].outputs[0].mime, 'text/plain');1429assert.equal(model.cells[0].outputs[0].outputs[0].data.toString(), 'foo');1430assert.equal(model.cells[0].outputs[0].outputs[1].mime, 'text/plain');1431assert.equal(model.cells[0].outputs[0].outputs[1].data.toString(), 'bar');1432assert.equal(model.cells[0].outputs[0].outputs[2].mime, 'text/plain');1433assert.equal(model.cells[0].outputs[0].outputs[2].data.toString(), 'baz');1434});1435});1436test('Append multiple stdout stream output items to an output with another mime', async function () {1437await withTestNotebook([1438['var a = 1;', 'javascript', CellKind.Code, [{1439outputId: '1',1440outputs: [{ mime: 'text/plain', data: valueBytesFromString('foo') }]1441}], {}]1442], (editor) => {1443const model = editor.textModel;1444const edits: ICellEditOperation[] = [1445{1446editType: CellEditType.OutputItems,1447outputId: '1',1448append: true,1449items: [{ mime: 'application/vnd.code.notebook.stdout', data: VSBuffer.fromString('bar') }, { mime: 'application/vnd.code.notebook.stdout', data: VSBuffer.fromString('baz') }]1450}1451];1452model.applyEdits(edits, true, undefined, () => undefined, undefined, true);1453assert.equal(model.cells.length, 1);1454assert.equal(model.cells[0].outputs.length, 1);1455assert.equal(model.cells[0].outputs[0].outputs.length, 3);1456assert.equal(model.cells[0].outputs[0].outputs[0].mime, 'text/plain');1457assert.equal(model.cells[0].outputs[0].outputs[0].data.toString(), 'foo');1458assert.equal(model.cells[0].outputs[0].outputs[1].mime, 'application/vnd.code.notebook.stdout');1459assert.equal(model.cells[0].outputs[0].outputs[1].data.toString(), 'bar');1460assert.equal(model.cells[0].outputs[0].outputs[2].mime, 'application/vnd.code.notebook.stdout');1461assert.equal(model.cells[0].outputs[0].outputs[2].data.toString(), 'baz');1462});1463});1464test('Compress multiple stdout stream output items', async function () {1465await withTestNotebook([1466['var a = 1;', 'javascript', CellKind.Code, [{1467outputId: '1',1468outputs: [{ mime: 'application/vnd.code.notebook.stdout', data: valueBytesFromString('foo') }]1469}], {}]1470], (editor) => {1471const model = editor.textModel;1472const edits: ICellEditOperation[] = [1473{1474editType: CellEditType.OutputItems,1475outputId: '1',1476append: true,1477items: [{ mime: 'application/vnd.code.notebook.stdout', data: VSBuffer.fromString('bar') }, { mime: 'application/vnd.code.notebook.stdout', data: VSBuffer.fromString('baz') }]1478}1479];1480model.applyEdits(edits, true, undefined, () => undefined, undefined, true);1481assert.equal(model.cells.length, 1);1482assert.equal(model.cells[0].outputs.length, 1);1483assert.equal(model.cells[0].outputs[0].outputs.length, 1);1484assert.equal(model.cells[0].outputs[0].outputs[0].mime, 'application/vnd.code.notebook.stdout');1485assert.equal(model.cells[0].outputs[0].outputs[0].data.toString(), 'foobarbaz');1486});14871488});1489test('Compress multiple stderr stream output items', async function () {1490await withTestNotebook([1491['var a = 1;', 'javascript', CellKind.Code, [{1492outputId: '1',1493outputs: [{ mime: 'application/vnd.code.notebook.stderr', data: valueBytesFromString('foo') }]1494}], {}]1495], (editor) => {1496const model = editor.textModel;1497const edits: ICellEditOperation[] = [1498{1499editType: CellEditType.OutputItems,1500outputId: '1',1501append: true,1502items: [{ mime: 'application/vnd.code.notebook.stderr', data: VSBuffer.fromString('bar') }, { mime: 'application/vnd.code.notebook.stderr', data: VSBuffer.fromString('baz') }]1503}1504];1505model.applyEdits(edits, true, undefined, () => undefined, undefined, true);1506assert.equal(model.cells.length, 1);1507assert.equal(model.cells[0].outputs.length, 1);1508assert.equal(model.cells[0].outputs[0].outputs.length, 1);1509assert.equal(model.cells[0].outputs[0].outputs[0].mime, 'application/vnd.code.notebook.stderr');1510assert.equal(model.cells[0].outputs[0].outputs[0].data.toString(), 'foobarbaz');1511});15121513});15141515test('findNextMatch', async function () {1516await withTestNotebook(1517[1518['var a = 1;', 'javascript', CellKind.Code, [], {}],1519['var b = 2;', 'javascript', CellKind.Code, [], {}],1520['var c = 3;', 'javascript', CellKind.Code, [], {}],1521['var d = 4;', 'javascript', CellKind.Code, [], {}]1522],1523(editor, viewModel) => {1524const notebookModel = viewModel.notebookDocument;15251526// Test case 1: Find 'var' starting from the first cell1527let findMatch = notebookModel.findNextMatch('var', { cellIndex: 0, position: new Position(1, 1) }, false, false, null);1528assert.ok(findMatch);1529assert.strictEqual(findMatch!.match.range.startLineNumber, 1);1530assert.strictEqual(findMatch!.match.range.startColumn, 1);15311532// Test case 2: Find 'b' starting from the second cell1533findMatch = notebookModel.findNextMatch('b', { cellIndex: 1, position: new Position(1, 1) }, false, false, null);1534assert.ok(findMatch);1535assert.strictEqual(findMatch!.match.range.startLineNumber, 1);1536assert.strictEqual(findMatch!.match.range.startColumn, 5);15371538// Test case 3: Find 'c' starting from the third cell1539findMatch = notebookModel.findNextMatch('c', { cellIndex: 2, position: new Position(1, 1) }, false, false, null);1540assert.ok(findMatch);1541assert.strictEqual(findMatch!.match.range.startLineNumber, 1);1542assert.strictEqual(findMatch!.match.range.startColumn, 5);15431544// Test case 4: Find 'd' starting from the fourth cell1545findMatch = notebookModel.findNextMatch('d', { cellIndex: 3, position: new Position(1, 1) }, false, false, null);1546assert.ok(findMatch);1547assert.strictEqual(findMatch!.match.range.startLineNumber, 1);1548assert.strictEqual(findMatch!.match.range.startColumn, 5);15491550// Test case 5: No match found1551findMatch = notebookModel.findNextMatch('e', { cellIndex: 0, position: new Position(1, 1) }, false, false, null);1552assert.strictEqual(findMatch, null);1553}1554);1555});15561557test('findNextMatch 2', async function () {1558await withTestNotebook(1559[1560['var a = 1; var a = 2;', 'javascript', CellKind.Code, [], {}],1561['var b = 2;', 'javascript', CellKind.Code, [], {}],1562['var c = 3;', 'javascript', CellKind.Code, [], {}],1563['var d = 4;', 'javascript', CellKind.Code, [], {}]1564],1565(editor, viewModel) => {1566const notebookModel = viewModel.notebookDocument;15671568// Test case 1: Find 'var' starting from the first cell1569let findMatch = notebookModel.findNextMatch('var', { cellIndex: 0, position: new Position(1, 1) }, false, false, null);1570assert.ok(findMatch);1571assert.strictEqual(findMatch!.match.range.startLineNumber, 1);1572assert.strictEqual(findMatch!.match.range.startColumn, 1);15731574// Test case 2: Find 'b' starting from the second cell1575findMatch = notebookModel.findNextMatch('b', { cellIndex: 1, position: new Position(1, 1) }, false, false, null);1576assert.ok(findMatch);1577assert.strictEqual(findMatch!.match.range.startLineNumber, 1);1578assert.strictEqual(findMatch!.match.range.startColumn, 5);15791580// Test case 3: Find 'c' starting from the third cell1581findMatch = notebookModel.findNextMatch('c', { cellIndex: 2, position: new Position(1, 1) }, false, false, null);1582assert.ok(findMatch);1583assert.strictEqual(findMatch!.match.range.startLineNumber, 1);1584assert.strictEqual(findMatch!.match.range.startColumn, 5);15851586// Test case 4: Find 'd' starting from the fourth cell1587findMatch = notebookModel.findNextMatch('d', { cellIndex: 3, position: new Position(1, 1) }, false, false, null);1588assert.ok(findMatch);1589assert.strictEqual(findMatch!.match.range.startLineNumber, 1);1590assert.strictEqual(findMatch!.match.range.startColumn, 5);15911592// Test case 5: No match found1593findMatch = notebookModel.findNextMatch('e', { cellIndex: 0, position: new Position(1, 1) }, false, false, null);1594assert.strictEqual(findMatch, null);15951596// Test case 6: Same keywords in the same cell1597findMatch = notebookModel.findNextMatch('var', { cellIndex: 0, position: new Position(1, 1) }, false, false, null);1598assert.ok(findMatch);1599assert.strictEqual(findMatch!.match.range.startLineNumber, 1);1600assert.strictEqual(findMatch!.match.range.startColumn, 1);16011602findMatch = notebookModel.findNextMatch('var', { cellIndex: 0, position: new Position(1, 5) }, false, false, null);1603assert.ok(findMatch);1604assert.strictEqual(findMatch!.match.range.startLineNumber, 1);1605assert.strictEqual(findMatch!.match.range.startColumn, 12);16061607// Test case 7: Search from the middle of a cell with keyword before and after1608findMatch = notebookModel.findNextMatch('a', { cellIndex: 0, position: new Position(1, 10) }, false, false, null);1609assert.ok(findMatch);1610assert.strictEqual(findMatch!.match.range.startLineNumber, 1);1611assert.strictEqual(findMatch!.match.range.startColumn, 13);16121613// Test case 8: Search from a cell and next match is in another cell below1614findMatch = notebookModel.findNextMatch('var', { cellIndex: 0, position: new Position(1, 20) }, false, false, null);1615assert.ok(findMatch);1616assert.strictEqual(findMatch!.match.range.startLineNumber, 1);1617assert.strictEqual(findMatch!.match.range.startColumn, 1);1618// assert.strictEqual(match!.cellIndex, 1);1619}1620);1621});1622});162316241625