on main help sign in
fix(diffview): fix behavior of tabs and add tab width setting
bac4f0aAndrey Nering
internal/exp/diffview/diffview.go+18
@@ -42,6 +42,7 @@ type DiffView struct {
42 xOffset int
42 xOffset int
43 yOffset int
43 yOffset int
44 style Style
44 style Style
45+ tabWidth int
45
46
46 isComputed bool
47 isComputed bool
47 err error
48 err error
@@ -63,6 +64,7 @@ func New() *DiffView {
63 layout: layoutUnified,
64 layout: layoutUnified,
64 contextLines: udiff.DefaultContextLines,
65 contextLines: udiff.DefaultContextLines,
65 lineNumbers: true,
66 lineNumbers: true,
67+ tabWidth: 8,
66 }
68 }
67 if lipgloss.HasDarkBackground(os.Stdin, os.Stdout) {
69 if lipgloss.HasDarkBackground(os.Stdin, os.Stdout) {
68 dv.style = DefaultDarkStyle
70 dv.style = DefaultDarkStyle
@@ -144,8 +146,16 @@ func (dv *DiffView) YOffset(yOffset int) *DiffView {
144 return dv
146 return dv
145}
147}
146
148
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+
147// String returns the string representation of the DiffView.
156// String returns the string representation of the DiffView.
148func (dv *DiffView) String() string {
157func (dv *DiffView) String() string {
158+ dv.replaceTabs()
149 if err := dv.computeDiff(); err != nil {
159 if err := dv.computeDiff(); err != nil {
150 return err.Error()
160 return err.Error()
151 }
161 }
@@ -177,6 +187,14 @@ func (dv *DiffView) String() string {
177 }
187 }
178}
188}
179
189
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+
180// computeDiff computes the differences between the "before" and "after" files.
198// computeDiff computes the differences between the "before" and "after" files.
181func (dv *DiffView) computeDiff() error {
199func (dv *DiffView) computeDiff() error {
182 if dv.isComputed {
200 if dv.isComputed {
internal/exp/diffview/diffview_test.go+21
@@ -29,6 +29,12 @@ var TestNarrowBefore string
29//go:embed testdata/TestNarrow.after
29//go:embed testdata/TestNarrow.after
30var TestNarrowAfter string
30var TestNarrowAfter string
31
31
32+//go:embed testdata/TestTabs.before
33+var TestTabsBefore string
34+
35+//go:embed testdata/TestTabs.after
36+var TestTabsAfter string
37+
32type (
38type (
33 TestFunc func(dv *diffview.DiffView) *diffview.DiffView
39 TestFunc func(dv *diffview.DiffView) *diffview.DiffView
34 TestFuncs map[string]TestFunc
40 TestFuncs map[string]TestFunc
@@ -137,6 +143,21 @@ func TestDiffView(t *testing.T) {
137 }
143 }
138}
144}
139
145
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+
140func TestDiffViewWidth(t *testing.T) {
161func TestDiffViewWidth(t *testing.T) {
141 for layoutName, layoutFunc := range LayoutFuncs {
162 for layoutName, layoutFunc := range LayoutFuncs {
142 t.Run(layoutName, func(t *testing.T) {
163 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+}