Detailed changes
@@ -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 {
@@ -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) {
@@ -0,0 +1,15 @@
+[48;2;71;118;255mĀ [m[38;2;77;76;87;48;2;71;118;255m ā¦[m[48;2;71;118;255mĀ [m[38;2;96;95;107;48;2;113;154;252m @@ -2,6 +2,7 @@ [m[48;2;113;154;252m [m[48;2;71;118;255mĀ [m[38;2;77;76;87;48;2;71;118;255m ā¦[m[48;2;71;118;255mĀ [m[38;2;96;95;107;48;2;113;154;252m [m[48;2;113;154;252m [m
+[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m 2[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m [m[48;2;241;239;239m [m[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m 2[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m [m[48;2;241;239;239m [m
+[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m 3[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m import ([m[48;2;241;239;239m [m[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m 3[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m import ([m[48;2;241;239;239m [m
+[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m 4[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m "fmt"[m[48;2;241;239;239m [m[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m 4[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m "fmt"[m[48;2;241;239;239m [m
+[48;2;223;219;221mĀ [m[48;2;223;219;221m [m[48;2;223;219;221mĀ [m[48;2;223;219;221m [m[48;2;223;219;221m [m[48;2;200;230;201mĀ [m[38;2;10;220;217;48;2;200;230;201m 5[m[48;2;200;230;201mĀ [m[38;2;10;220;217;48;2;232;245;233m+ [m[38;2;32;31;38;48;2;232;245;233m "strings"[m[48;2;232;245;233m [m
+[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m 5[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m )[m[48;2;241;239;239m [m[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m 6[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m )[m[48;2;241;239;239m [m
+[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m 6[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m [m[48;2;241;239;239m [m[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m 7[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m [m[48;2;241;239;239m [m
+[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m 7[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m func main() {[m[48;2;241;239;239m [m[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m 8[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m func main() {[m[48;2;241;239;239m [m
+[48;2;71;118;255mĀ [m[38;2;77;76;87;48;2;71;118;255m ā¦[m[48;2;71;118;255mĀ [m[38;2;96;95;107;48;2;113;154;252m @@ -9,5 +10,6 @@ [m[48;2;113;154;252m [m[48;2;71;118;255mĀ [m[38;2;77;76;87;48;2;71;118;255m ā¦[m[48;2;71;118;255mĀ [m[38;2;96;95;107;48;2;113;154;252m [m[48;2;113;154;252m [m
+[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m 9[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m }[m[48;2;241;239;239m [m[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m10[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m }[m[48;2;241;239;239m [m
+[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m10[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m [m[48;2;241;239;239m [m[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m11[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m [m[48;2;241;239;239m [m
+[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m11[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m func getContent() string {[m[48;2;241;239;239m [m[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m12[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m func getContent() string {[m[48;2;241;239;239m [m
+[48;2;255;205;210mĀ [m[38;2;255;56;139;48;2;255;205;210m12[m[48;2;255;205;210mĀ [m[38;2;255;56;139;48;2;255;235;238m- [m[38;2;32;31;38;48;2;255;235;238m return "Hello, world!"[m[48;2;255;235;238m [m[48;2;200;230;201mĀ [m[38;2;10;220;217;48;2;200;230;201m13[m[48;2;200;230;201mĀ [m[38;2;10;220;217;48;2;232;245;233m+ [m[38;2;32;31;38;48;2;232;245;233m content := strings.ToUpper("Hello, World!")[m[48;2;232;245;233m [m
+[48;2;223;219;221mĀ [m[48;2;223;219;221m [m[48;2;223;219;221mĀ [m[48;2;223;219;221m [m[48;2;223;219;221m [m[48;2;200;230;201mĀ [m[38;2;10;220;217;48;2;200;230;201m14[m[48;2;200;230;201mĀ [m[38;2;10;220;217;48;2;232;245;233m+ [m[38;2;32;31;38;48;2;232;245;233m return content[m[48;2;232;245;233m [m
+[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m13[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m }[m[48;2;241;239;239m [m[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m15[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m }[m[48;2;241;239;239m [m
@@ -0,0 +1,16 @@
+[48;2;71;118;255mĀ [m[38;2;77;76;87;48;2;71;118;255m ā¦[m[48;2;71;118;255mĀ [m[48;2;71;118;255mĀ [m[38;2;77;76;87;48;2;71;118;255m ā¦[m[48;2;71;118;255mĀ [m[38;2;96;95;107;48;2;113;154;252m @@ -2,6 +2,7 @@ [m[48;2;113;154;252m [m
+[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m 2[m[48;2;223;219;221mĀ [m[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m 2[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m [m[48;2;241;239;239m [m
+[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m 3[m[48;2;223;219;221mĀ [m[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m 3[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m import ([m[48;2;241;239;239m [m
+[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m 4[m[48;2;223;219;221mĀ [m[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m 4[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m "fmt"[m[48;2;241;239;239m [m
+[48;2;200;230;201mĀ [m[38;2;10;220;217;48;2;200;230;201m [m[48;2;200;230;201mĀ [m[48;2;200;230;201mĀ [m[38;2;10;220;217;48;2;200;230;201m 5[m[48;2;200;230;201mĀ [m[38;2;10;220;217;48;2;232;245;233m+ [m[38;2;32;31;38;48;2;232;245;233m "strings"[m[48;2;232;245;233m [m
+[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m 5[m[48;2;223;219;221mĀ [m[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m 6[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m )[m[48;2;241;239;239m [m
+[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m 6[m[48;2;223;219;221mĀ [m[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m 7[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m [m[48;2;241;239;239m [m
+[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m 7[m[48;2;223;219;221mĀ [m[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m 8[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m func main() {[m[48;2;241;239;239m [m
+[48;2;71;118;255mĀ [m[38;2;77;76;87;48;2;71;118;255m ā¦[m[48;2;71;118;255mĀ [m[48;2;71;118;255mĀ [m[38;2;77;76;87;48;2;71;118;255m ā¦[m[48;2;71;118;255mĀ [m[38;2;96;95;107;48;2;113;154;252m @@ -9,5 +10,6 @@ [m[48;2;113;154;252m [m
+[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m 9[m[48;2;223;219;221mĀ [m[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m10[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m }[m[48;2;241;239;239m [m
+[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m10[m[48;2;223;219;221mĀ [m[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m11[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m [m[48;2;241;239;239m [m
+[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m11[m[48;2;223;219;221mĀ [m[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m12[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m func getContent() string {[m[48;2;241;239;239m [m
+[48;2;255;205;210mĀ [m[38;2;255;56;139;48;2;255;205;210m12[m[48;2;255;205;210mĀ [m[48;2;255;205;210mĀ [m[38;2;255;56;139;48;2;255;205;210m [m[48;2;255;205;210mĀ [m[38;2;255;56;139;48;2;255;235;238m- [m[38;2;32;31;38;48;2;255;235;238m return "Hello, world!"[m[48;2;255;235;238m [m
+[48;2;200;230;201mĀ [m[38;2;10;220;217;48;2;200;230;201m [m[48;2;200;230;201mĀ [m[48;2;200;230;201mĀ [m[38;2;10;220;217;48;2;200;230;201m13[m[48;2;200;230;201mĀ [m[38;2;10;220;217;48;2;232;245;233m+ [m[38;2;32;31;38;48;2;232;245;233m content := strings.ToUpper("Hello, World!")[m[48;2;232;245;233m [m
+[48;2;200;230;201mĀ [m[38;2;10;220;217;48;2;200;230;201m [m[48;2;200;230;201mĀ [m[48;2;200;230;201mĀ [m[38;2;10;220;217;48;2;200;230;201m14[m[48;2;200;230;201mĀ [m[38;2;10;220;217;48;2;232;245;233m+ [m[38;2;32;31;38;48;2;232;245;233m return content[m[48;2;232;245;233m [m
+[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m13[m[48;2;223;219;221mĀ [m[48;2;223;219;221mĀ [m[38;2;58;57;67;48;2;223;219;221m15[m[48;2;223;219;221mĀ [m[38;2;32;31;38;48;2;241;239;239m }[m[48;2;241;239;239m [m
@@ -0,0 +1,15 @@
+package main
+
+import (
+ "fmt"
+ "strings"
+)
+
+func main() {
+ fmt.Println(getContent())
+}
+
+func getContent() string {
+ content := strings.ToUpper("Hello, World!")
+ return content
+}
@@ -0,0 +1,13 @@
+package main
+
+import (
+ "fmt"
+)
+
+func main() {
+ fmt.Println(getContent())
+}
+
+func getContent() string {
+ return "Hello, world!"
+}