on main help sign in
fix(diffview): add fixes + more extensive testing for width handling
commit0da19d841fbaccac813708d940d5ccacbc85cd00 authorAndrey Nering <andreynering@users.noreply.github.com> dateTue 10 Jun 2025, 15:01 (1 year ago) parent371662c
4 files changed+83−9
internal/exp/diffview/Taskfile.yaml+20 internal/exp/diffview/diffview.go+17−9 internal/exp/diffview/diffview_test.go+31 internal/exp/diffview/util.go+15
internal/exp/diffview/Taskfile.yaml+20
@@ -20,3 +20,23 @@ tasks:
2020 - for: sources
2121 cmd: echo && echo "------- {{.ITEM}} -------" && echo && cat {{.ITEM}}
2222 silent: true
23+
24+ test:print:width:unified:
25+ desc: Print golden files for debugging
26+ method: none
27+ sources:
28+ - ./testdata/TestDiffViewWidth/Unified/LightMode/*.golden
29+ cmds:
30+ - for: sources
31+ cmd: echo && echo "------- {{.ITEM}} -------" && echo && cat {{.ITEM}}
32+ silent: true
33+
34+ test:print:width:split:
35+ desc: Print golden files for debugging
36+ method: none
37+ sources:
38+ - ./testdata/TestDiffViewWidth/Split/LightMode/*.golden
39+ cmds:
40+ - for: sources
41+ cmd: echo && echo "------- {{.ITEM}} -------" && echo && cat {{.ITEM}}
42+ silent: true
internal/exp/diffview/diffview.go+17−9
@@ -49,7 +49,8 @@ type DiffView struct {
4949 splitHunks []splitHunk
5050
5151 codeWidth int
52− fullCodeWidth int // with leading symbols
52+ fullCodeWidth int // with leading symbols
53+ extraColOnAfter bool // add extra column on after panel
5354 beforeNumDigits int
5455 afterNumDigits int
5556}
@@ -144,11 +145,16 @@ func (dv *DiffView) String() string {
144145 dv.resizeCodeWidth()
145146 }
146147
148+ style := lipgloss.NewStyle()
149+ if dv.width > 0 {
150+ style = style.MaxWidth(dv.width)
151+ }
152+
147153 switch dv.layout {
148154 case layoutUnified:
149− return dv.renderUnified()
155+ return style.Render(strings.TrimSuffix(dv.renderUnified(), "\n"))
150156 case layoutSplit:
151− return dv.renderSplit()
157+ return style.Render(strings.TrimSuffix(dv.renderSplit(), "\n"))
152158 default:
153159 panic("unknown diffview layout")
154160 }
@@ -268,7 +274,9 @@ func (dv *DiffView) resizeCodeWidth() {
268274 case layoutUnified:
269275 dv.codeWidth = dv.width - fullNumWidth - leadingSymbolsSize
270276 case layoutSplit:
271− dv.codeWidth = (dv.width - fullNumWidth - leadingSymbolsSize*2) / 2
277+ remainingWidth := dv.width - fullNumWidth - leadingSymbolsSize*2
278+ dv.codeWidth = remainingWidth / 2
279+ dv.extraColOnAfter = isOdd(remainingWidth)
272280 }
273281
274282 dv.fullCodeWidth = dv.codeWidth + leadingSymbolsSize
@@ -340,7 +348,7 @@ func (dv *DiffView) renderSplit() string {
340348 if dv.lineNumbers {
341349 b.WriteString(dv.style.DividerLine.LineNumber.Render(pad("…", dv.afterNumDigits)))
342350 }
343− b.WriteString(dv.style.DividerLine.Code.Width(dv.fullCodeWidth).Render(" "))
351+ b.WriteString(dv.style.DividerLine.Code.Width(dv.fullCodeWidth + btoi(dv.extraColOnAfter)).Render(" "))
344352 b.WriteRune('\n')
345353
346354 beforeLine := h.fromLine
@@ -355,7 +363,7 @@ func (dv *DiffView) renderSplit() string {
355363 }
356364 if l.after != nil {
357365 afterContent = strings.TrimSuffix(l.after.Content, "\n")
358− afterContent = ansi.Truncate(afterContent, dv.codeWidth, "…")
366+ afterContent = ansi.Truncate(afterContent, dv.codeWidth+btoi(dv.extraColOnAfter), "…")
359367 }
360368
361369 switch {
@@ -384,19 +392,19 @@ func (dv *DiffView) renderSplit() string {
384392 if dv.lineNumbers {
385393 b.WriteString(dv.style.MissingLine.LineNumber.Render(pad(" ", dv.afterNumDigits)))
386394 }
387− b.WriteString(dv.style.MissingLine.Code.Width(dv.fullCodeWidth).Render(" "))
395+ b.WriteString(dv.style.MissingLine.Code.Width(dv.fullCodeWidth + btoi(dv.extraColOnAfter)).Render(" "))
388396 case l.after.Kind == udiff.Equal:
389397 if dv.lineNumbers {
390398 b.WriteString(dv.style.EqualLine.LineNumber.Render(pad(afterLine, dv.afterNumDigits)))
391399 }
392− b.WriteString(dv.style.EqualLine.Code.Width(dv.fullCodeWidth).Render(" " + afterContent))
400+ b.WriteString(dv.style.EqualLine.Code.Width(dv.fullCodeWidth + btoi(dv.extraColOnAfter)).Render(" " + afterContent))
393401 afterLine++
394402 case l.after.Kind == udiff.Insert:
395403 if dv.lineNumbers {
396404 b.WriteString(dv.style.InsertLine.LineNumber.Render(pad(afterLine, dv.afterNumDigits)))
397405 }
398406 b.WriteString(dv.style.InsertLine.Symbol.Render("+ "))
399− b.WriteString(dv.style.InsertLine.Code.Width(dv.codeWidth).Render(afterContent))
407+ b.WriteString(dv.style.InsertLine.Code.Width(dv.codeWidth + btoi(dv.extraColOnAfter)).Render(afterContent))
400408 afterLine++
401409 }
402410
internal/exp/diffview/diffview_test.go+31
@@ -2,6 +2,7 @@ package diffview_test
22
33import (
44 _ "embed"
5+ "fmt"
56 "strings"
67 "testing"
78
@@ -136,6 +137,36 @@ func TestDiffView(t *testing.T) {
136137 }
137138}
138139
140+func TestDiffViewWidth(t *testing.T) {
141+ for layoutName, layoutFunc := range LayoutFuncs {
142+ t.Run(layoutName, func(t *testing.T) {
143+ for themeName, themeFunc := range ThemeFuncs {
144+ t.Run(themeName, func(t *testing.T) {
145+ for width := 1; width <= 110; width++ {
146+ if layoutName == "Unified" && width > 60 {
147+ continue
148+ }
149+
150+ t.Run(fmt.Sprintf("WidthOf%03d", width), func(t *testing.T) {
151+ dv := diffview.New().
152+ Before("main.go", TestMultipleHunksBefore).
153+ After("main.go", TestMultipleHunksAfter).
154+ Width(width)
155+ dv = layoutFunc(dv)
156+ dv = themeFunc(dv)
157+
158+ output := dv.String()
159+ golden.RequireEqual(t, []byte(output))
160+
161+ assertLineWidth(t, width, output)
162+ })
163+ }
164+ })
165+ }
166+ })
167+ }
168+}
169+
139170func assertLineWidth(t *testing.T, expected int, output string) {
140171 var lineWidth int
141172 for line := range strings.SplitSeq(output, "\n") {
internal/exp/diffview/util.go+15
@@ -15,3 +15,18 @@ func pad(v any, width int) string {
1515 }
1616 return strings.Repeat(" ", width-w) + s
1717}
18+
19+func isEven(n int) bool {
20+ return n%2 == 0
21+}
22+
23+func isOdd(n int) bool {
24+ return !isEven(n)
25+}
26+
27+func btoi(b bool) int {
28+ if b {
29+ return 1
30+ }
31+ return 0
32+}