in src/lib.ts [197:307]
public apply(patches: Patch[]): void {
if (patches.length === 0) {
return;
}
patches = patches.sort((a, b) => {
const lca = a.span.start;
const lcb = b.span.start;
return lca.line !== lcb.line ? lca.line - lcb.line : lca.character - lcb.character;
});
let overlapping = false;
if (patches.length > 1) {
const previousSpan = patches[0].span;
for (let i = 1; i < patches.length; i++) {
const nextSpan = patches[i].span;
if (previousSpan.end.line > nextSpan.start.line || (previousSpan.end.line === nextSpan.start.line && previousSpan.end.character >= nextSpan.start.character)) {
overlapping = true;
break;
}
}
}
if (overlapping) {
throw new Error(`Overlapping text edits generated.`);
}
const lastPatch = patches[patches.length - 1];
const lastLine = this.lines[this.lineCount - 1];
if (lastPatch.span.end.line > this.lines.length || (lastPatch.span.end.line === this.lineCount && lastPatch.span.end.character > lastLine.content!.length)) {
throw new Error(`Patches are outside of the buffer content.`);
}
let mappingCursor: {
line: number;
index: number;
} = { line: -1, index: -1 };
patches.reverse().forEach((patch) => {
const startLineNumber = patch.span.start.line;
const endLineNumber = patch.span.end.line;
const startLine = this.lines[startLineNumber];
const endLine = this.lines[endLineNumber];
// Do the textual manipulations.
startLine.content = [
startLine.content!.substring(0, patch.span.start.character),
patch.content,
endLine.content!.substring(patch.span.end.character)
].join('');
for (let i = startLineNumber + 1; i <= endLineNumber; i++) {
this.lines[i].content = null;
}
// Adopt source mapping if available
if (this.rawSourceMap) {
if (startLineNumber === endLineNumber) {
if (!mappingCursor || mappingCursor.line !== startLineNumber) {
mappingCursor.line = startLineNumber;
mappingCursor.index = startLine.mappings ? startLine.mappings.length - 1 : -1;
}
let delta = patch.content.length - (patch.span.end.character - patch.span.start.character);
let mappingItem: MappingItem | null = null;
while ((mappingItem = mappingCursor.index !== -1 ? startLine.mappings![mappingCursor.index] : null) !== null
&& mappingItem.generatedColumn > patch.span.start.character) {
if (mappingItem.generatedColumn < patch.span.end.character) {
// The patch covers the source mapping. Delete it
mappingItem.delete = true;
}
mappingCursor.index--;
}
// Record the delta on the first source marker after the patch.
if (mappingCursor.index + 1 < startLine.mappings!.length) {
let mapping = startLine.mappings![mappingCursor.index + 1];
mapping.columnDelta = (mapping.columnDelta || 0) + delta;
}
} else {
let startLineMappings = startLine.mappings;
if (startLineMappings) {
for (let i = startLineMappings.length - 1; i >= 0 && startLineMappings[i].generatedColumn > patch.span.start.character; i--) {
startLineMappings[i].delete = true;
}
}
for (let i = startLineNumber + 1; i < endLineNumber; i++) {
let line = this.lines[i];
if (line.mappings) {
line.mappings.forEach(mapping => mapping.delete = true);
}
}
let endLineMappings = endLine.mappings;
if (endLineMappings) {
let lineDelta = startLineNumber - endLineNumber;
let index = 0;
for (; index < endLineMappings.length; index++) {
let mapping = endLineMappings[index];
if (mapping.generatedColumn < patch.span.end.character) {
mapping.delete = true;
} else {
break;
}
}
if (index < endLineMappings.length) {
let mapping = endLineMappings[index];
mapping.lineDelta = lineDelta;
mapping.columnDelta = (patch.span.start.character - patch.span.end.character) + patch.content.length;
}
}
}
}
});
}