chore: having some fun with the `go-udiff` package

Andrey Nering created

Change summary

go.mod                                                                                       |  1 
go.sum                                                                                       |  4 
internal/exp/diff/diff.go                                                                    |  1 
internal/exp/diff/testdata/TestUdiff/ToUnifiedDiff/DefaultContextLines/Content.golden        | 11 
internal/exp/diff/testdata/TestUdiff/ToUnifiedDiff/DefaultContextLines/JSON.golden           | 40 
internal/exp/diff/testdata/TestUdiff/ToUnifiedDiff/DefaultContextLinesPlusOne/Content.golden | 12 
internal/exp/diff/testdata/TestUdiff/ToUnifiedDiff/DefaultContextLinesPlusOne/JSON.golden    | 44 
internal/exp/diff/testdata/TestUdiff/ToUnifiedDiff/DefaultContextLinesPlusTwo/Content.golden | 13 
internal/exp/diff/testdata/TestUdiff/ToUnifiedDiff/DefaultContextLinesPlusTwo/JSON.golden    | 48 
internal/exp/diff/testdata/TestUdiff/Unified.golden                                          | 11 
internal/exp/diff/udiff_test.go                                                              | 92 
11 files changed, 275 insertions(+), 2 deletions(-)

Detailed changes

go.mod 🔗

@@ -16,6 +16,7 @@ require (
 	github.com/charmbracelet/glamour v0.9.1
 	github.com/charmbracelet/lipgloss v1.1.0
 	github.com/charmbracelet/x/ansi v0.8.0
+	github.com/charmbracelet/x/exp/golden v0.0.0-20250602192518-9e722df69bbb
 	github.com/fsnotify/fsnotify v1.8.0
 	github.com/go-logfmt/logfmt v0.6.0
 	github.com/google/uuid v1.6.0

go.sum 🔗

@@ -82,8 +82,8 @@ github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2ll
 github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
 github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
 github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
-github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ=
-github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
+github.com/charmbracelet/x/exp/golden v0.0.0-20250602192518-9e722df69bbb h1:GT/STWThMsrOfYQnhnIPb165e/g1waAp0gNMFvEO6WI=
+github.com/charmbracelet/x/exp/golden v0.0.0-20250602192518-9e722df69bbb/go.mod h1:929X+xY3LeoOZrDWIBVZcx/zyS0CYtyLiUIvE4VbKC0=
 github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
 github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
 github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=

internal/exp/diff/testdata/TestUdiff/ToUnifiedDiff/DefaultContextLines/JSON.golden 🔗

@@ -0,0 +1,40 @@
+{
+  "From": "main.go",
+  "To": "main.go",
+  "Hunks": [
+    {
+      "FromLine": 5,
+      "ToLine": 5,
+      "Lines": [
+        {
+          "Kind": 2,
+          "Content": "\t)\n"
+        },
+        {
+          "Kind": 2,
+          "Content": "\n"
+        },
+        {
+          "Kind": 2,
+          "Content": "\tfunc main() {\n"
+        },
+        {
+          "Kind": 0,
+          "Content": "\t\tfmt.Println(\"Hello, World!\")\n"
+        },
+        {
+          "Kind": 1,
+          "Content": "\t\tcontent := \"Hello, World!\"\n"
+        },
+        {
+          "Kind": 1,
+          "Content": "\t\tfmt.Println(content)\n"
+        },
+        {
+          "Kind": 2,
+          "Content": "\t}"
+        }
+      ]
+    }
+  ]
+}

internal/exp/diff/testdata/TestUdiff/ToUnifiedDiff/DefaultContextLinesPlusOne/JSON.golden 🔗

@@ -0,0 +1,44 @@
+{
+  "From": "main.go",
+  "To": "main.go",
+  "Hunks": [
+    {
+      "FromLine": 4,
+      "ToLine": 4,
+      "Lines": [
+        {
+          "Kind": 2,
+          "Content": "\t\t\"fmt\"\n"
+        },
+        {
+          "Kind": 2,
+          "Content": "\t)\n"
+        },
+        {
+          "Kind": 2,
+          "Content": "\n"
+        },
+        {
+          "Kind": 2,
+          "Content": "\tfunc main() {\n"
+        },
+        {
+          "Kind": 0,
+          "Content": "\t\tfmt.Println(\"Hello, World!\")\n"
+        },
+        {
+          "Kind": 1,
+          "Content": "\t\tcontent := \"Hello, World!\"\n"
+        },
+        {
+          "Kind": 1,
+          "Content": "\t\tfmt.Println(content)\n"
+        },
+        {
+          "Kind": 2,
+          "Content": "\t}"
+        }
+      ]
+    }
+  ]
+}

internal/exp/diff/testdata/TestUdiff/ToUnifiedDiff/DefaultContextLinesPlusTwo/JSON.golden 🔗

@@ -0,0 +1,48 @@
+{
+  "From": "main.go",
+  "To": "main.go",
+  "Hunks": [
+    {
+      "FromLine": 3,
+      "ToLine": 3,
+      "Lines": [
+        {
+          "Kind": 2,
+          "Content": "\timport (\n"
+        },
+        {
+          "Kind": 2,
+          "Content": "\t\t\"fmt\"\n"
+        },
+        {
+          "Kind": 2,
+          "Content": "\t)\n"
+        },
+        {
+          "Kind": 2,
+          "Content": "\n"
+        },
+        {
+          "Kind": 2,
+          "Content": "\tfunc main() {\n"
+        },
+        {
+          "Kind": 0,
+          "Content": "\t\tfmt.Println(\"Hello, World!\")\n"
+        },
+        {
+          "Kind": 1,
+          "Content": "\t\tcontent := \"Hello, World!\"\n"
+        },
+        {
+          "Kind": 1,
+          "Content": "\t\tfmt.Println(content)\n"
+        },
+        {
+          "Kind": 2,
+          "Content": "\t}"
+        }
+      ]
+    }
+  ]
+}

internal/exp/diff/udiff_test.go 🔗

@@ -0,0 +1,92 @@
+package diff_test
+
+import (
+	"bytes"
+	"encoding/json"
+	"testing"
+
+	"github.com/aymanbagabas/go-udiff"
+	"github.com/aymanbagabas/go-udiff/myers"
+	"github.com/charmbracelet/x/exp/golden"
+)
+
+func TestUdiff(t *testing.T) {
+	before := `package main
+
+	import (
+		"fmt"
+	)
+
+	func main() {
+		fmt.Println("Hello, World!")
+	}`
+
+	after := `package main
+
+	import (
+		"fmt"
+	)
+
+	func main() {
+		content := "Hello, World!"
+		fmt.Println(content)
+	}`
+
+	t.Run("Unified", func(t *testing.T) {
+		content := udiff.Unified("main.go", "main.go", before, after)
+		golden.RequireEqual(t, []byte(content))
+	})
+
+	t.Run("ToUnifiedDiff", func(t *testing.T) {
+		toUnifiedDiff := func(t *testing.T, before, after string, contextLines int) udiff.UnifiedDiff {
+			edits := myers.ComputeEdits(before, after)
+			unifiedDiff, err := udiff.ToUnifiedDiff("main.go", "main.go", before, edits, contextLines)
+			if err != nil {
+				t.Fatalf("ToUnifiedDiff failed: %v", err)
+			}
+			return unifiedDiff
+		}
+		toJSON := func(t *testing.T, unifiedDiff udiff.UnifiedDiff) []byte {
+			var buff bytes.Buffer
+			encoder := json.NewEncoder(&buff)
+			encoder.SetIndent("", "  ")
+			if err := encoder.Encode(unifiedDiff); err != nil {
+				t.Fatalf("Failed to encode unified diff: %v", err)
+			}
+			return buff.Bytes()
+		}
+
+		t.Run("DefaultContextLines", func(t *testing.T) {
+			unifiedDiff := toUnifiedDiff(t, before, after, udiff.DefaultContextLines)
+
+			t.Run("Content", func(t *testing.T) {
+				golden.RequireEqual(t, []byte(unifiedDiff.String()))
+			})
+			t.Run("JSON", func(t *testing.T) {
+				golden.RequireEqual(t, toJSON(t, unifiedDiff))
+			})
+		})
+
+		t.Run("DefaultContextLinesPlusOne", func(t *testing.T) {
+			unifiedDiff := toUnifiedDiff(t, before, after, udiff.DefaultContextLines+1)
+
+			t.Run("Content", func(t *testing.T) {
+				golden.RequireEqual(t, []byte(unifiedDiff.String()))
+			})
+			t.Run("JSON", func(t *testing.T) {
+				golden.RequireEqual(t, toJSON(t, unifiedDiff))
+			})
+		})
+
+		t.Run("DefaultContextLinesPlusTwo", func(t *testing.T) {
+			unifiedDiff := toUnifiedDiff(t, before, after, udiff.DefaultContextLines+2)
+
+			t.Run("Content", func(t *testing.T) {
+				golden.RequireEqual(t, []byte(unifiedDiff.String()))
+			})
+			t.Run("JSON", func(t *testing.T) {
+				golden.RequireEqual(t, toJSON(t, unifiedDiff))
+			})
+		})
+	})
+}