( oldLines: string[], newLines: LineStream, )
| 12 | * - Old lines in a hunk are always output before the new lines |
| 13 | */ |
| 14 | export async function* streamDiff( |
| 15 | oldLines: string[], |
| 16 | newLines: LineStream, |
| 17 | ): AsyncGenerator<DiffLine> { |
| 18 | const oldLinesCopy = [...oldLines]; |
| 19 | |
| 20 | // If one indentation mistake is made, others are likely. So we are more permissive about matching |
| 21 | let seenIndentationMistake = false; |
| 22 | |
| 23 | let newLineResult = await newLines.next(); |
| 24 | |
| 25 | while (oldLinesCopy.length > 0 && !newLineResult.done) { |
| 26 | const { matchIndex, isPerfectMatch, newLine } = matchLine( |
| 27 | newLineResult.value, |
| 28 | oldLinesCopy, |
| 29 | seenIndentationMistake, |
| 30 | ); |
| 31 | |
| 32 | if (!seenIndentationMistake && newLineResult.value !== newLine) { |
| 33 | seenIndentationMistake = true; |
| 34 | } |
| 35 | |
| 36 | let type: DiffType; |
| 37 | |
| 38 | const isNewLine = matchIndex === -1; |
| 39 | |
| 40 | if (isNewLine) { |
| 41 | type = "new"; |
| 42 | } else { |
| 43 | // Insert all deleted lines before match |
| 44 | for (let i = 0; i < matchIndex; i++) { |
| 45 | yield { type: "old", line: oldLinesCopy.shift()! }; |
| 46 | } |
| 47 | type = isPerfectMatch ? "same" : "old"; |
| 48 | } |
| 49 | |
| 50 | switch (type) { |
| 51 | case "new": |
| 52 | yield { type, line: newLine }; |
| 53 | break; |
| 54 | |
| 55 | case "same": |
| 56 | yield { type, line: oldLinesCopy.shift()! }; |
| 57 | break; |
| 58 | |
| 59 | case "old": |
| 60 | yield { type, line: oldLinesCopy.shift()! }; |
| 61 | yield { type: "new", line: newLine }; |
| 62 | break; |
| 63 | |
| 64 | default: |
| 65 | console.error(`Error streaming diff, unrecognized diff type: ${type}`); |
| 66 | } |
| 67 | newLineResult = await newLines.next(); |
| 68 | } |
| 69 | |
| 70 | // Once at the edge, only one choice |
| 71 | if (newLineResult.done && oldLinesCopy.length > 0) { |
no test coverage detected