@@ -43,9 +43,10 @@ type DiffView struct {
43 unified udiff.UnifiedDiff
43 unified udiff.UnifiedDiff
44 edits []udiff.Edit
44 edits []udiff.Edit
46− splitHunks []splitHunk
46+ splitHunks []splitHunk
47− beforeCodeWidth int
47+
48− afterCodeWidth int
48+ codeWidth int
49+ fullCodeWidth int // with leading symbols
51// New creates a new DiffView with default settings.
52// New creates a new DiffView with default settings.
@@ -128,6 +129,8 @@ func (dv *DiffView) String() string {
128 if err := dv.computeDiff(); err != nil {
129 if err := dv.computeDiff(); err != nil {
129 return err.Error()
130 return err.Error()
132+ dv.convertDiffToSplit()
133+ dv.detectCodeWidth()
132 switch dv.layout {
135 switch dv.layout {
133 case layoutUnified:
136 case layoutUnified:
@@ -141,9 +144,6 @@ func (dv *DiffView) String() string {
142// renderUnified renders the unified diff view as a string.
145// renderUnified renders the unified diff view as a string.
143func (dv *DiffView) renderUnified() string {
146func (dv *DiffView) renderUnified() string {
144− dv.detectUnifiedWidth()
146− codeWidth := dv.width - leadingSymbolsSize
147 beforeNumDigits, afterNumDigits := dv.lineNumberDigits()
147 beforeNumDigits, afterNumDigits := dv.lineNumberDigits()
149 var b strings.Builder
149 var b strings.Builder
@@ -153,7 +153,7 @@ func (dv *DiffView) renderUnified() string {
153 b.WriteString(dv.style.DividerLine.LineNumber.Render(pad("…", beforeNumDigits)))
153 b.WriteString(dv.style.DividerLine.LineNumber.Render(pad("…", beforeNumDigits)))
154 b.WriteString(dv.style.DividerLine.LineNumber.Render(pad("…", afterNumDigits)))
154 b.WriteString(dv.style.DividerLine.LineNumber.Render(pad("…", afterNumDigits)))
156− b.WriteString(dv.style.DividerLine.Code.Width(codeWidth + leadingSymbolsSize).Render(dv.hunkLineFor(h)))
156+ b.WriteString(dv.style.DividerLine.Code.Width(dv.fullCodeWidth).Render(dv.hunkLineFor(h)))
157 b.WriteRune('\n')
157 b.WriteRune('\n')
159 beforeLine := h.FromLine
159 beforeLine := h.FromLine
@@ -168,7 +168,7 @@ func (dv *DiffView) renderUnified() string {
168 b.WriteString(dv.style.EqualLine.LineNumber.Render(pad(beforeLine, beforeNumDigits)))
168 b.WriteString(dv.style.EqualLine.LineNumber.Render(pad(beforeLine, beforeNumDigits)))
169 b.WriteString(dv.style.EqualLine.LineNumber.Render(pad(afterLine, afterNumDigits)))
169 b.WriteString(dv.style.EqualLine.LineNumber.Render(pad(afterLine, afterNumDigits)))
171− b.WriteString(dv.style.EqualLine.Code.Width(codeWidth + leadingSymbolsSize).Render(" " + content))
171+ b.WriteString(dv.style.EqualLine.Code.Width(dv.fullCodeWidth).Render(" " + content))
172 beforeLine++
172 beforeLine++
173 afterLine++
173 afterLine++
174 case udiff.Insert:
174 case udiff.Insert:
@@ -177,7 +177,7 @@ func (dv *DiffView) renderUnified() string {
177 b.WriteString(dv.style.InsertLine.LineNumber.Render(pad(afterLine, afterNumDigits)))
177 b.WriteString(dv.style.InsertLine.LineNumber.Render(pad(afterLine, afterNumDigits)))
179 b.WriteString(dv.style.InsertLine.Symbol.Render("+ "))
179 b.WriteString(dv.style.InsertLine.Symbol.Render("+ "))
180− b.WriteString(dv.style.InsertLine.Code.Width(codeWidth).Render(content))
180+ b.WriteString(dv.style.InsertLine.Code.Width(dv.codeWidth).Render(content))
181 afterLine++
181 afterLine++
182 case udiff.Delete:
182 case udiff.Delete:
183 if dv.lineNumbers {
183 if dv.lineNumbers {
@@ -185,7 +185,7 @@ func (dv *DiffView) renderUnified() string {
185 b.WriteString(dv.style.DeleteLine.LineNumber.Render(pad(" ", afterNumDigits)))
185 b.WriteString(dv.style.DeleteLine.LineNumber.Render(pad(" ", afterNumDigits)))
187 b.WriteString(dv.style.DeleteLine.Symbol.Render("- "))
187 b.WriteString(dv.style.DeleteLine.Symbol.Render("- "))
188− b.WriteString(dv.style.DeleteLine.Code.Width(codeWidth).Render(content))
188+ b.WriteString(dv.style.DeleteLine.Code.Width(dv.codeWidth).Render(content))
189 beforeLine++
189 beforeLine++
191 b.WriteRune('\n')
191 b.WriteRune('\n')
@@ -197,9 +197,6 @@ func (dv *DiffView) renderUnified() string {
198// renderSplit renders the split (side-by-side) diff view as a string.
198// renderSplit renders the split (side-by-side) diff view as a string.
199func (dv *DiffView) renderSplit() string {
199func (dv *DiffView) renderSplit() string {
200− dv.convertDiffToSplit()
201− dv.detectSplitWidth()
203 beforeNumDigits, afterNumDigits := dv.lineNumberDigits()
200 beforeNumDigits, afterNumDigits := dv.lineNumberDigits()
205 var b strings.Builder
202 var b strings.Builder
@@ -208,11 +205,11 @@ func (dv *DiffView) renderSplit() string {
208 if dv.lineNumbers {
205 if dv.lineNumbers {
209 b.WriteString(dv.style.DividerLine.LineNumber.Render(pad("…", beforeNumDigits)))
206 b.WriteString(dv.style.DividerLine.LineNumber.Render(pad("…", beforeNumDigits)))
211− b.WriteString(dv.style.DividerLine.Code.Width(dv.beforeCodeWidth + leadingSymbolsSize).Render(dv.hunkLineFor(dv.unified.Hunks[i])))
208+ b.WriteString(dv.style.DividerLine.Code.Width(dv.fullCodeWidth).Render(dv.hunkLineFor(dv.unified.Hunks[i])))
212 if dv.lineNumbers {
209 if dv.lineNumbers {
213 b.WriteString(dv.style.DividerLine.LineNumber.Render(pad("…", afterNumDigits)))
210 b.WriteString(dv.style.DividerLine.LineNumber.Render(pad("…", afterNumDigits)))
215− b.WriteString(dv.style.DividerLine.Code.Width(dv.afterCodeWidth + leadingSymbolsSize).Render(" "))
212+ b.WriteString(dv.style.DividerLine.Code.Width(dv.fullCodeWidth).Render(" "))
216 b.WriteRune('\n')
213 b.WriteRune('\n')
218 beforeLine := h.fromLine
215 beforeLine := h.fromLine
@@ -233,19 +230,19 @@ func (dv *DiffView) renderSplit() string {
233 if dv.lineNumbers {
230 if dv.lineNumbers {
234 b.WriteString(dv.style.MissingLine.LineNumber.Render(pad(" ", beforeNumDigits)))
231 b.WriteString(dv.style.MissingLine.LineNumber.Render(pad(" ", beforeNumDigits)))
236− b.WriteString(dv.style.MissingLine.Code.Width(dv.beforeCodeWidth + leadingSymbolsSize).Render(" "))
233+ b.WriteString(dv.style.MissingLine.Code.Width(dv.fullCodeWidth).Render(" "))
237 case l.before.Kind == udiff.Equal:
234 case l.before.Kind == udiff.Equal:
238 if dv.lineNumbers {
235 if dv.lineNumbers {
239 b.WriteString(dv.style.EqualLine.LineNumber.Render(pad(beforeLine, beforeNumDigits)))
236 b.WriteString(dv.style.EqualLine.LineNumber.Render(pad(beforeLine, beforeNumDigits)))
241− b.WriteString(dv.style.EqualLine.Code.Width(dv.beforeCodeWidth + leadingSymbolsSize).Render(" " + beforeContent))
238+ b.WriteString(dv.style.EqualLine.Code.Width(dv.fullCodeWidth).Render(" " + beforeContent))
242 beforeLine++
239 beforeLine++
243 case l.before.Kind == udiff.Delete:
240 case l.before.Kind == udiff.Delete:
244 if dv.lineNumbers {
241 if dv.lineNumbers {
245 b.WriteString(dv.style.DeleteLine.LineNumber.Render(pad(beforeLine, beforeNumDigits)))
242 b.WriteString(dv.style.DeleteLine.LineNumber.Render(pad(beforeLine, beforeNumDigits)))
247 b.WriteString(dv.style.DeleteLine.Symbol.Render("- "))
244 b.WriteString(dv.style.DeleteLine.Symbol.Render("- "))
248− b.WriteString(dv.style.DeleteLine.Code.Width(dv.beforeCodeWidth).Render(beforeContent))
245+ b.WriteString(dv.style.DeleteLine.Code.Width(dv.codeWidth).Render(beforeContent))
249 beforeLine++
246 beforeLine++
@@ -254,19 +251,19 @@ func (dv *DiffView) renderSplit() string {
254 if dv.lineNumbers {
251 if dv.lineNumbers {
255 b.WriteString(dv.style.MissingLine.LineNumber.Render(pad(" ", afterNumDigits)))
252 b.WriteString(dv.style.MissingLine.LineNumber.Render(pad(" ", afterNumDigits)))
257− b.WriteString(dv.style.MissingLine.Code.Width(dv.afterCodeWidth + leadingSymbolsSize).Render(" "))
254+ b.WriteString(dv.style.MissingLine.Code.Width(dv.fullCodeWidth).Render(" "))
258 case l.after.Kind == udiff.Equal:
255 case l.after.Kind == udiff.Equal:
259 if dv.lineNumbers {
256 if dv.lineNumbers {
260 b.WriteString(dv.style.EqualLine.LineNumber.Render(pad(afterLine, afterNumDigits)))
257 b.WriteString(dv.style.EqualLine.LineNumber.Render(pad(afterLine, afterNumDigits)))
262− b.WriteString(dv.style.EqualLine.Code.Width(dv.afterCodeWidth + leadingSymbolsSize).Render(" " + afterContent))
259+ b.WriteString(dv.style.EqualLine.Code.Width(dv.fullCodeWidth).Render(" " + afterContent))
263 afterLine++
260 afterLine++
264 case l.after.Kind == udiff.Insert:
261 case l.after.Kind == udiff.Insert:
265 if dv.lineNumbers {
262 if dv.lineNumbers {
266 b.WriteString(dv.style.InsertLine.LineNumber.Render(pad(afterLine, afterNumDigits)))
263 b.WriteString(dv.style.InsertLine.LineNumber.Render(pad(afterLine, afterNumDigits)))
268 b.WriteString(dv.style.InsertLine.Symbol.Render("+ "))
265 b.WriteString(dv.style.InsertLine.Symbol.Render("+ "))
269− b.WriteString(dv.style.InsertLine.Code.Width(dv.afterCodeWidth).Render(afterContent))
266+ b.WriteString(dv.style.InsertLine.Code.Width(dv.codeWidth).Render(afterContent))
270 afterLine++
267 afterLine++
@@ -335,51 +332,55 @@ func (dv *DiffView) hunkShownLines(h *udiff.Hunk) (before, after int) {
338−func (dv *DiffView) detectUnifiedWidth() {
335+func (dv *DiffView) detectCodeWidth() {
339− if dv.width > 0 {
336+ switch dv.layout {
340− return
337+ case layoutUnified:
338+ dv.detectUnifiedCodeWidth()
340+ dv.detectSplitCodeWidth()
342+ dv.fullCodeWidth = dv.codeWidth + leadingSymbolsSize
345+func (dv *DiffView) detectUnifiedCodeWidth() {
343 for _, h := range dv.unified.Hunks {
348 for _, h := range dv.unified.Hunks {
344 shownLines := ansi.StringWidth(dv.hunkLineFor(h))
349 shownLines := ansi.StringWidth(dv.hunkLineFor(h))
346 for _, l := range h.Lines {
351 for _, l := range h.Lines {
347− lineWidth := ansi.StringWidth(strings.TrimSuffix(l.Content, "\n"))
352+ lineWidth := ansi.StringWidth(strings.TrimSuffix(l.Content, "\n")) + 1
348− lineWidth += leadingSymbolsSize
349− dv.width = max(dv.width, lineWidth, shownLines)
353+ dv.codeWidth = max(dv.codeWidth, lineWidth, shownLines)
354func (dv *DiffView) convertDiffToSplit() {
358func (dv *DiffView) convertDiffToSplit() {
359+ if dv.layout != layoutSplit {
355 dv.splitHunks = make([]splitHunk, len(dv.unified.Hunks))
363 dv.splitHunks = make([]splitHunk, len(dv.unified.Hunks))
356 for i, h := range dv.unified.Hunks {
364 for i, h := range dv.unified.Hunks {
357 dv.splitHunks[i] = hunkToSplit(h)
365 dv.splitHunks[i] = hunkToSplit(h)
361−func (dv *DiffView) detectSplitWidth() {
369+func (dv *DiffView) detectSplitCodeWidth() {
362− if dv.width > 0 {
370+ dv.codeWidth = 0
366 for i, h := range dv.splitHunks {
372 for i, h := range dv.splitHunks {
367 shownLines := ansi.StringWidth(dv.hunkLineFor(dv.unified.Hunks[i]))
373 shownLines := ansi.StringWidth(dv.hunkLineFor(dv.unified.Hunks[i]))
369 for _, l := range h.lines {
375 for _, l := range h.lines {
371 if l.before != nil {
376 if l.before != nil {
372 codeWidth := ansi.StringWidth(strings.TrimSuffix(l.before.Content, "\n")) + 1
377 codeWidth := ansi.StringWidth(strings.TrimSuffix(l.before.Content, "\n")) + 1
373− dv.beforeCodeWidth = max(dv.beforeCodeWidth, codeWidth, shownLines)
378+ dv.codeWidth = max(dv.codeWidth, codeWidth, shownLines)
374− lineWidth += codeWidth
376 if l.after != nil {
380 if l.after != nil {
377 codeWidth := ansi.StringWidth(strings.TrimSuffix(l.after.Content, "\n")) + 1
381 codeWidth := ansi.StringWidth(strings.TrimSuffix(l.after.Content, "\n")) + 1
378− dv.afterCodeWidth = max(dv.afterCodeWidth, codeWidth, shownLines)
382+ dv.codeWidth = max(dv.codeWidth, codeWidth, shownLines)
379− lineWidth += codeWidth
381− lineWidth += leadingSymbolsSize * 2
382− dv.width = max(dv.width, lineWidth, shownLines)