-
-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathmerge.go
81 lines (71 loc) · 1.89 KB
/
merge.go
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
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package udiff
import (
"slices"
)
// Merge merges two valid, ordered lists of edits.
// It returns zero if there was a conflict.
//
// If corresponding edits in x and y are identical,
// they are coalesced in the result.
//
// If x and y both provide different insertions at the same point,
// the insertions from x will be first in the result.
//
// TODO(adonovan): this algorithm could be improved, for example by
// working harder to coalesce non-identical edits that share a common
// deletion or common prefix of insertion (see the tests).
// Survey the academic literature for insights.
func Merge(x, y []Edit) ([]Edit, bool) {
// Make a defensive (premature) copy of the arrays.
x = slices.Clone(x)
y = slices.Clone(y)
var merged []Edit
add := func(edit Edit) {
merged = append(merged, edit)
}
var xi, yi int
for xi < len(x) && yi < len(y) {
px := &x[xi]
py := &y[yi]
if *px == *py {
// x and y are identical: coalesce.
add(*px)
xi++
yi++
} else if px.End <= py.Start {
// x is entirely before y,
// or an insertion at start of y.
add(*px)
xi++
} else if py.End <= px.Start {
// y is entirely before x,
// or an insertion at start of x.
add(*py)
yi++
} else if px.Start < py.Start {
// x is partly before y:
// split it into a deletion and an edit.
add(Edit{px.Start, py.Start, ""})
px.Start = py.Start
} else if py.Start < px.Start {
// y is partly before x:
// split it into a deletion and an edit.
add(Edit{py.Start, px.Start, ""})
py.Start = px.Start
} else {
// x and y are unequal non-insertions
// at the same point: conflict.
return nil, false
}
}
for ; xi < len(x); xi++ {
add(x[xi])
}
for ; yi < len(y); yi++ {
add(y[yi])
}
return merged, true
}