From f3a93a209707a2fe30a9bdc69023fffeb4ba8a9a Mon Sep 17 00:00:00 2001 From: Boming Zhang Date: Wed, 26 Mar 2025 21:07:44 -0400 Subject: [PATCH] chore(parser/diff): use generic --- internal/parser/diff/diff.go | 2 +- internal/parser/diff/myers.go | 35 ++++++++++++------------------ internal/parser/diff/myers_test.go | 24 ++++++++++---------- 3 files changed, 27 insertions(+), 34 deletions(-) diff --git a/internal/parser/diff/diff.go b/internal/parser/diff/diff.go index 8d5853e..3e22e8b 100644 --- a/internal/parser/diff/diff.go +++ b/internal/parser/diff/diff.go @@ -50,7 +50,7 @@ func isWhitespace(b byte) bool { b == 0xA0 } -func formatDiff(oldList []string, newList []string, ops []Op) string { +func formatDiff(oldList []string, newList []string, ops []Op[string]) string { var result []string i, j := 0, 0 for _, op := range ops { diff --git a/internal/parser/diff/myers.go b/internal/parser/diff/myers.go index b249e10..a52a6a8 100644 --- a/internal/parser/diff/myers.go +++ b/internal/parser/diff/myers.go @@ -11,21 +11,21 @@ const ( OpDelete ) -type Op struct { +type Op[T any] struct { OpType OpType // Insert or delete, as above OldPos int // Position in the old list of item to be inserted or deleted NewPos int // Position in the _new_ list of item to be inserted - Elem any // Actual value to be inserted or deleted + Elem T // Actual value to be inserted or deleted } // Returns a minimal list of differences between 2 lists e and f // requiring O(min(len(e),len(f))) space and O(min(len(e),len(f)) * D) // worst-case execution time where D is the number of differences. -func myersDiff(e, f []any, equals func(any, any) bool) []Op { +func myersDiff[T any](e, f []T, equals func(T, T) bool) []Op[T] { return diffInternal(e, f, equals, 0, 0) } -func diffInternal(e, f []any, equals func(any, any) bool, i, j int) []Op { +func diffInternal[T any](e, f []T, equals func(T, T) bool, i, j int) []Op[T] { N := len(e) M := len(f) L := N + M @@ -88,26 +88,26 @@ func diffInternal(e, f []any, equals func(any, any) bool, i, j int) []Op { case D > 1 || (x != u && y != v): return append(diffInternal(e[0:x], f[0:y], equals, i, j), diffInternal(e[u:N], f[v:M], equals, i+u, j+v)...) case M > N: - return diffInternal(make([]any, 0), f[N:M], equals, i+N, j+N) + return diffInternal(make([]T, 0), f[N:M], equals, i+N, j+N) case M < N: - return diffInternal(e[M:N], make([]any, 0), equals, i+M, j+M) + return diffInternal(e[M:N], make([]T, 0), equals, i+M, j+M) default: - return make([]Op, 0) + return make([]Op[T], 0) } } } } } case N > 0: - res := make([]Op, N) + res := make([]Op[T], N) for n := range N { - res[n] = Op{OpDelete, i + n, -1, e[n]} + res[n] = Op[T]{OpDelete, i + n, -1, e[n]} } return res default: - res := make([]Op, M) + res := make([]Op[T], M) for n := range M { - res[n] = Op{OpInsert, i, j + n, f[n]} + res[n] = Op[T]{OpInsert, i, j + n, f[n]} } return res } @@ -127,15 +127,8 @@ func pyMod(x, y int) int { // Let us map element in same way as in // Convenient wrapper for string lists -func myersDiffStr(e, f []string, compareSpace bool) []Op { - e1, f1 := make([]any, len(e)), make([]any, len(f)) - for i, ee := range e { - e1[i] = ee - } - for i, fe := range f { - f1[i] = fe - } - return myersDiff(e1, f1, func(s1, s2 any) bool { - return compareStrings(s1.(string), s2.(string), compareSpace) +func myersDiffStr(e, f []string, compareSpace bool) []Op[string] { + return myersDiff[string](e, f, func(s1, s2 string) bool { + return compareStrings(s1, s2, compareSpace) }) } diff --git a/internal/parser/diff/myers_test.go b/internal/parser/diff/myers_test.go index b32e125..6d331c4 100644 --- a/internal/parser/diff/myers_test.go +++ b/internal/parser/diff/myers_test.go @@ -8,7 +8,7 @@ import ( type TestCase struct { l1 []string l2 []string - exp []Op + exp []Op[string] } func TestDiff(t *t.T) { @@ -16,28 +16,28 @@ func TestDiff(t *t.T) { B := "B" C := "C" testCases := []TestCase{ - {[]string{}, []string{}, []Op{}}, - {[]string{}, []string{"foo"}, []Op{{OpInsert, 0, 0, "foo"}}}, - {[]string{"foo"}, []string{}, []Op{{OpDelete, 0, -1, "foo"}}}, - {[]string{"foo", "bar", "baz"}, []string{"foo", "bar", "baz"}, []Op{}}, - {[]string{"foo", "bar", "baz"}, []string{"foo", "baz"}, []Op{{OpDelete, 1, -1, "bar"}}}, - {[]string{"baz"}, []string{"foo", "baz"}, []Op{{OpInsert, 0, 0, "foo"}}}, - {[]string{"bar", "baz"}, []string{"foo", "baz"}, []Op{{OpDelete, 0, -1, "bar"}, {OpInsert, 1, 0, "foo"}}}, - {[]string{"foo", "bar", "baz"}, []string{"foo", "bar"}, []Op{{OpDelete, 2, -1, "baz"}}}, + {[]string{}, []string{}, []Op[string]{}}, + {[]string{}, []string{"foo"}, []Op[string]{{OpInsert, 0, 0, "foo"}}}, + {[]string{"foo"}, []string{}, []Op[string]{{OpDelete, 0, -1, "foo"}}}, + {[]string{"foo", "bar", "baz"}, []string{"foo", "bar", "baz"}, []Op[string]{}}, + {[]string{"foo", "bar", "baz"}, []string{"foo", "baz"}, []Op[string]{{OpDelete, 1, -1, "bar"}}}, + {[]string{"baz"}, []string{"foo", "baz"}, []Op[string]{{OpInsert, 0, 0, "foo"}}}, + {[]string{"bar", "baz"}, []string{"foo", "baz"}, []Op[string]{{OpDelete, 0, -1, "bar"}, {OpInsert, 1, 0, "foo"}}}, + {[]string{"foo", "bar", "baz"}, []string{"foo", "bar"}, []Op[string]{{OpDelete, 2, -1, "baz"}}}, { []string{A, B, C, A, B, B, A}, []string{C, B, A, B, A, C}, - []Op{{OpDelete, 0, -1, A}, {OpInsert, 1, 0, C}, {OpDelete, 2, -1, C}, {OpDelete, 5, -1, B}, {OpInsert, 7, 5, C}}, + []Op[string]{{OpDelete, 0, -1, A}, {OpInsert, 1, 0, C}, {OpDelete, 2, -1, C}, {OpDelete, 5, -1, B}, {OpInsert, 7, 5, C}}, }, { []string{C, A, B, A, B, A, B, A, B, A, B, A, B, C}, []string{B, A, B, A, B, A, B, A, B, A, B, A, B, A}, - []Op{{OpDelete, 0, -1, C}, {OpInsert, 1, 0, B}, {OpDelete, 13, -1, C}, {OpInsert, 14, 13, A}}, + []Op[string]{{OpDelete, 0, -1, C}, {OpInsert, 1, 0, B}, {OpDelete, 13, -1, C}, {OpInsert, 14, 13, A}}, }, { []string{B}, []string{A, B, C, B, A}, - []Op{{OpInsert, 0, 0, A}, {OpInsert, 0, 1, B}, {OpInsert, 0, 2, C}, {OpInsert, 1, 4, A}}, + []Op[string]{{OpInsert, 0, 0, A}, {OpInsert, 0, 1, B}, {OpInsert, 0, 2, C}, {OpInsert, 1, 4, A}}, }, } for _, c := range testCases {