From 789a6b314f968a9a4c30e8794c227fb7361d01fe Mon Sep 17 00:00:00 2001 From: Andrey Nering Date: Wed, 11 Jun 2025 11:42:51 -0300 Subject: [PATCH] feat(diffview): implement ability to set horizontal offset --- internal/exp/diffview/Taskfile.yaml | 20 ++++++++++++++++++ internal/exp/diffview/diffview.go | 29 +++++++++++++++++++------- internal/exp/diffview/diffview_test.go | 23 ++++++++++++++++++++ internal/exp/diffview/util.go | 7 +++++++ 4 files changed, 72 insertions(+), 7 deletions(-) diff --git a/internal/exp/diffview/Taskfile.yaml b/internal/exp/diffview/Taskfile.yaml index be40107e348a340eadffc7d7095c9535330faeca..6dc8d477471b87498eb5365389bdf8f02458f406 100644 --- a/internal/exp/diffview/Taskfile.yaml +++ b/internal/exp/diffview/Taskfile.yaml @@ -60,3 +60,23 @@ tasks: - for: sources cmd: echo && echo "------- {{.ITEM}} -------" && echo && cat {{.ITEM}} silent: true + + test:print:xoffset:unified: + desc: Print golden files for debugging + method: none + sources: + - ./testdata/TestDiffViewXOffset/Unified/*.golden + cmds: + - for: sources + cmd: echo && echo "------- {{.ITEM}} -------" && echo && cat {{.ITEM}} + silent: true + + test:print:xoffset:split: + desc: Print golden files for debugging + method: none + sources: + - ./testdata/TestDiffViewXOffset/Split/*.golden + cmds: + - for: sources + cmd: echo && echo "------- {{.ITEM}} -------" && echo && cat {{.ITEM}} + silent: true diff --git a/internal/exp/diffview/diffview.go b/internal/exp/diffview/diffview.go index e273094851e10b6d7b596f0eccd364348fae809f..b5111692cdbda89b87663f412afcd82aba1dcf4f 100644 --- a/internal/exp/diffview/diffview.go +++ b/internal/exp/diffview/diffview.go @@ -39,6 +39,7 @@ type DiffView struct { highlight bool height int width int + xOffset int style Style isComputed bool @@ -130,6 +131,12 @@ func (dv *DiffView) Width(width int) *DiffView { return dv } +// XOffset sets the horizontal offset for the DiffView. +func (dv *DiffView) XOffset(xOffset int) *DiffView { + dv.xOffset = xOffset + return dv +} + // String returns the string representation of the DiffView. func (dv *DiffView) String() string { if err := dv.computeDiff(); err != nil { @@ -323,8 +330,11 @@ outer: } content := strings.TrimSuffix(l.Content, "\n") + content = ansi.GraphemeWidth.Cut(content, dv.xOffset, len(content)) content = ansi.Truncate(content, dv.codeWidth, "…") + leadingEllipsis := dv.xOffset > 0 && strings.TrimSpace(content) != "" + switch l.Kind { case udiff.Equal: if dv.lineNumbers { @@ -332,7 +342,7 @@ outer: b.WriteString(dv.style.EqualLine.LineNumber.Render(pad(afterLine, dv.afterNumDigits))) } b.WriteString(fullContentStyle.Render( - dv.style.EqualLine.Code.Width(dv.fullCodeWidth).Render(" " + content), + dv.style.EqualLine.Code.Width(dv.fullCodeWidth).Render(ternary(leadingEllipsis, " …", " ") + content), )) beforeLine++ afterLine++ @@ -342,7 +352,7 @@ outer: b.WriteString(dv.style.InsertLine.LineNumber.Render(pad(afterLine, dv.afterNumDigits))) } b.WriteString(fullContentStyle.Render( - dv.style.InsertLine.Symbol.Render("+ ") + + dv.style.InsertLine.Symbol.Render(ternary(leadingEllipsis, "+…", "+ ")) + dv.style.InsertLine.Code.Width(dv.codeWidth).Render(content), )) afterLine++ @@ -352,7 +362,7 @@ outer: b.WriteString(dv.style.DeleteLine.LineNumber.Render(pad(" ", dv.afterNumDigits))) } b.WriteString(fullContentStyle.Render( - dv.style.DeleteLine.Symbol.Render("- ") + + dv.style.DeleteLine.Symbol.Render(ternary(leadingEllipsis, "-…", "- ")) + dv.style.DeleteLine.Code.Width(dv.codeWidth).Render(content), )) beforeLine++ @@ -427,13 +437,18 @@ outer: var afterContent string if l.before != nil { beforeContent = strings.TrimSuffix(l.before.Content, "\n") + beforeContent = ansi.GraphemeWidth.Cut(beforeContent, dv.xOffset, len(beforeContent)) beforeContent = ansi.Truncate(beforeContent, dv.codeWidth, "…") } if l.after != nil { afterContent = strings.TrimSuffix(l.after.Content, "\n") + afterContent = ansi.GraphemeWidth.Cut(afterContent, dv.xOffset, len(afterContent)) afterContent = ansi.Truncate(afterContent, dv.codeWidth+btoi(dv.extraColOnAfter), "…") } + leadingBeforeEllipsis := dv.xOffset > 0 && strings.TrimSpace(beforeContent) != "" + leadingAfterEllipsis := dv.xOffset > 0 && strings.TrimSpace(afterContent) != "" + switch { case l.before == nil: if dv.lineNumbers { @@ -447,7 +462,7 @@ outer: b.WriteString(dv.style.EqualLine.LineNumber.Render(pad(beforeLine, dv.beforeNumDigits))) } b.WriteString(beforeFullContentStyle.Render( - dv.style.EqualLine.Code.Width(dv.fullCodeWidth).Render(" " + beforeContent), + dv.style.EqualLine.Code.Width(dv.fullCodeWidth).Render(ternary(leadingBeforeEllipsis, " …", " ") + beforeContent), )) beforeLine++ case l.before.Kind == udiff.Delete: @@ -455,7 +470,7 @@ outer: b.WriteString(dv.style.DeleteLine.LineNumber.Render(pad(beforeLine, dv.beforeNumDigits))) } b.WriteString(beforeFullContentStyle.Render( - dv.style.DeleteLine.Symbol.Render("- ") + + dv.style.DeleteLine.Symbol.Render(ternary(leadingBeforeEllipsis, "-…", "- ")) + dv.style.DeleteLine.Code.Width(dv.codeWidth).Render(beforeContent), )) beforeLine++ @@ -474,7 +489,7 @@ outer: b.WriteString(dv.style.EqualLine.LineNumber.Render(pad(afterLine, dv.afterNumDigits))) } b.WriteString(afterFullContentStyle.Render( - dv.style.EqualLine.Code.Width(dv.fullCodeWidth + btoi(dv.extraColOnAfter)).Render(" " + afterContent), + dv.style.EqualLine.Code.Width(dv.fullCodeWidth + btoi(dv.extraColOnAfter)).Render(ternary(leadingAfterEllipsis, " …", " ") + afterContent), )) afterLine++ case l.after.Kind == udiff.Insert: @@ -482,7 +497,7 @@ outer: b.WriteString(dv.style.InsertLine.LineNumber.Render(pad(afterLine, dv.afterNumDigits))) } b.WriteString(afterFullContentStyle.Render( - dv.style.InsertLine.Symbol.Render("+ ") + + dv.style.InsertLine.Symbol.Render(ternary(leadingAfterEllipsis, "+…", "+ ")) + dv.style.InsertLine.Code.Width(dv.codeWidth+btoi(dv.extraColOnAfter)).Render(afterContent), )) afterLine++ diff --git a/internal/exp/diffview/diffview_test.go b/internal/exp/diffview/diffview_test.go index fbf2661ccd269ed71aa40d46333ba5594df9078d..6d68a46d197630fecf01204841aa83e7e87e39df 100644 --- a/internal/exp/diffview/diffview_test.go +++ b/internal/exp/diffview/diffview_test.go @@ -193,6 +193,29 @@ func TestDiffViewHeight(t *testing.T) { } } +func TestDiffViewXOffset(t *testing.T) { + for layoutName, layoutFunc := range LayoutFuncs { + t.Run(layoutName, func(t *testing.T) { + for xOffset := range 21 { + t.Run(fmt.Sprintf("XOffsetOf%02d", xOffset), func(t *testing.T) { + dv := diffview.New(). + Before("main.go", TestDefaultBefore). + After("main.go", TestDefaultAfter). + Style(diffview.DefaultLightStyle). + Width(60). + XOffset(xOffset) + dv = layoutFunc(dv) + + output := dv.String() + golden.RequireEqual(t, []byte(output)) + + assertLineWidth(t, 60, output) + }) + } + }) + } +} + func assertLineWidth(t *testing.T, expected int, output string) { var lineWidth int for line := range strings.SplitSeq(output, "\n") { diff --git a/internal/exp/diffview/util.go b/internal/exp/diffview/util.go index 7ad3b880135216e668225a83859ff4866a7a2640..0ab35b16a5b9fb877b823784d8a4393a2ce4f650 100644 --- a/internal/exp/diffview/util.go +++ b/internal/exp/diffview/util.go @@ -30,3 +30,10 @@ func btoi(b bool) int { } return 0 } + +func ternary[T any](cond bool, t, f T) T { + if cond { + return t + } + return f +}