Path: blob/main/src/vs/editor/test/common/model/modelInjectedText.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 { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';7import { EditOperation } from '../../../common/core/editOperation.js';8import { Range } from '../../../common/core/range.js';9import { InternalModelContentChangeEvent, LineInjectedText, ModelRawChange, RawContentChangedType } from '../../../common/textModelEvents.js';10import { createTextModel } from '../testTextModel.js';1112suite('Editor Model - Injected Text Events', () => {13const store = ensureNoDisposablesAreLeakedInTestSuite();1415test('Basic', () => {16const thisModel = store.add(createTextModel('First Line\nSecond Line'));1718const recordedChanges = new Array<unknown>();1920store.add(thisModel.onDidChangeContentOrInjectedText((e) => {21const changes = (e instanceof InternalModelContentChangeEvent ? e.rawContentChangedEvent.changes : e.changes);22for (const change of changes) {23recordedChanges.push(mapChange(change));24}25}));2627// Initial decoration28let decorations = thisModel.deltaDecorations([], [{29options: {30after: { content: 'injected1' },31description: 'test1',32showIfCollapsed: true33},34range: new Range(1, 1, 1, 1),35}]);36assert.deepStrictEqual(recordedChanges.splice(0), [37{38kind: 'lineChanged',39line: '[injected1]First Line',40lineNumber: 1,41}42]);4344// Decoration change45decorations = thisModel.deltaDecorations(decorations, [{46options: {47after: { content: 'injected1' },48description: 'test1',49showIfCollapsed: true50},51range: new Range(2, 1, 2, 1),52}, {53options: {54after: { content: 'injected2' },55description: 'test2',56showIfCollapsed: true57},58range: new Range(2, 2, 2, 2),59}]);60assert.deepStrictEqual(recordedChanges.splice(0), [61{62kind: 'lineChanged',63line: 'First Line',64lineNumber: 1,65},66{67kind: 'lineChanged',68line: '[injected1]S[injected2]econd Line',69lineNumber: 2,70}71]);7273// Simple Insert74thisModel.applyEdits([EditOperation.replace(new Range(2, 2, 2, 2), 'Hello')]);75assert.deepStrictEqual(recordedChanges.splice(0), [76{77kind: 'lineChanged',78line: '[injected1]SHello[injected2]econd Line',79lineNumber: 2,80}81]);8283// Multi-Line Insert84thisModel.pushEditOperations(null, [EditOperation.replace(new Range(2, 2, 2, 2), '\n\n\n')], null);85assert.deepStrictEqual(thisModel.getAllDecorations(undefined).map(d => ({ description: d.options.description, range: d.range.toString() })), [{86'description': 'test1',87'range': '[2,1 -> 2,1]'88},89{90'description': 'test2',91'range': '[2,2 -> 5,6]'92}]);93assert.deepStrictEqual(recordedChanges.splice(0), [94{95kind: 'lineChanged',96line: '[injected1]S',97lineNumber: 2,98},99{100fromLineNumber: 3,101kind: 'linesInserted',102lines: [103'',104'',105'Hello[injected2]econd Line',106]107}108]);109110111// Multi-Line Replace112thisModel.pushEditOperations(null, [EditOperation.replace(new Range(3, 1, 5, 1), '\n\n\n\n\n\n\n\n\n\n\n\n\n')], null);113assert.deepStrictEqual(recordedChanges.splice(0), [114{115'kind': 'lineChanged',116'line': '',117'lineNumber': 5,118},119{120'kind': 'lineChanged',121'line': '',122'lineNumber': 4,123},124{125'kind': 'lineChanged',126'line': '',127'lineNumber': 3,128},129{130'fromLineNumber': 6,131'kind': 'linesInserted',132'lines': [133'',134'',135'',136'',137'',138'',139'',140'',141'',142'',143'Hello[injected2]econd Line',144]145}146]);147148// Multi-Line Replace undo149assert.strictEqual(thisModel.undo(), undefined);150assert.deepStrictEqual(recordedChanges.splice(0), [151{152kind: 'lineChanged',153line: '[injected1]SHello[injected2]econd Line',154lineNumber: 2,155},156{157kind: 'linesDeleted',158}159]);160});161});162163function mapChange(change: ModelRawChange): unknown {164if (change.changeType === RawContentChangedType.LineChanged) {165(change.injectedText || []).every(e => {166assert.deepStrictEqual(e.lineNumber, change.lineNumber);167});168169return {170kind: 'lineChanged',171line: getDetail(change.detail, change.injectedText),172lineNumber: change.lineNumber,173};174} else if (change.changeType === RawContentChangedType.LinesInserted) {175return {176kind: 'linesInserted',177lines: change.detail.map((e, idx) => getDetail(e, change.injectedTexts[idx])),178fromLineNumber: change.fromLineNumber179};180} else if (change.changeType === RawContentChangedType.LinesDeleted) {181return {182kind: 'linesDeleted',183};184} else if (change.changeType === RawContentChangedType.EOLChanged) {185return {186kind: 'eolChanged'187};188} else if (change.changeType === RawContentChangedType.Flush) {189return {190kind: 'flush'191};192}193return { kind: 'unknown' };194}195196function getDetail(line: string, injectedTexts: LineInjectedText[] | null): string {197return LineInjectedText.applyInjectedText(line, (injectedTexts || []).map(t => t.withText(`[${t.options.content}]`)));198}199200201