@@ -2,13 +2,13 @@ package diffview
22
33import (
44 "os"
5+ "strconv"
56 "strings"
67
78 "github.com/aymanbagabas/go-udiff"
89 "github.com/aymanbagabas/go-udiff/myers"
910 "github.com/charmbracelet/lipgloss/v2"
1011 "github.com/charmbracelet/x/ansi"
11− "github.com/charmbracelet/x/exp/charmtone"
1212)
1313
1414const leadingSymbolsSize = 2
@@ -25,71 +25,13 @@ const (
2525 layoutSplit
2626)
2727
28−type LineStyle struct {
29− Symbol lipgloss.Style
30− Code lipgloss.Style
31−}
32−
33−type Style struct {
34− EqualLine LineStyle
35− InsertLine LineStyle
36− DeleteLine LineStyle
37−}
38−
39−var DefaultLightStyle = Style{
40− EqualLine: LineStyle{
41− Code: lipgloss.NewStyle().
42− Foreground(charmtone.Pepper).
43− Background(charmtone.Salt),
44− },
45− InsertLine: LineStyle{
46− Symbol: lipgloss.NewStyle().
47− Foreground(charmtone.Turtle).
48− Background(lipgloss.Color("#e8f5e9")),
49− Code: lipgloss.NewStyle().
50− Foreground(charmtone.Pepper).
51− Background(lipgloss.Color("#e8f5e9")),
52− },
53− DeleteLine: LineStyle{
54− Symbol: lipgloss.NewStyle().
55− Foreground(charmtone.Cherry).
56− Background(lipgloss.Color("#ffebee")),
57− Code: lipgloss.NewStyle().
58− Foreground(charmtone.Pepper).
59− Background(lipgloss.Color("#ffebee")),
60− },
61−}
62−
63−var DefaultDarkStyle = Style{
64− EqualLine: LineStyle{
65− Code: lipgloss.NewStyle().
66− Foreground(charmtone.Salt).
67− Background(charmtone.Pepper),
68− },
69− InsertLine: LineStyle{
70− Symbol: lipgloss.NewStyle().
71− Foreground(charmtone.Turtle).
72− Background(lipgloss.Color("#303a30")),
73− Code: lipgloss.NewStyle().
74− Foreground(charmtone.Salt).
75− Background(lipgloss.Color("#303a30")),
76− },
77− DeleteLine: LineStyle{
78− Symbol: lipgloss.NewStyle().
79− Foreground(charmtone.Cherry).
80− Background(lipgloss.Color("#3a3030")),
81− Code: lipgloss.NewStyle().
82− Foreground(charmtone.Salt).
83− Background(lipgloss.Color("#3a3030")),
84− },
85−}
86−
8728// DiffView represents a view for displaying differences between two files.
8829type DiffView struct {
8930 layout layout
9031 before file
9132 after file
9233 contextLines int
34+ lineNumbers bool
9335 highlight bool
9436 height int
9537 width int
@@ -106,6 +48,7 @@ func New() *DiffView {
10648 dv := &DiffView{
10749 layout: layoutUnified,
10850 contextLines: udiff.DefaultContextLines,
51+ lineNumbers: true,
10952 }
11053 if lipgloss.HasDarkBackground(os.Stdin, os.Stdout) {
11154 dv.style = DefaultDarkStyle
@@ -151,8 +94,14 @@ func (dv *DiffView) Style(style Style) *DiffView {
15194 return dv
15295}
15396
154−// SyntaxHighlight sets whether to enable syntax highlighting in the DiffView.
155−func (dv *DiffView) SyntaxHighlight(highlight bool) *DiffView {
97+// LineNumbers sets whether to display line numbers in the DiffView.
98+func (dv *DiffView) LineNumbers(lineNumbers bool) *DiffView {
99+ dv.lineNumbers = lineNumbers
100+ return dv
101+}
102+
103+// SyntaxHightlight sets whether to enable syntax highlighting in the DiffView.
104+func (dv *DiffView) SyntaxHightlight(highlight bool) *DiffView {
156105 dv.highlight = highlight
157106 return dv
158107}
@@ -176,22 +125,48 @@ func (dv *DiffView) String() string {
176125 }
177126 dv.detectWidth()
178127
128+ lineNumberWidth := func(start, num int) int {
129+ return len(strconv.Itoa(start + num))
130+ }
131+
179132 var b strings.Builder
180133
181134 for _, h := range dv.unified.Hunks {
135+ beforeLine := h.FromLine
136+ afterLine := h.ToLine
137+
138+ beforeNumDigits := lineNumberWidth(h.FromLine, len(h.Lines))
139+ afterNumDigits := lineNumberWidth(h.ToLine, len(h.Lines))
140+
182141 for _, l := range h.Lines {
183142 content := strings.TrimSuffix(l.Content, "\n")
184− width := dv.width - leadingSymbolsSize
143+ codeWidth := dv.width - leadingSymbolsSize
185144
186145 switch l.Kind {
146+ case udiff.Equal:
147+ if dv.lineNumbers {
148+ b.WriteString(dv.style.EqualLine.LineNumber.Render(pad(beforeLine, beforeNumDigits)))
149+ b.WriteString(dv.style.EqualLine.LineNumber.Render(pad(afterLine, afterNumDigits)))
150+ }
151+ b.WriteString(dv.style.EqualLine.Code.Width(codeWidth + leadingSymbolsSize).Render(" " + content))
152+ beforeLine++
153+ afterLine++
187154 case udiff.Insert:
155+ if dv.lineNumbers {
156+ b.WriteString(dv.style.InsertLine.LineNumber.Render(pad(" ", beforeNumDigits)))
157+ b.WriteString(dv.style.InsertLine.LineNumber.Render(pad(afterLine, afterNumDigits)))
158+ }
188159 b.WriteString(dv.style.InsertLine.Symbol.Render("+ "))
189− b.WriteString(dv.style.InsertLine.Code.Width(width).Render(content))
160+ b.WriteString(dv.style.InsertLine.Code.Width(codeWidth).Render(content))
161+ afterLine++
190162 case udiff.Delete:
163+ if dv.lineNumbers {
164+ b.WriteString(dv.style.DeleteLine.LineNumber.Render(pad(beforeLine, beforeNumDigits)))
165+ b.WriteString(dv.style.DeleteLine.LineNumber.Render(pad(" ", afterNumDigits)))
166+ }
191167 b.WriteString(dv.style.DeleteLine.Symbol.Render("- "))
192− b.WriteString(dv.style.DeleteLine.Code.Width(width).Render(content))
193− case udiff.Equal:
194− b.WriteString(dv.style.EqualLine.Code.Width(width + leadingSymbolsSize).Render(" " + content))
168+ b.WriteString(dv.style.DeleteLine.Code.Width(codeWidth).Render(content))
169+ beforeLine++
195170 }
196171 b.WriteRune('\n')
197172 }