1
- import { DiffLine } from "../index.js" ;
1
+ import { DiffLine , DiffLineType } from "../index.js" ;
2
2
import { LineStream , matchLine } from "./util.js" ;
3
3
4
4
/**
5
5
* https://blog.jcoglan.com/2017/02/12/the-myers-diff-algorithm-part-1/
6
6
* Invariants:
7
7
* - new + same = newLines.length
8
- * - old + same = oldLines .length
8
+ * - old + same = oldLinesCopy .length
9
9
* ^ (above two guarantee that all lines get represented)
10
10
* - Lines are always output in order, at least among old and new separately
11
11
*/
12
12
export async function * streamDiff (
13
13
oldLines : string [ ] ,
14
14
newLines : LineStream ,
15
15
) : AsyncGenerator < DiffLine > {
16
+ const oldLinesCopy = [ ...oldLines ] ;
17
+
16
18
// If one indentation mistake is made, others are likely. So we are more permissive about matching
17
19
let seenIndentationMistake = false ;
18
20
19
21
let newLineResult = await newLines . next ( ) ;
20
22
21
- while ( oldLines . length > 0 && ! newLineResult . done ) {
23
+ while ( oldLinesCopy . length > 0 && ! newLineResult . done ) {
22
24
const { matchIndex, isPerfectMatch, newLine } = matchLine (
23
25
newLineResult . value ,
24
- oldLines ,
26
+ oldLinesCopy ,
25
27
seenIndentationMistake ,
26
28
) ;
27
29
28
30
if ( ! seenIndentationMistake && newLineResult . value !== newLine ) {
29
31
seenIndentationMistake = true ;
30
32
}
31
33
32
- let type : DiffLine [ "type" ] ;
34
+ let type : DiffLineType ;
33
35
34
36
let isLineRemoval = false ;
35
37
const isNewLine = matchIndex === - 1 ;
@@ -39,7 +41,7 @@ export async function* streamDiff(
39
41
} else {
40
42
// Insert all deleted lines before match
41
43
for ( let i = 0 ; i < matchIndex ; i ++ ) {
42
- yield { type : "old" , line : oldLines . shift ( ) ! } ;
44
+ yield { type : "old" , line : oldLinesCopy . shift ( ) ! } ;
43
45
}
44
46
45
47
type = isPerfectMatch ? "same" : "old" ;
@@ -51,13 +53,13 @@ export async function* streamDiff(
51
53
break ;
52
54
53
55
case "same" :
54
- yield { type, line : oldLines . shift ( ) ! } ;
56
+ yield { type, line : oldLinesCopy . shift ( ) ! } ;
55
57
break ;
56
58
57
59
case "old" :
58
- yield { type, line : oldLines . shift ( ) ! } ;
60
+ yield { type, line : oldLinesCopy . shift ( ) ! } ;
59
61
60
- if ( oldLines [ 0 ] !== newLine ) {
62
+ if ( oldLinesCopy [ 0 ] !== newLine ) {
61
63
yield { type : "new" , line : newLine } ;
62
64
} else {
63
65
isLineRemoval = true ;
@@ -75,13 +77,13 @@ export async function* streamDiff(
75
77
}
76
78
77
79
// Once at the edge, only one choice
78
- if ( newLineResult . done && oldLines . length > 0 ) {
79
- for ( const oldLine of oldLines ) {
80
+ if ( newLineResult . done && oldLinesCopy . length > 0 ) {
81
+ for ( const oldLine of oldLinesCopy ) {
80
82
yield { type : "old" , line : oldLine } ;
81
83
}
82
84
}
83
85
84
- if ( ! newLineResult . done && oldLines . length === 0 ) {
86
+ if ( ! newLineResult . done && oldLinesCopy . length === 0 ) {
85
87
yield { type : "new" , line : newLineResult . value } ;
86
88
for await ( const newLine of newLines ) {
87
89
yield { type : "new" , line : newLine } ;
0 commit comments