on main help sign in
fix(diffview): fix behavior of tabs and add tab width setting
commitbac4f0ac1ad317586997bbf412e153900d411e8f authorAndrey Nering <andreynering@users.noreply.github.com> dateWed 11 Jun 2025, 15:44 (1 year ago) parentc5bf830
6 files changed+98
internal/exp/diffview/diffview.go+18 internal/exp/diffview/diffview_test.go+21 internal/exp/diffview/testdata/TestDiffViewTabs/Split.golden+15 internal/exp/diffview/testdata/TestDiffViewTabs/Unified.golden+16 internal/exp/diffview/testdata/TestTabs.after+15 internal/exp/diffview/testdata/TestTabs.before+13
internal/exp/diffview/diffview.go+18
@@ -42,6 +42,7 @@ type DiffView struct {
4242 xOffset int
4343 yOffset int
4444 style Style
45+ tabWidth int
4546
4647 isComputed bool
4748 err error
@@ -63,6 +64,7 @@ func New() *DiffView {
6364 layout: layoutUnified,
6465 contextLines: udiff.DefaultContextLines,
6566 lineNumbers: true,
67+ tabWidth: 8,
6668 }
6769 if lipgloss.HasDarkBackground(os.Stdin, os.Stdout) {
6870 dv.style = DefaultDarkStyle
@@ -144,8 +146,16 @@ func (dv *DiffView) YOffset(yOffset int) *DiffView {
144146 return dv
145147}
146148
149+// TabWidth sets the tab width. Only relevant for code that contains tabs, like
150+// Go code.
151+func (dv *DiffView) TabWidth(tabWidth int) *DiffView {
152+ dv.tabWidth = tabWidth
153+ return dv
154+}
155+
147156// String returns the string representation of the DiffView.
148157func (dv *DiffView) String() string {
158+ dv.replaceTabs()
149159 if err := dv.computeDiff(); err != nil {
150160 return err.Error()
151161 }
@@ -177,6 +187,14 @@ func (dv *DiffView) String() string {
177187 }
178188}
179189
190+// replaceTabs replaces tabs in the before and after file contents with spaces
191+// according to the specified tab width.
192+func (dv *DiffView) replaceTabs() {
193+ spaces := strings.Repeat(" ", dv.tabWidth)
194+ dv.before.content = strings.ReplaceAll(dv.before.content, "\t", spaces)
195+ dv.after.content = strings.ReplaceAll(dv.after.content, "\t", spaces)
196+}
197+
180198// computeDiff computes the differences between the "before" and "after" files.
181199func (dv *DiffView) computeDiff() error {
182200 if dv.isComputed {
internal/exp/diffview/diffview_test.go+21
@@ -29,6 +29,12 @@ var TestNarrowBefore string
2929//go:embed testdata/TestNarrow.after
3030var TestNarrowAfter string
3131
32+//go:embed testdata/TestTabs.before
33+var TestTabsBefore string
34+
35+//go:embed testdata/TestTabs.after
36+var TestTabsAfter string
37+
3238type (
3339 TestFunc func(dv *diffview.DiffView) *diffview.DiffView
3440 TestFuncs map[string]TestFunc
@@ -137,6 +143,21 @@ func TestDiffView(t *testing.T) {
137143 }
138144}
139145
146+func TestDiffViewTabs(t *testing.T) {
147+ for layoutName, layoutFunc := range LayoutFuncs {
148+ t.Run(layoutName, func(t *testing.T) {
149+ dv := diffview.New().
150+ Before("main.go", TestTabsBefore).
151+ After("main.go", TestTabsAfter).
152+ Style(diffview.DefaultLightStyle)
153+ dv = layoutFunc(dv)
154+
155+ output := dv.String()
156+ golden.RequireEqual(t, []byte(output))
157+ })
158+ }
159+}
160+
140161func TestDiffViewWidth(t *testing.T) {
141162 for layoutName, layoutFunc := range LayoutFuncs {
142163 t.Run(layoutName, func(t *testing.T) {
internal/exp/diffview/testdata/TestDiffViewTabs/Split.goldennew file+15
@@ -0,0 +1,15 @@
1+  …  @@ -2,6 +2,7 @@    …   
2+  2     2   
3+  3  import (   3  import ( 
4+  4  "fmt"   4  "fmt" 
5+       5 +  "strings" 
6+  5  )   6  ) 
7+  6     7   
8+  7  func main() {   8  func main() { 
9+  …  @@ -9,5 +10,6 @@    …   
10+  9  }  10  } 
11+ 10    11   
12+ 11  func getContent() string {  12  func getContent() string { 
13+ 12 -  return "Hello, world!"  13 +  content := strings.ToUpper("Hello, World!") 
14+      14 +  return content 
15+ 13  }  15  } 
internal/exp/diffview/testdata/TestDiffViewTabs/Unified.goldennew file+16
@@ -0,0 +1,16 @@
1+  …   …  @@ -2,6 +2,7 @@  
2+  2   2   
3+  3   3  import ( 
4+  4   4  "fmt" 
5+     5 +  "strings" 
6+  5   6  ) 
7+  6   7   
8+  7   8  func main() { 
9+  …   …  @@ -9,5 +10,6 @@  
10+  9  10  } 
11+ 10  11   
12+ 11  12  func getContent() string { 
13+ 12    -  return "Hello, world!" 
14+    13 +  content := strings.ToUpper("Hello, World!") 
15+    14 +  return content 
16+ 13  15  } 
internal/exp/diffview/testdata/TestTabs.afternew file+15
@@ -0,0 +1,15 @@
1+package main
2+
3+import (
4+ "fmt"
5+ "strings"
6+)
7+
8+func main() {
9+ fmt.Println(getContent())
10+}
11+
12+func getContent() string {
13+ content := strings.ToUpper("Hello, World!")
14+ return content
15+}
internal/exp/diffview/testdata/TestTabs.beforenew file+13
@@ -0,0 +1,13 @@
1+package main
2+
3+import (
4+ "fmt"
5+)
6+
7+func main() {
8+ fmt.Println(getContent())
9+}
10+
11+func getContent() string {
12+ return "Hello, world!"
13+}