From bac4f0ac1ad317586997bbf412e153900d411e8f Mon Sep 17 00:00:00 2001 From: Andrey Nering Date: Wed, 11 Jun 2025 15:44:08 -0300 Subject: [PATCH] fix(diffview): fix behavior of tabs and add tab width setting --- internal/exp/diffview/diffview.go | 18 ++++++++++++++++ internal/exp/diffview/diffview_test.go | 21 +++++++++++++++++++ .../testdata/TestDiffViewTabs/Split.golden | 15 +++++++++++++ .../testdata/TestDiffViewTabs/Unified.golden | 16 ++++++++++++++ internal/exp/diffview/testdata/TestTabs.after | 15 +++++++++++++ .../exp/diffview/testdata/TestTabs.before | 13 ++++++++++++ 6 files changed, 98 insertions(+) create mode 100644 internal/exp/diffview/testdata/TestDiffViewTabs/Split.golden create mode 100644 internal/exp/diffview/testdata/TestDiffViewTabs/Unified.golden create mode 100644 internal/exp/diffview/testdata/TestTabs.after create mode 100644 internal/exp/diffview/testdata/TestTabs.before diff --git a/internal/exp/diffview/diffview.go b/internal/exp/diffview/diffview.go index 41212b340c1a94bb188c374d9b989e14c708875a..6d2947bca534eb2ccc8466d6483ed6e5d3d48fe8 100644 --- a/internal/exp/diffview/diffview.go +++ b/internal/exp/diffview/diffview.go @@ -42,6 +42,7 @@ type DiffView struct { xOffset int yOffset int style Style + tabWidth int isComputed bool err error @@ -63,6 +64,7 @@ func New() *DiffView { layout: layoutUnified, contextLines: udiff.DefaultContextLines, lineNumbers: true, + tabWidth: 8, } if lipgloss.HasDarkBackground(os.Stdin, os.Stdout) { dv.style = DefaultDarkStyle @@ -144,8 +146,16 @@ func (dv *DiffView) YOffset(yOffset int) *DiffView { return dv } +// TabWidth sets the tab width. Only relevant for code that contains tabs, like +// Go code. +func (dv *DiffView) TabWidth(tabWidth int) *DiffView { + dv.tabWidth = tabWidth + return dv +} + // String returns the string representation of the DiffView. func (dv *DiffView) String() string { + dv.replaceTabs() if err := dv.computeDiff(); err != nil { return err.Error() } @@ -177,6 +187,14 @@ func (dv *DiffView) String() string { } } +// replaceTabs replaces tabs in the before and after file contents with spaces +// according to the specified tab width. +func (dv *DiffView) replaceTabs() { + spaces := strings.Repeat(" ", dv.tabWidth) + dv.before.content = strings.ReplaceAll(dv.before.content, "\t", spaces) + dv.after.content = strings.ReplaceAll(dv.after.content, "\t", spaces) +} + // computeDiff computes the differences between the "before" and "after" files. func (dv *DiffView) computeDiff() error { if dv.isComputed { diff --git a/internal/exp/diffview/diffview_test.go b/internal/exp/diffview/diffview_test.go index d9f55892024ef4a9da00b0f5586e3f50135e27d9..4feb40a68c140d75a143ed37535573fb05d9fbdd 100644 --- a/internal/exp/diffview/diffview_test.go +++ b/internal/exp/diffview/diffview_test.go @@ -29,6 +29,12 @@ var TestNarrowBefore string //go:embed testdata/TestNarrow.after var TestNarrowAfter string +//go:embed testdata/TestTabs.before +var TestTabsBefore string + +//go:embed testdata/TestTabs.after +var TestTabsAfter string + type ( TestFunc func(dv *diffview.DiffView) *diffview.DiffView TestFuncs map[string]TestFunc @@ -137,6 +143,21 @@ func TestDiffView(t *testing.T) { } } +func TestDiffViewTabs(t *testing.T) { + for layoutName, layoutFunc := range LayoutFuncs { + t.Run(layoutName, func(t *testing.T) { + dv := diffview.New(). + Before("main.go", TestTabsBefore). + After("main.go", TestTabsAfter). + Style(diffview.DefaultLightStyle) + dv = layoutFunc(dv) + + output := dv.String() + golden.RequireEqual(t, []byte(output)) + }) + } +} + func TestDiffViewWidth(t *testing.T) { for layoutName, layoutFunc := range LayoutFuncs { t.Run(layoutName, func(t *testing.T) { diff --git a/internal/exp/diffview/testdata/TestDiffViewTabs/Split.golden b/internal/exp/diffview/testdata/TestDiffViewTabs/Split.golden new file mode 100644 index 0000000000000000000000000000000000000000..47ec287f0894851ff20b75d5f267e9733e30b497 --- /dev/null +++ b/internal/exp/diffview/testdata/TestDiffViewTabs/Split.golden @@ -0,0 +1,15 @@ +  …  @@ -2,6 +2,7 @@    …    +  2     2    +  3  import (   3  import (  +  4  "fmt"   4  "fmt"  +       5 +  "strings"  +  5  )   6  )  +  6     7    +  7  func main() {   8  func main() {  +  …  @@ -9,5 +10,6 @@    …    +  9  }  10  }  + 10    11    + 11  func getContent() string {  12  func getContent() string {  + 12 -  return "Hello, world!"  13 +  content := strings.ToUpper("Hello, World!")  +      14 +  return content  + 13  }  15  }  \ No newline at end of file diff --git a/internal/exp/diffview/testdata/TestDiffViewTabs/Unified.golden b/internal/exp/diffview/testdata/TestDiffViewTabs/Unified.golden new file mode 100644 index 0000000000000000000000000000000000000000..1f71e8a1bfad8219360784d1ef2893d2a86f3b41 --- /dev/null +++ b/internal/exp/diffview/testdata/TestDiffViewTabs/Unified.golden @@ -0,0 +1,16 @@ +  …   …  @@ -2,6 +2,7 @@   +  2   2    +  3   3  import (  +  4   4  "fmt"  +     5 +  "strings"  +  5   6  )  +  6   7    +  7   8  func main() {  +  …   …  @@ -9,5 +10,6 @@   +  9  10  }  + 10  11    + 11  12  func getContent() string {  + 12    -  return "Hello, world!"  +    13 +  content := strings.ToUpper("Hello, World!")  +    14 +  return content  + 13  15  }  \ No newline at end of file diff --git a/internal/exp/diffview/testdata/TestTabs.after b/internal/exp/diffview/testdata/TestTabs.after new file mode 100644 index 0000000000000000000000000000000000000000..26b88f61224561d442b23473ce6b9daaf761aee9 --- /dev/null +++ b/internal/exp/diffview/testdata/TestTabs.after @@ -0,0 +1,15 @@ +package main + +import ( + "fmt" + "strings" +) + +func main() { + fmt.Println(getContent()) +} + +func getContent() string { + content := strings.ToUpper("Hello, World!") + return content +} diff --git a/internal/exp/diffview/testdata/TestTabs.before b/internal/exp/diffview/testdata/TestTabs.before new file mode 100644 index 0000000000000000000000000000000000000000..72d7ebc2699bfd859311b85e3193e6c87013672f --- /dev/null +++ b/internal/exp/diffview/testdata/TestTabs.before @@ -0,0 +1,13 @@ +package main + +import ( + "fmt" +) + +func main() { + fmt.Println(getContent()) +} + +func getContent() string { + return "Hello, world!" +}