in src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts [225:396]
public applyEdits(rawOperations: ValidAnnotatedEditOperation[], recordTrimAutoWhitespace: boolean, computeUndoEdits: boolean): ApplyEditsResult {
let mightContainRTL = this._mightContainRTL;
let mightContainUnusualLineTerminators = this._mightContainUnusualLineTerminators;
let mightContainNonBasicASCII = this._mightContainNonBasicASCII;
let canReduceOperations = true;
let operations: IValidatedEditOperation[] = [];
for (let i = 0; i < rawOperations.length; i++) {
let op = rawOperations[i];
if (canReduceOperations && op._isTracked) {
canReduceOperations = false;
}
let validatedRange = op.range;
if (op.text) {
let textMightContainNonBasicASCII = true;
if (!mightContainNonBasicASCII) {
textMightContainNonBasicASCII = !strings.isBasicASCII(op.text);
mightContainNonBasicASCII = textMightContainNonBasicASCII;
}
if (!mightContainRTL && textMightContainNonBasicASCII) {
// check if the new inserted text contains RTL
mightContainRTL = strings.containsRTL(op.text);
}
if (!mightContainUnusualLineTerminators && textMightContainNonBasicASCII) {
// check if the new inserted text contains unusual line terminators
mightContainUnusualLineTerminators = strings.containsUnusualLineTerminators(op.text);
}
}
let validText = '';
let eolCount = 0;
let firstLineLength = 0;
let lastLineLength = 0;
if (op.text) {
let strEOL: StringEOL;
[eolCount, firstLineLength, lastLineLength, strEOL] = countEOL(op.text);
const bufferEOL = this.getEOL();
const expectedStrEOL = (bufferEOL === '\r\n' ? StringEOL.CRLF : StringEOL.LF);
if (strEOL === StringEOL.Unknown || strEOL === expectedStrEOL) {
validText = op.text;
} else {
validText = op.text.replace(/\r\n|\r|\n/g, bufferEOL);
}
}
operations[i] = {
sortIndex: i,
identifier: op.identifier || null,
range: validatedRange,
rangeOffset: this.getOffsetAt(validatedRange.startLineNumber, validatedRange.startColumn),
rangeLength: this.getValueLengthInRange(validatedRange),
text: validText,
eolCount: eolCount,
firstLineLength: firstLineLength,
lastLineLength: lastLineLength,
forceMoveMarkers: Boolean(op.forceMoveMarkers),
isAutoWhitespaceEdit: op.isAutoWhitespaceEdit || false
};
}
// Sort operations ascending
operations.sort(PieceTreeTextBuffer._sortOpsAscending);
let hasTouchingRanges = false;
for (let i = 0, count = operations.length - 1; i < count; i++) {
let rangeEnd = operations[i].range.getEndPosition();
let nextRangeStart = operations[i + 1].range.getStartPosition();
if (nextRangeStart.isBeforeOrEqual(rangeEnd)) {
if (nextRangeStart.isBefore(rangeEnd)) {
// overlapping ranges
throw new Error('Overlapping ranges are not allowed!');
}
hasTouchingRanges = true;
}
}
if (canReduceOperations) {
operations = this._reduceOperations(operations);
}
// Delta encode operations
let reverseRanges = (computeUndoEdits || recordTrimAutoWhitespace ? PieceTreeTextBuffer._getInverseEditRanges(operations) : []);
let newTrimAutoWhitespaceCandidates: { lineNumber: number, oldContent: string }[] = [];
if (recordTrimAutoWhitespace) {
for (let i = 0; i < operations.length; i++) {
let op = operations[i];
let reverseRange = reverseRanges[i];
if (op.isAutoWhitespaceEdit && op.range.isEmpty()) {
// Record already the future line numbers that might be auto whitespace removal candidates on next edit
for (let lineNumber = reverseRange.startLineNumber; lineNumber <= reverseRange.endLineNumber; lineNumber++) {
let currentLineContent = '';
if (lineNumber === reverseRange.startLineNumber) {
currentLineContent = this.getLineContent(op.range.startLineNumber);
if (strings.firstNonWhitespaceIndex(currentLineContent) !== -1) {
continue;
}
}
newTrimAutoWhitespaceCandidates.push({ lineNumber: lineNumber, oldContent: currentLineContent });
}
}
}
}
let reverseOperations: IReverseSingleEditOperation[] | null = null;
if (computeUndoEdits) {
let reverseRangeDeltaOffset = 0;
reverseOperations = [];
for (let i = 0; i < operations.length; i++) {
const op = operations[i];
const reverseRange = reverseRanges[i];
const bufferText = this.getValueInRange(op.range);
const reverseRangeOffset = op.rangeOffset + reverseRangeDeltaOffset;
reverseRangeDeltaOffset += (op.text.length - bufferText.length);
reverseOperations[i] = {
sortIndex: op.sortIndex,
identifier: op.identifier,
range: reverseRange,
text: bufferText,
textChange: new TextChange(op.rangeOffset, bufferText, reverseRangeOffset, op.text)
};
}
// Can only sort reverse operations when the order is not significant
if (!hasTouchingRanges) {
reverseOperations.sort((a, b) => a.sortIndex - b.sortIndex);
}
}
this._mightContainRTL = mightContainRTL;
this._mightContainUnusualLineTerminators = mightContainUnusualLineTerminators;
this._mightContainNonBasicASCII = mightContainNonBasicASCII;
const contentChanges = this._doApplyEdits(operations);
let trimAutoWhitespaceLineNumbers: number[] | null = null;
if (recordTrimAutoWhitespace && newTrimAutoWhitespaceCandidates.length > 0) {
// sort line numbers auto whitespace removal candidates for next edit descending
newTrimAutoWhitespaceCandidates.sort((a, b) => b.lineNumber - a.lineNumber);
trimAutoWhitespaceLineNumbers = [];
for (let i = 0, len = newTrimAutoWhitespaceCandidates.length; i < len; i++) {
let lineNumber = newTrimAutoWhitespaceCandidates[i].lineNumber;
if (i > 0 && newTrimAutoWhitespaceCandidates[i - 1].lineNumber === lineNumber) {
// Do not have the same line number twice
continue;
}
let prevContent = newTrimAutoWhitespaceCandidates[i].oldContent;
let lineContent = this.getLineContent(lineNumber);
if (lineContent.length === 0 || lineContent === prevContent || strings.firstNonWhitespaceIndex(lineContent) !== -1) {
continue;
}
trimAutoWhitespaceLineNumbers.push(lineNumber);
}
}
this._onDidChangeContent.fire();
return new ApplyEditsResult(
reverseOperations,
contentChanges,
trimAutoWhitespaceLineNumbers
);
}