Skip to content

Commit 415fc6f

Browse files
committed
new diff lib: diferenco (WIP)
1 parent fc2d2be commit 415fc6f

26 files changed

+3262
-0
lines changed

modules/diferenco/diferenco.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package diferenco
2+
3+
import "slices"
4+
5+
// https://github.com/Wilfred/difftastic/wiki/Line-Based-Diffs
6+
// https://neil.fraser.name/writing/diff/
7+
// https://prettydiff.com/2/guide/unrelated_diff.xhtml
8+
9+
// Operation defines the operation of a diff item.
10+
type Operation int8
11+
12+
const (
13+
// Delete item represents a delete hunk.
14+
Delete Operation = -1
15+
// Insert item represents an insert hunk.
16+
Insert Operation = 1
17+
// Equal item represents an equal hunk.
18+
Equal Operation = 0
19+
)
20+
21+
// commonPrefixLength returns the length of the common prefix of two T slices.
22+
func commonPrefixLength[E comparable](a, b []E) int {
23+
n := min(len(a), len(b))
24+
i := 0
25+
for i < n && a[i] == b[i] {
26+
i++
27+
}
28+
return i
29+
}
30+
31+
// commonSuffixLength returns the length of the common suffix of two rune slices.
32+
func commonSuffixLength[E comparable](a, b []E) int {
33+
i1, i2 := len(a), len(b)
34+
n := min(i1, i2)
35+
i := 0
36+
for i < n && a[i1-1-i] == b[i2-1-i] {
37+
i++
38+
}
39+
return i
40+
}
41+
42+
func slicesHasSuffix[E comparable](a, suffix []E) bool {
43+
return len(a) >= len(suffix) && slices.Equal(a[len(a)-len(suffix):], suffix)
44+
}
45+
46+
func slicesHasPrefix[E comparable](a, prefix []E) bool {
47+
return len(a) >= len(prefix) && slices.Equal(a[:len(prefix)], prefix)
48+
}
49+
50+
// slicesIndex is the equivalent of strings.Index for rune slices.
51+
func slicesIndex[E comparable](s1, s2 []E) int {
52+
last := len(s1) - len(s2)
53+
for i := 0; i <= last; i++ {
54+
if slices.Equal(s1[i:i+len(s2)], s2) {
55+
return i
56+
}
57+
}
58+
return -1
59+
}
60+
61+
// slicesIndexOf returns the index of pattern in target, starting at target[i].
62+
func slicesIndexOf[E comparable](target, pattern []E, i int) int {
63+
if i > len(target)-1 {
64+
return -1
65+
}
66+
if i <= 0 {
67+
return slicesIndex(target, pattern)
68+
}
69+
ind := slicesIndex(target[i:], pattern)
70+
if ind == -1 {
71+
return -1
72+
}
73+
return ind + i
74+
}
75+
76+
type Change struct {
77+
P1 int // before: position in before
78+
P2 int // after: position in after
79+
Del int // number of elements that deleted from a
80+
Ins int // number of elements that inserted into b
81+
}
82+
83+
type Dfio[E comparable] struct {
84+
T Operation
85+
E []E
86+
}
87+
88+
// StringDiff represents one diff operation
89+
type StringDiff struct {
90+
Type Operation
91+
Text string
92+
}

modules/diferenco/diferenco_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package diferenco
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
"runtime"
8+
"testing"
9+
)
10+
11+
func TestDiff(t *testing.T) {
12+
_, filename, _, _ := runtime.Caller(0)
13+
dir := filepath.Dir(filename)
14+
bytesA, err := os.ReadFile(filepath.Join(dir, "testdata/a.txt"))
15+
if err != nil {
16+
fmt.Fprintf(os.Stderr, "read a error: %v\n", err)
17+
return
18+
}
19+
textA := string(bytesA)
20+
bytesB, err := os.ReadFile(filepath.Join(dir, "testdata/b.txt"))
21+
if err != nil {
22+
fmt.Fprintf(os.Stderr, "read b error: %v\n", err)
23+
return
24+
}
25+
textB := string(bytesB)
26+
sink := &Sink{
27+
Index: make(map[string]int),
28+
}
29+
a := sink.ParseLines(textA)
30+
b := sink.ParseLines(textB)
31+
changes := OnpDiff(a, b)
32+
i := 0
33+
for _, c := range changes {
34+
for ; i < c.P1; i++ {
35+
fmt.Fprintf(os.Stderr, " %s", sink.Lines[a[i]])
36+
}
37+
for j := c.P1; j < c.P1+c.Del; j++ {
38+
fmt.Fprintf(os.Stderr, "\x1b[31m- %s\x1b[0m", sink.Lines[a[j]])
39+
}
40+
for j := c.P2; j < c.P2+c.Ins; j++ {
41+
fmt.Fprintf(os.Stderr, "\x1b[32m+ %s\x1b[0m", sink.Lines[b[j]])
42+
}
43+
i += c.Del
44+
}
45+
for ; i < len(a); i++ {
46+
fmt.Fprintf(os.Stderr, " %s", sink.Lines[a[i]])
47+
}
48+
fmt.Fprintf(os.Stderr, "\n\npatience\n\n")
49+
50+
diffs := PatienceDiff(a, b)
51+
for _, d := range diffs {
52+
switch d.T {
53+
case Delete:
54+
for _, i := range d.E {
55+
fmt.Fprintf(os.Stderr, "\x1b[31m-%s\x1b[0m", sink.Lines[i])
56+
}
57+
case Insert:
58+
for _, i := range d.E {
59+
fmt.Fprintf(os.Stderr, "\x1b[32m+%s\x1b[0m", sink.Lines[i])
60+
}
61+
default:
62+
for _, i := range d.E {
63+
fmt.Fprintf(os.Stderr, " %s", sink.Lines[i])
64+
}
65+
}
66+
}
67+
}

0 commit comments

Comments
 (0)