From 1f98b24fab6b16ebbca9100412f1177e870a37f2 Mon Sep 17 00:00:00 2001 From: Andrey Nering Date: Tue, 3 Jun 2025 15:01:34 -0300 Subject: [PATCH] feat(diffview): getting started with the api design --- go.mod | 5 +- go.sum | 10 +- internal/exp/diffview/diffview.go | 124 ++++++++++++++++++ internal/exp/diffview/diffview_test.go | 15 +++ .../exp/diffview/testdata/TestDefault.golden | 7 + 5 files changed, 155 insertions(+), 6 deletions(-) create mode 100644 internal/exp/diffview/diffview_test.go create mode 100644 internal/exp/diffview/testdata/TestDefault.golden diff --git a/go.mod b/go.mod index 866c30da7e92eefaad23d3fd715bed3533e5f9f4..1db1275fadeeac9c8cc2033b1c689a843e41d8a5 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ require ( github.com/charmbracelet/bubbletea v1.3.5 github.com/charmbracelet/glamour v0.9.1 github.com/charmbracelet/lipgloss v1.1.0 + github.com/charmbracelet/lipgloss/v2 v2.0.0-beta1 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 @@ -59,8 +60,8 @@ require ( github.com/aws/smithy-go v1.20.3 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect - github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect - github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect + github.com/charmbracelet/colorprofile v0.3.0 // indirect + github.com/charmbracelet/x/cellbuf v0.0.13 // indirect github.com/charmbracelet/x/term v0.2.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/disintegration/imaging v1.6.2 diff --git a/go.sum b/go.sum index 3c742d185d7cf35460c7e1dc3d321e2f91013d41..3aff4dea3db17367f337812636de7f5431e1ff3f 100644 --- a/go.sum +++ b/go.sum @@ -72,16 +72,18 @@ github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg= github.com/charmbracelet/bubbletea v1.3.5 h1:JAMNLTbqMOhSwoELIr0qyP4VidFq72/6E9j7HHmRKQc= github.com/charmbracelet/bubbletea v1.3.5/go.mod h1:TkCnmH+aBd4LrXhXcqrKiYwRs7qyQx5rBgH5fVY3v54= -github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs= -github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= +github.com/charmbracelet/colorprofile v0.3.0 h1:KtLh9uuu1RCt+Hml4s6Hz+kB1PfV3wi++1h5ia65yKQ= +github.com/charmbracelet/colorprofile v0.3.0/go.mod h1:oHJ340RS2nmG1zRGPmhJKJ/jf4FPNNk0P39/wBPA1G0= github.com/charmbracelet/glamour v0.9.1 h1:11dEfiGP8q1BEqvGoIjivuc2rBk+5qEXdPtaQ2WoiCM= github.com/charmbracelet/glamour v0.9.1/go.mod h1:+SHvIS8qnwhgTpVMiXwn7OfGomSqff1cHBCI8jLOetk= github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= +github.com/charmbracelet/lipgloss/v2 v2.0.0-beta1 h1:SOylT6+BQzPHEjn15TIzawBPVD0QmhKXbcb3jY0ZIKU= +github.com/charmbracelet/lipgloss/v2 v2.0.0-beta1/go.mod h1:tRlx/Hu0lo/j9viunCN2H+Ze6JrmdjQlXUQvvArgaOc= github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE= 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/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k= +github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= 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= diff --git a/internal/exp/diffview/diffview.go b/internal/exp/diffview/diffview.go index 5bf12488f6eb960702f10ffafd8c7f58e608c06b..b4949c1be2ea2d15ba0829e4a686e020cd9c5a45 100644 --- a/internal/exp/diffview/diffview.go +++ b/internal/exp/diffview/diffview.go @@ -1 +1,125 @@ package diffview + +import ( + "github.com/aymanbagabas/go-udiff" + "github.com/aymanbagabas/go-udiff/myers" + "github.com/charmbracelet/lipgloss/v2" +) + +type file struct { + path string + content string +} + +type layout int + +const ( + layoutUnified layout = iota + 1 + layoutSplit +) + +// DiffView represents a view for displaying differences between two files. +type DiffView struct { + layout layout + before file + after file + contextLines int + baseStyle lipgloss.Style + highlight bool + height int + width int + + isComputed bool + err error + unified udiff.UnifiedDiff + edits []udiff.Edit +} + +// New creates a new DiffView with default settings. +func New() *DiffView { + return &DiffView{ + layout: layoutUnified, + contextLines: udiff.DefaultContextLines, + } +} + +// Unified sets the layout of the DiffView to unified. +func (dv *DiffView) Unified() *DiffView { + dv.layout = layoutUnified + return dv +} + +// Split sets the layout of the DiffView to split (side-by-side). +func (dv *DiffView) Split() *DiffView { + dv.layout = layoutSplit + return dv +} + +// Before sets the "before" file for the DiffView. +func (dv *DiffView) Before(path, content string) *DiffView { + dv.before = file{path: path, content: content} + return dv +} + +// After sets the "after" file for the DiffView. +func (dv *DiffView) After(path, content string) *DiffView { + dv.after = file{path: path, content: content} + return dv +} + +// ContextLines sets the number of context lines for the DiffView. +func (dv *DiffView) ContextLines(contextLines int) *DiffView { + dv.contextLines = contextLines + return dv +} + +// BaseStyle sets the base style for the DiffView. +// This is useful for setting a custom background color, for example. +func (dv *DiffView) BaseStyle(baseStyle lipgloss.Style) *DiffView { + dv.baseStyle = baseStyle + return dv +} + +// SyntaxHighlight sets whether to enable syntax highlighting in the DiffView. +func (dv *DiffView) SyntaxHighlight(highlight bool) *DiffView { + dv.highlight = highlight + return dv +} + +// Height sets the height of the DiffView. +func (dv *DiffView) Height(height int) *DiffView { + dv.height = height + return dv +} + +// Width sets the width of the DiffView. +func (dv *DiffView) Width(width int) *DiffView { + dv.width = width + return dv +} + +// String returns the string representation of the DiffView. +func (dv *DiffView) String() string { + if !dv.isComputed { + dv.compute() + } + if dv.err != nil { + return dv.err.Error() + } + return dv.unified.String() +} + +func (dv *DiffView) compute() { + dv.isComputed = true + dv.edits = myers.ComputeEdits( + dv.before.content, + dv.after.content, + ) + dv.unified, dv.err = udiff.ToUnifiedDiff( + dv.before.path, + dv.after.path, + dv.before.content, + dv.edits, + dv.contextLines, + ) +} diff --git a/internal/exp/diffview/diffview_test.go b/internal/exp/diffview/diffview_test.go new file mode 100644 index 0000000000000000000000000000000000000000..b04adacccc9e78461377dacc2c8c39a763c70c27 --- /dev/null +++ b/internal/exp/diffview/diffview_test.go @@ -0,0 +1,15 @@ +package diffview_test + +import ( + "testing" + + "github.com/charmbracelet/x/exp/golden" + "github.com/opencode-ai/opencode/internal/exp/diffview" +) + +func TestDefault(t *testing.T) { + dv := diffview.New(). + Before("test.txt", "This is the original content."). + After("test.txt", "This is the modified content.") + golden.RequireEqual(t, []byte(dv.String())) +} diff --git a/internal/exp/diffview/testdata/TestDefault.golden b/internal/exp/diffview/testdata/TestDefault.golden new file mode 100644 index 0000000000000000000000000000000000000000..2ef961a2191b5195f5dadbea21350e370ba5c426 --- /dev/null +++ b/internal/exp/diffview/testdata/TestDefault.golden @@ -0,0 +1,7 @@ +--- test.txt ++++ test.txt +@@ -1 +1 @@ +-This is the original content. +\ No newline at end of file ++This is the modified content. +\ No newline at end of file