/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { lengthAdd, lengthDiffNonNegative, lengthLessThanEqual, lengthToObj, toLength } from './length.js'; export class TextEditInfo { constructor(startOffset, endOffset, newLength) { this.startOffset = startOffset; this.endOffset = endOffset; this.newLength = newLength; } } export class BeforeEditPositionMapper { /** * @param edits Must be sorted by offset in ascending order. */ constructor(edits, documentLength) { this.documentLength = documentLength; this.nextEditIdx = 0; this.deltaOldToNewLineCount = 0; this.deltaOldToNewColumnCount = 0; this.deltaLineIdxInOld = -1; this.edits = edits.map(edit => TextEditInfoCache.from(edit)); } /** * @param offset Must be equal to or greater than the last offset this method has been called with. */ getOffsetBeforeChange(offset) { this.adjustNextEdit(offset); return this.translateCurToOld(offset); } /** * @param offset Must be equal to or greater than the last offset this method has been called with. */ getDistanceToNextChange(offset) { this.adjustNextEdit(offset); const nextEdit = this.edits[this.nextEditIdx]; const nextChangeOffset = nextEdit ? this.translateOldToCur(nextEdit.offsetObj) : this.documentLength; return lengthDiffNonNegative(offset, nextChangeOffset); } translateOldToCur(oldOffsetObj) { if (oldOffsetObj.lineCount === this.deltaLineIdxInOld) { return toLength(oldOffsetObj.lineCount + this.deltaOldToNewLineCount, oldOffsetObj.columnCount + this.deltaOldToNewColumnCount); } else { return toLength(oldOffsetObj.lineCount + this.deltaOldToNewLineCount, oldOffsetObj.columnCount); } } translateCurToOld(newOffset) { const offsetObj = lengthToObj(newOffset); if (offsetObj.lineCount - this.deltaOldToNewLineCount === this.deltaLineIdxInOld) { return toLength(offsetObj.lineCount - this.deltaOldToNewLineCount, offsetObj.columnCount - this.deltaOldToNewColumnCount); } else { return toLength(offsetObj.lineCount - this.deltaOldToNewLineCount, offsetObj.columnCount); } } adjustNextEdit(offset) { while (this.nextEditIdx < this.edits.length) { const nextEdit = this.edits[this.nextEditIdx]; // After applying the edit, what is its end offset (considering all previous edits)? const nextEditEndOffsetInCur = this.translateOldToCur(nextEdit.endOffsetAfterObj); if (lengthLessThanEqual(nextEditEndOffsetInCur, offset)) { // We are after the edit, skip it this.nextEditIdx++; const nextEditEndOffsetInCurObj = lengthToObj(nextEditEndOffsetInCur); // Before applying the edit, what is its end offset (considering all previous edits)? const nextEditEndOffsetBeforeInCurObj = lengthToObj(this.translateOldToCur(nextEdit.endOffsetBeforeObj)); const lineDelta = nextEditEndOffsetInCurObj.lineCount - nextEditEndOffsetBeforeInCurObj.lineCount; this.deltaOldToNewLineCount += lineDelta; const previousColumnDelta = this.deltaLineIdxInOld === nextEdit.endOffsetBeforeObj.lineCount ? this.deltaOldToNewColumnCount : 0; const columnDelta = nextEditEndOffsetInCurObj.columnCount - nextEditEndOffsetBeforeInCurObj.columnCount; this.deltaOldToNewColumnCount = previousColumnDelta + columnDelta; this.deltaLineIdxInOld = nextEdit.endOffsetBeforeObj.lineCount; } else { // We are in or before the edit. break; } } } } class TextEditInfoCache { constructor(startOffset, endOffset, textLength) { this.endOffsetBeforeObj = lengthToObj(endOffset); this.endOffsetAfterObj = lengthToObj(lengthAdd(startOffset, textLength)); this.offsetObj = lengthToObj(startOffset); } static from(edit) { return new TextEditInfoCache(edit.startOffset, edit.endOffset, edit.newLength); } }