Path: blob/main/src/vs/editor/test/common/model/editableTextModelTestUtils.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 { ISingleEditOperation } from '../../../common/core/editOperation.js';7import { Position } from '../../../common/core/position.js';8import { EndOfLinePreference, EndOfLineSequence } from '../../../common/model.js';9import { MirrorTextModel } from '../../../common/model/mirrorTextModel.js';10import { TextModel } from '../../../common/model/textModel.js';11import { IModelContentChangedEvent } from '../../../common/textModelEvents.js';12import { createTextModel } from '../testTextModel.js';1314export function testApplyEditsWithSyncedModels(original: string[], edits: ISingleEditOperation[], expected: string[], inputEditsAreInvalid: boolean = false): void {15const originalStr = original.join('\n');16const expectedStr = expected.join('\n');1718assertSyncedModels(originalStr, (model, assertMirrorModels) => {19// Apply edits & collect inverse edits20const inverseEdits = model.applyEdits(edits, true);2122// Assert edits produced expected result23assert.deepStrictEqual(model.getValue(EndOfLinePreference.LF), expectedStr);2425assertMirrorModels();2627// Apply the inverse edits28const inverseInverseEdits = model.applyEdits(inverseEdits, true);2930// Assert the inverse edits brought back model to original state31assert.deepStrictEqual(model.getValue(EndOfLinePreference.LF), originalStr);3233if (!inputEditsAreInvalid) {34const simplifyEdit = (edit: ISingleEditOperation) => {35return {36range: edit.range,37text: edit.text,38forceMoveMarkers: edit.forceMoveMarkers || false39};40};41// Assert the inverse of the inverse edits are the original edits42assert.deepStrictEqual(inverseInverseEdits.map(simplifyEdit), edits.map(simplifyEdit));43}4445assertMirrorModels();46});47}4849const enum AssertDocumentLineMappingDirection {50OffsetToPosition,51PositionToOffset52}5354function assertOneDirectionLineMapping(model: TextModel, direction: AssertDocumentLineMappingDirection, msg: string): void {55const allText = model.getValue();5657let line = 1, column = 1, previousIsCarriageReturn = false;58for (let offset = 0; offset <= allText.length; offset++) {59// The position coordinate system cannot express the position between \r and \n60const position: Position = new Position(line, column + (previousIsCarriageReturn ? -1 : 0));6162if (direction === AssertDocumentLineMappingDirection.OffsetToPosition) {63const actualPosition = model.getPositionAt(offset);64assert.strictEqual(actualPosition.toString(), position.toString(), msg + ' - getPositionAt mismatch for offset ' + offset);65} else {66// The position coordinate system cannot express the position between \r and \n67const expectedOffset: number = offset + (previousIsCarriageReturn ? -1 : 0);68const actualOffset = model.getOffsetAt(position);69assert.strictEqual(actualOffset, expectedOffset, msg + ' - getOffsetAt mismatch for position ' + position.toString());70}7172if (allText.charAt(offset) === '\n') {73line++;74column = 1;75} else {76column++;77}7879previousIsCarriageReturn = (allText.charAt(offset) === '\r');80}81}8283function assertLineMapping(model: TextModel, msg: string): void {84assertOneDirectionLineMapping(model, AssertDocumentLineMappingDirection.PositionToOffset, msg);85assertOneDirectionLineMapping(model, AssertDocumentLineMappingDirection.OffsetToPosition, msg);86}878889export function assertSyncedModels(text: string, callback: (model: TextModel, assertMirrorModels: () => void) => void, setup: ((model: TextModel) => void) | null = null): void {90const model = createTextModel(text);91model.setEOL(EndOfLineSequence.LF);92assertLineMapping(model, 'model');9394if (setup) {95setup(model);96assertLineMapping(model, 'model');97}9899const mirrorModel2 = new MirrorTextModel(null!, model.getLinesContent(), model.getEOL(), model.getVersionId());100let mirrorModel2PrevVersionId = model.getVersionId();101102const disposable = model.onDidChangeContent((e: IModelContentChangedEvent) => {103const versionId = e.versionId;104if (versionId < mirrorModel2PrevVersionId) {105console.warn('Model version id did not advance between edits (2)');106}107mirrorModel2PrevVersionId = versionId;108mirrorModel2.onEvents(e);109});110111const assertMirrorModels = () => {112assertLineMapping(model, 'model');113assert.strictEqual(mirrorModel2.getText(), model.getValue(), 'mirror model 2 text OK');114assert.strictEqual(mirrorModel2.version, model.getVersionId(), 'mirror model 2 version OK');115};116117callback(model, assertMirrorModels);118119disposable.dispose();120model.dispose();121mirrorModel2.dispose();122}123124125