| 130 | } |
| 131 | |
| 132 | function computeReplacements(lines: ReadonlyArray<string>, path: string, chunks: ReadonlyArray<UpdateFileChunk>) { |
| 133 | const replacements: Array<readonly [start: number, remove: number, insert: ReadonlyArray<string>]> = [] |
| 134 | let lineIndex = 0 |
| 135 | for (const chunk of chunks) { |
| 136 | if (chunk.changeContext) { |
| 137 | const context = seek(lines, [chunk.changeContext], lineIndex) |
| 138 | if (context === -1) throw new Error(`Failed to find context '${chunk.changeContext}' in ${path}`) |
| 139 | lineIndex = context + 1 |
| 140 | } |
| 141 | if (chunk.oldLines.length === 0) { |
| 142 | replacements.push([lines.length, 0, chunk.newLines]) |
| 143 | continue |
| 144 | } |
| 145 | let oldLines = chunk.oldLines |
| 146 | let newLines = chunk.newLines |
| 147 | let found = seek(lines, oldLines, lineIndex, chunk.endOfFile) |
| 148 | if (found === -1 && oldLines.at(-1) === "") { |
| 149 | oldLines = oldLines.slice(0, -1) |
| 150 | if (newLines.at(-1) === "") newLines = newLines.slice(0, -1) |
| 151 | found = seek(lines, oldLines, lineIndex, chunk.endOfFile) |
| 152 | } |
| 153 | if (found === -1) throw new Error(`Failed to find expected lines in ${path}:\n${chunk.oldLines.join("\n")}`) |
| 154 | replacements.push([found, oldLines.length, newLines]) |
| 155 | lineIndex = found + oldLines.length |
| 156 | } |
| 157 | return replacements.toSorted((left, right) => left[0] - right[0]) |
| 158 | } |
| 159 | |
| 160 | function seek(lines: ReadonlyArray<string>, pattern: ReadonlyArray<string>, start: number, eof = false) { |
| 161 | if (pattern.length === 0) return -1 |