123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 |
- "use strict"
- const lineEndingsRe = /\r\n|\r|\n/g
- function lineStarts(str) {
- const result = [0]
- lineEndingsRe.lastIndex = 0
- while (true) {
- const match = lineEndingsRe.exec(str)
- if (!match) break
- result.push(lineEndingsRe.lastIndex)
- }
- return result
- }
- function locationToIndex(location, lineStarts) {
- if (
- !location.line ||
- location.line < 0 ||
- !location.column ||
- location.column < 0
- ) {
- throw new Error("Invalid location")
- }
- return lineStarts[location.line - 1] + location.column - 1
- }
- function indexToLocation(index, lineStarts) {
- if (index < 0) throw new Error("Invalid index")
- let line = 0
- while (line + 1 < lineStarts.length && lineStarts[line + 1] <= index) {
- line += 1
- }
- return {
- line: line + 1,
- column: index - lineStarts[line] + 1,
- }
- }
- module.exports = class TransformableString {
- constructor(original) {
- this._original = original
- this._blocks = []
- this._lineStarts = lineStarts(original)
- this._cache = null
- }
- _compute() {
- if (!this._cache) {
- let result = ""
- let index = 0
- for (const block of this._blocks) {
- result += this._original.slice(index, block.from) + block.str
- index = block.to
- }
- result += this._original.slice(index)
- this._cache = {
- lineStarts: lineStarts(result),
- result,
- }
- }
- return this._cache
- }
- getOriginalLine(n) {
- if (n < 1 || n > this._lineStarts.length) {
- throw new Error("Invalid line number")
- }
- return this._original
- .slice(this._lineStarts[n - 1], this._lineStarts[n])
- .replace(lineEndingsRe, "")
- }
- toString() {
- return this._compute().result
- }
- replace(from, to, str) {
- this._cache = null
- if (from > to || from < 0 || to > this._original.length) {
- throw new Error("Invalid slice indexes")
- }
- const newBlock = { from, to, str }
- if (
- !this._blocks.length ||
- this._blocks[this._blocks.length - 1].to <= from
- ) {
- this._blocks.push(newBlock)
- } else {
- const index = this._blocks.findIndex((other) => other.to > from)
- if (this._blocks[index].from < to) throw new Error("Can't replace slice")
- this._blocks.splice(index, 0, newBlock)
- }
- }
- originalIndex(index) {
- let block
- for (block of this._blocks) {
- if (index < block.from) break
- if (index < block.from + block.str.length) {
- return
- } else {
- index += block.to - block.from - block.str.length
- }
- }
- if (index < 0 || index > this._original.length) {
- throw new Error("Invalid index")
- }
- if (index == this._original.length) {
- if (block.to && block.to === this._original.length) {
- return block.from + block.str.length
- }
- return this._original.length
- }
- return index
- }
- originalLocation(location) {
- const index = locationToIndex(location, this._compute().lineStarts)
- const originalIndex = this.originalIndex(index)
- if (originalIndex !== undefined) {
- return indexToLocation(originalIndex, this._lineStarts)
- }
- }
- }
|