public apply()

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;
						}
					}
				}
			}
		});
	}