on main help sign in
fix(diffview): add fixes + more extensive testing for width handling
0da19d8Andrey Nering
internal/exp/diffview/Taskfile.yaml+20
@@ -20,3 +20,23 @@ tasks:
20 - for: sources
20 - for: sources
21 cmd: echo && echo "------- {{.ITEM}} -------" && echo && cat {{.ITEM}}
21 cmd: echo && echo "------- {{.ITEM}} -------" && echo && cat {{.ITEM}}
22 silent: true
22 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 {
49 splitHunks []splitHunk
49 splitHunks []splitHunk
50
50
51 codeWidth int
51 codeWidth int
52− fullCodeWidth int // with leading symbols
52+ fullCodeWidth int // with leading symbols
53+ extraColOnAfter bool // add extra column on after panel
53 beforeNumDigits int
54 beforeNumDigits int
54 afterNumDigits int
55 afterNumDigits int
55}
56}
@@ -144,11 +145,16 @@ func (dv *DiffView) String() string {
144 dv.resizeCodeWidth()
145 dv.resizeCodeWidth()
145 }
146 }
146
147
148+ style := lipgloss.NewStyle()
149+ if dv.width > 0 {
150+ style = style.MaxWidth(dv.width)
151+ }
152+
147 switch dv.layout {
153 switch dv.layout {
148 case layoutUnified:
154 case layoutUnified:
149− return dv.renderUnified()
155+ return style.Render(strings.TrimSuffix(dv.renderUnified(), "\n"))
150 case layoutSplit:
156 case layoutSplit:
151− return dv.renderSplit()
157+ return style.Render(strings.TrimSuffix(dv.renderSplit(), "\n"))
152 default:
158 default:
153 panic("unknown diffview layout")
159 panic("unknown diffview layout")
154 }
160 }
@@ -268,7 +274,9 @@ func (dv *DiffView) resizeCodeWidth() {
268 case layoutUnified:
274 case layoutUnified:
269 dv.codeWidth = dv.width - fullNumWidth - leadingSymbolsSize
275 dv.codeWidth = dv.width - fullNumWidth - leadingSymbolsSize
270 case layoutSplit:
276 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)
272 }
280 }
273
281
274 dv.fullCodeWidth = dv.codeWidth + leadingSymbolsSize
282 dv.fullCodeWidth = dv.codeWidth + leadingSymbolsSize
@@ -340,7 +348,7 @@ func (dv *DiffView) renderSplit() string {
340 if dv.lineNumbers {
348 if dv.lineNumbers {
341 b.WriteString(dv.style.DividerLine.LineNumber.Render(pad("…", dv.afterNumDigits)))
349 b.WriteString(dv.style.DividerLine.LineNumber.Render(pad("…", dv.afterNumDigits)))
342 }
350 }
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(" "))
344 b.WriteRune('\n')
352 b.WriteRune('\n')
345
353
346 beforeLine := h.fromLine
354 beforeLine := h.fromLine
@@ -355,7 +363,7 @@ func (dv *DiffView) renderSplit() string {
355 }
363 }
356 if l.after != nil {
364 if l.after != nil {
357 afterContent = strings.TrimSuffix(l.after.Content, "\n")
365 afterContent = strings.TrimSuffix(l.after.Content, "\n")
358− afterContent = ansi.Truncate(afterContent, dv.codeWidth, "…")
366+ afterContent = ansi.Truncate(afterContent, dv.codeWidth+btoi(dv.extraColOnAfter), "…")
359 }
367 }
360
368
361 switch {
369 switch {
@@ -384,19 +392,19 @@ func (dv *DiffView) renderSplit() string {
384 if dv.lineNumbers {
392 if dv.lineNumbers {
385 b.WriteString(dv.style.MissingLine.LineNumber.Render(pad(" ", dv.afterNumDigits)))
393 b.WriteString(dv.style.MissingLine.LineNumber.Render(pad(" ", dv.afterNumDigits)))
386 }
394 }
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(" "))
388 case l.after.Kind == udiff.Equal:
396 case l.after.Kind == udiff.Equal:
389 if dv.lineNumbers {
397 if dv.lineNumbers {
390 b.WriteString(dv.style.EqualLine.LineNumber.Render(pad(afterLine, dv.afterNumDigits)))
398 b.WriteString(dv.style.EqualLine.LineNumber.Render(pad(afterLine, dv.afterNumDigits)))
391 }
399 }
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))
393 afterLine++
401 afterLine++
394 case l.after.Kind == udiff.Insert:
402 case l.after.Kind == udiff.Insert:
395 if dv.lineNumbers {
403 if dv.lineNumbers {
396 b.WriteString(dv.style.InsertLine.LineNumber.Render(pad(afterLine, dv.afterNumDigits)))
404 b.WriteString(dv.style.InsertLine.LineNumber.Render(pad(afterLine, dv.afterNumDigits)))
397 }
405 }
398 b.WriteString(dv.style.InsertLine.Symbol.Render("+ "))
406 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))
400 afterLine++
408 afterLine++
401 }
409 }
402
410
internal/exp/diffview/diffview_test.go+31
@@ -2,6 +2,7 @@ package diffview_test
2
2
3import (
3import (
4 _ "embed"
4 _ "embed"
5+ "fmt"
5 "strings"
6 "strings"
6 "testing"
7 "testing"
7
8
@@ -136,6 +137,36 @@ func TestDiffView(t *testing.T) {
136 }
137 }
137}
138}
138
139
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+
139func assertLineWidth(t *testing.T, expected int, output string) {
170func assertLineWidth(t *testing.T, expected int, output string) {
140 var lineWidth int
171 var lineWidth int
141 for line := range strings.SplitSeq(output, "\n") {
172 for line := range strings.SplitSeq(output, "\n") {
internal/exp/diffview/util.go+15
@@ -15,3 +15,18 @@ func pad(v any, width int) string {
15 }
15 }
16 return strings.Repeat(" ", width-w) + s
16 return strings.Repeat(" ", width-w) + s
17}
17}
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+}