monaco-editor
Version:
A browser based code editor
108 lines (107 loc) • 5.21 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Range } from '../../../core/range.js';
import { lengthAdd, lengthDiffNonNegative, lengthLessThanEqual, lengthOfString, lengthToObj, positionToLength, toLength } from './length.js';
export class TextEditInfo {
static fromModelContentChanges(changes) {
// Must be sorted in ascending order
const edits = changes.map(c => {
const range = Range.lift(c.range);
return new TextEditInfo(positionToLength(range.getStartPosition()), positionToLength(range.getEndPosition()), lengthOfString(c.text));
}).reverse();
return edits;
}
constructor(startOffset, endOffset, newLength) {
this.startOffset = startOffset;
this.endOffset = endOffset;
this.newLength = newLength;
}
toString() {
return `[${lengthToObj(this.startOffset)}...${lengthToObj(this.endOffset)}) -> ${lengthToObj(this.newLength)}`;
}
}
export class BeforeEditPositionMapper {
/**
* @param edits Must be sorted by offset in ascending order.
*/
constructor(edits) {
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.
* Returns null if there is no edit anymore.
*/
getDistanceToNextChange(offset) {
this.adjustNextEdit(offset);
const nextEdit = this.edits[this.nextEditIdx];
const nextChangeOffset = nextEdit ? this.translateOldToCur(nextEdit.offsetObj) : null;
if (nextChangeOffset === null) {
return null;
}
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 {
static from(edit) {
return new TextEditInfoCache(edit.startOffset, edit.endOffset, edit.newLength);
}
constructor(startOffset, endOffset, textLength) {
this.endOffsetBeforeObj = lengthToObj(endOffset);
this.endOffsetAfterObj = lengthToObj(lengthAdd(startOffset, textLength));
this.offsetObj = lengthToObj(startOffset);
}
}