forked from continuedev/continue
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathstreamDiff.ts
83 lines (68 loc) · 2.28 KB
/
streamDiff.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import { DiffLine, DiffLineType } from "../index.js";
import { LineStream, matchLine } from "./util.js";
/**
* https://blog.jcoglan.com/2017/02/12/the-myers-diff-algorithm-part-1/
* Invariants:
* - new + same = newLines.length
* - old + same = oldLinesCopy.length
* ^ (above two guarantee that all lines get represented)
* - Lines are always output in order, at least among old and new separately
* - Old lines in a hunk are always output before the new lines
*/
export async function* streamDiff(
oldLines: string[],
newLines: LineStream,
): AsyncGenerator<DiffLine> {
const oldLinesCopy = [...oldLines];
// If one indentation mistake is made, others are likely. So we are more permissive about matching
let seenIndentationMistake = false;
let newLineResult = await newLines.next();
while (oldLinesCopy.length > 0 && !newLineResult.done) {
const { matchIndex, isPerfectMatch, newLine } = matchLine(
newLineResult.value,
oldLinesCopy,
seenIndentationMistake,
);
if (!seenIndentationMistake && newLineResult.value !== newLine) {
seenIndentationMistake = true;
}
let type: DiffLineType;
const isNewLine = matchIndex === -1;
if (isNewLine) {
type = "new";
} else {
// Insert all deleted lines before match
for (let i = 0; i < matchIndex; i++) {
yield { type: "old", line: oldLinesCopy.shift()! };
}
type = isPerfectMatch ? "same" : "old";
}
switch (type) {
case "new":
yield { type, line: newLine };
break;
case "same":
yield { type, line: oldLinesCopy.shift()! };
break;
case "old":
yield { type, line: oldLinesCopy.shift()! };
yield { type: "new", line: newLine };
break;
default:
console.error(`Error streaming diff, unrecognized diff type: ${type}`);
}
newLineResult = await newLines.next();
}
// Once at the edge, only one choice
if (newLineResult.done && oldLinesCopy.length > 0) {
for (const oldLine of oldLinesCopy) {
yield { type: "old", line: oldLine };
}
}
if (!newLineResult.done && oldLinesCopy.length === 0) {
yield { type: "new", line: newLineResult.value };
for await (const newLine of newLines) {
yield { type: "new", line: newLine };
}
}
}