@@ -362,23 +362,29 @@ func (dv *DiffView) renderUnified() string {
363 fullContentStyle := lipgloss.NewStyle().MaxWidth(dv.fullCodeWidth)
363 fullContentStyle := lipgloss.NewStyle().MaxWidth(dv.fullCodeWidth)
364 printedLines := -dv.yOffset
364 printedLines := -dv.yOffset
366− write := func(s string) {
367− if printedLines >= 0 {
365+ shouldWrite := func() bool { return printedLines >= 0 }
368− b.WriteString(s)
366+
369− }
367+ getContent := func(in string, ls LineStyle) (content string, leadingEllipsis bool) {
368+ content = strings.TrimSuffix(in, "\n")
369+ content = dv.hightlightCode(content, ls.Code.GetBackground())
370+ content = ansi.GraphemeWidth.Cut(content, dv.xOffset, len(content))
371+ content = ansi.Truncate(content, dv.codeWidth, "…")
372+ leadingEllipsis = dv.xOffset > 0 && strings.TrimSpace(content) != ""
373 for i, h := range dv.unified.Hunks {
377 for i, h := range dv.unified.Hunks {
374− ls := dv.style.DividerLine
379+ ls := dv.style.DividerLine
375− if dv.lineNumbers {
380+ if dv.lineNumbers {
376− write(ls.LineNumber.Render(pad("…", dv.beforeNumDigits)))
381+ b.WriteString(ls.LineNumber.Render(pad("…", dv.beforeNumDigits)))
377− write(ls.LineNumber.Render(pad("…", dv.afterNumDigits)))
382+ b.WriteString(ls.LineNumber.Render(pad("…", dv.afterNumDigits)))
384+ content := ansi.Truncate(dv.hunkLineFor(h), dv.fullCodeWidth, "…")
385+ b.WriteString(ls.Code.Width(dv.fullCodeWidth).Render(content))
379− content := ansi.Truncate(dv.hunkLineFor(h), dv.fullCodeWidth, "…")
380− write(ls.Code.Width(dv.fullCodeWidth).Render(content))
382 printedLines++
388 printedLines++
384 beforeLine := h.FromLine
390 beforeLine := h.FromLine
@@ -390,80 +396,82 @@ outer:
390 isLastHunk := i+1 == len(dv.unified.Hunks)
396 isLastHunk := i+1 == len(dv.unified.Hunks)
391 isLastLine := j+1 == len(h.Lines)
397 isLastLine := j+1 == len(h.Lines)
392 if hasReachedHeight && (!isLastHunk || !isLastLine) {
398 if hasReachedHeight && (!isLastHunk || !isLastLine) {
393− ls := dv.lineStyleForType(l.Kind)
400+ ls := dv.lineStyleForType(l.Kind)
394− if dv.lineNumbers {
401+ if dv.lineNumbers {
395− write(ls.LineNumber.Render(pad("…", dv.beforeNumDigits)))
402+ b.WriteString(ls.LineNumber.Render(pad("…", dv.beforeNumDigits)))
396− write(ls.LineNumber.Render(pad("…", dv.afterNumDigits)))
403+ b.WriteString(ls.LineNumber.Render(pad("…", dv.afterNumDigits)))
405+ b.WriteString(fullContentStyle.Render(
406+ ls.Code.Width(dv.fullCodeWidth).Render(" …"),
398− write(fullContentStyle.Render(
399− ls.Code.Width(dv.fullCodeWidth).Render(" …"),
402 break outer
410 break outer
405− getContent := func(ls LineStyle) string {
406− content := strings.TrimSuffix(l.Content, "\n")
407− content = dv.hightlightCode(content, ls.Code.GetBackground())
408− content = ansi.GraphemeWidth.Cut(content, dv.xOffset, len(content))
409− content = ansi.Truncate(content, dv.codeWidth, "…")
413− leadingEllipsis := dv.xOffset > 0 && strings.TrimSpace(content) != ""
415 switch l.Kind {
413 switch l.Kind {
416 case udiff.Equal:
414 case udiff.Equal:
417− ls := dv.style.EqualLine
416+ ls := dv.style.EqualLine
418− content := getContent(ls)
417+ content, leadingEllipsis := getContent(l.Content, ls)
419− if dv.lineNumbers {
418+ if dv.lineNumbers {
420− write(ls.LineNumber.Render(pad(beforeLine, dv.beforeNumDigits)))
419+ b.WriteString(ls.LineNumber.Render(pad(beforeLine, dv.beforeNumDigits)))
421− write(ls.LineNumber.Render(pad(afterLine, dv.afterNumDigits)))
420+ b.WriteString(ls.LineNumber.Render(pad(afterLine, dv.afterNumDigits)))
422+ b.WriteString(fullContentStyle.Render(
423+ ls.Code.Width(dv.fullCodeWidth).Render(ternary(leadingEllipsis, " …", " ") + content),
423− write(fullContentStyle.Render(
424− ls.Code.Width(dv.fullCodeWidth).Render(ternary(leadingEllipsis, " …", " ") + content),
426 beforeLine++
426 beforeLine++
427 afterLine++
427 afterLine++
428 case udiff.Insert:
428 case udiff.Insert:
429− ls := dv.style.InsertLine
430+ ls := dv.style.InsertLine
430− content := getContent(ls)
431+ content, leadingEllipsis := getContent(l.Content, ls)
431− if dv.lineNumbers {
432+ if dv.lineNumbers {
432− write(ls.LineNumber.Render(pad(" ", dv.beforeNumDigits)))
433+ b.WriteString(ls.LineNumber.Render(pad(" ", dv.beforeNumDigits)))
433− write(ls.LineNumber.Render(pad(afterLine, dv.afterNumDigits)))
434+ b.WriteString(ls.LineNumber.Render(pad(afterLine, dv.afterNumDigits)))
436+ b.WriteString(fullContentStyle.Render(
437+ ls.Symbol.Render(ternary(leadingEllipsis, "+…", "+ ")) +
438+ ls.Code.Width(dv.codeWidth).Render(content),
435− write(fullContentStyle.Render(
436− ls.Symbol.Render(ternary(leadingEllipsis, "+…", "+ ")) +
437− ls.Code.Width(dv.codeWidth).Render(content),
439 afterLine++
441 afterLine++
440 case udiff.Delete:
442 case udiff.Delete:
441− ls := dv.style.DeleteLine
444+ ls := dv.style.DeleteLine
442− content := getContent(ls)
445+ content, leadingEllipsis := getContent(l.Content, ls)
443− if dv.lineNumbers {
446+ if dv.lineNumbers {
444− write(ls.LineNumber.Render(pad(beforeLine, dv.beforeNumDigits)))
447+ b.WriteString(ls.LineNumber.Render(pad(beforeLine, dv.beforeNumDigits)))
445− write(ls.LineNumber.Render(pad(" ", dv.afterNumDigits)))
448+ b.WriteString(ls.LineNumber.Render(pad(" ", dv.afterNumDigits)))
450+ b.WriteString(fullContentStyle.Render(
451+ ls.Symbol.Render(ternary(leadingEllipsis, "-…", "- ")) +
452+ ls.Code.Width(dv.codeWidth).Render(content),
447− write(fullContentStyle.Render(
448− ls.Symbol.Render(ternary(leadingEllipsis, "-…", "- ")) +
449− ls.Code.Width(dv.codeWidth).Render(content),
451 beforeLine++
455 beforeLine++
453− write("\n")
457+ if shouldWrite() {
455 printedLines++
461 printedLines++
459 for printedLines < dv.height {
465 for printedLines < dv.height {
460− ls := dv.style.MissingLine
467+ ls := dv.style.MissingLine
461− if dv.lineNumbers {
468+ if dv.lineNumbers {
462− write(ls.LineNumber.Render(pad(" ", dv.beforeNumDigits)))
469+ b.WriteString(ls.LineNumber.Render(pad(" ", dv.beforeNumDigits)))
463− write(ls.LineNumber.Render(pad(" ", dv.afterNumDigits)))
470+ b.WriteString(ls.LineNumber.Render(pad(" ", dv.afterNumDigits)))
472+ b.WriteString(ls.Code.Width(dv.fullCodeWidth).Render(" "))
465− write(ls.Code.Width(dv.fullCodeWidth).Render(" "))
467 printedLines++
475 printedLines++
@@ -477,26 +485,32 @@ func (dv *DiffView) renderSplit() string {
477 beforeFullContentStyle := lipgloss.NewStyle().MaxWidth(dv.fullCodeWidth)
485 beforeFullContentStyle := lipgloss.NewStyle().MaxWidth(dv.fullCodeWidth)
478 afterFullContentStyle := lipgloss.NewStyle().MaxWidth(dv.fullCodeWidth + btoi(dv.extraColOnAfter))
486 afterFullContentStyle := lipgloss.NewStyle().MaxWidth(dv.fullCodeWidth + btoi(dv.extraColOnAfter))
479 printedLines := -dv.yOffset
487 printedLines := -dv.yOffset
481− write := func(s string) {
482− if printedLines >= 0 {
488+ shouldWrite := func() bool { return printedLines >= 0 }
483− b.WriteString(s)
489+
484− }
490+ getContent := func(in string, ls LineStyle) (content string, leadingEllipsis bool) {
491+ content = strings.TrimSuffix(in, "\n")
492+ content = dv.hightlightCode(content, ls.Code.GetBackground())
493+ content = ansi.GraphemeWidth.Cut(content, dv.xOffset, len(content))
494+ content = ansi.Truncate(content, dv.codeWidth, "…")
495+ leadingEllipsis = dv.xOffset > 0 && strings.TrimSpace(content) != ""
488 for i, h := range dv.splitHunks {
500 for i, h := range dv.splitHunks {
489− ls := dv.style.DividerLine
502+ ls := dv.style.DividerLine
490− if dv.lineNumbers {
503+ if dv.lineNumbers {
491− write(ls.LineNumber.Render(pad("…", dv.beforeNumDigits)))
504+ b.WriteString(ls.LineNumber.Render(pad("…", dv.beforeNumDigits)))
493− content := ansi.Truncate(dv.hunkLineFor(dv.unified.Hunks[i]), dv.fullCodeWidth, "…")
506+ content := ansi.Truncate(dv.hunkLineFor(dv.unified.Hunks[i]), dv.fullCodeWidth, "…")
494− write(ls.Code.Width(dv.fullCodeWidth).Render(content))
507+ b.WriteString(ls.Code.Width(dv.fullCodeWidth).Render(content))
495− if dv.lineNumbers {
508+ if dv.lineNumbers {
496− write(ls.LineNumber.Render(pad("…", dv.afterNumDigits)))
509+ b.WriteString(ls.LineNumber.Render(pad("…", dv.afterNumDigits)))
511+ b.WriteString(ls.Code.Width(dv.fullCodeWidth + btoi(dv.extraColOnAfter)).Render(" "))
498− write(ls.Code.Width(dv.fullCodeWidth + btoi(dv.extraColOnAfter)).Render(" "))
500 printedLines++
514 printedLines++
502 beforeLine := h.fromLine
516 beforeLine := h.fromLine
@@ -508,126 +522,129 @@ outer:
508 isLastHunk := i+1 == len(dv.unified.Hunks)
522 isLastHunk := i+1 == len(dv.unified.Hunks)
509 isLastLine := j+1 == len(h.lines)
523 isLastLine := j+1 == len(h.lines)
510 if hasReachedHeight && (!isLastHunk || !isLastLine) {
524 if hasReachedHeight && (!isLastHunk || !isLastLine) {
511− ls := dv.style.MissingLine
526+ ls := dv.style.MissingLine
512− if l.before != nil {
527+ if l.before != nil {
513− ls = dv.lineStyleForType(l.before.Kind)
528+ ls = dv.lineStyleForType(l.before.Kind)
515− if dv.lineNumbers {
530+ if dv.lineNumbers {
516− write(ls.LineNumber.Render(pad("…", dv.beforeNumDigits)))
531+ b.WriteString(ls.LineNumber.Render(pad("…", dv.beforeNumDigits)))
533+ b.WriteString(beforeFullContentStyle.Render(
534+ ls.Code.Width(dv.fullCodeWidth).Render(" …"),
536+ ls = dv.style.MissingLine
538+ ls = dv.lineStyleForType(l.after.Kind)
541+ b.WriteString(ls.LineNumber.Render(pad("…", dv.afterNumDigits)))
543+ b.WriteString(afterFullContentStyle.Render(
544+ ls.Code.Width(dv.fullCodeWidth).Render(" …"),
518− write(beforeFullContentStyle.Render(
519− ls.Code.Width(dv.fullCodeWidth).Render(" …"),
521− ls = dv.style.MissingLine
523− ls = dv.lineStyleForType(l.after.Kind)
526− write(ls.LineNumber.Render(pad("…", dv.afterNumDigits)))
528− write(afterFullContentStyle.Render(
529− ls.Code.Width(dv.fullCodeWidth).Render(" …"),
532 break outer
548 break outer
535− getContent := func(content string, ls LineStyle) string {
536− content = strings.TrimSuffix(content, "\n")
537− content = dv.hightlightCode(content, ls.Code.GetBackground())
538− content = ansi.GraphemeWidth.Cut(content, dv.xOffset, len(content))
539− content = ansi.Truncate(content, dv.codeWidth, "…")
542− getLeadingEllipsis := func(content string) bool {
543− return dv.xOffset > 0 && strings.TrimSpace(content) != ""
547 case l.before == nil:
552 case l.before == nil:
548− ls := dv.style.MissingLine
554+ ls := dv.style.MissingLine
549− if dv.lineNumbers {
555+ if dv.lineNumbers {
550− write(ls.LineNumber.Render(pad(" ", dv.beforeNumDigits)))
556+ b.WriteString(ls.LineNumber.Render(pad(" ", dv.beforeNumDigits)))
558+ b.WriteString(beforeFullContentStyle.Render(
559+ ls.Code.Width(dv.fullCodeWidth).Render(" "),
552− write(beforeFullContentStyle.Render(
553− ls.Code.Width(dv.fullCodeWidth).Render(" "),
555 case l.before.Kind == udiff.Equal:
562 case l.before.Kind == udiff.Equal:
556− ls := dv.style.EqualLine
564+ ls := dv.style.EqualLine
557− content := getContent(l.before.Content, ls)
565+ content, leadingEllipsis := getContent(l.before.Content, ls)
558− leadingEllipsis := getLeadingEllipsis(content)
559− if dv.lineNumbers {
566+ if dv.lineNumbers {
560− write(ls.LineNumber.Render(pad(beforeLine, dv.beforeNumDigits)))
567+ b.WriteString(ls.LineNumber.Render(pad(beforeLine, dv.beforeNumDigits)))
569+ b.WriteString(beforeFullContentStyle.Render(
570+ ls.Code.Width(dv.fullCodeWidth).Render(ternary(leadingEllipsis, " …", " ") + content),
562− write(beforeFullContentStyle.Render(
563− ls.Code.Width(dv.fullCodeWidth).Render(ternary(leadingEllipsis, " …", " ") + content),
565 beforeLine++
573 beforeLine++
566 case l.before.Kind == udiff.Delete:
574 case l.before.Kind == udiff.Delete:
567− ls := dv.style.DeleteLine
576+ ls := dv.style.DeleteLine
568− content := getContent(l.before.Content, ls)
577+ content, leadingEllipsis := getContent(l.before.Content, ls)
569− leadingEllipsis := getLeadingEllipsis(content)
570− if dv.lineNumbers {
578+ if dv.lineNumbers {
571− write(ls.LineNumber.Render(pad(beforeLine, dv.beforeNumDigits)))
579+ b.WriteString(ls.LineNumber.Render(pad(beforeLine, dv.beforeNumDigits)))
581+ b.WriteString(beforeFullContentStyle.Render(
582+ ls.Symbol.Render(ternary(leadingEllipsis, "-…", "- ")) +
583+ ls.Code.Width(dv.codeWidth).Render(content),
573− write(beforeFullContentStyle.Render(
574− ls.Symbol.Render(ternary(leadingEllipsis, "-…", "- ")) +
575− ls.Code.Width(dv.codeWidth).Render(content),
577 beforeLine++
586 beforeLine++
581 case l.after == nil:
590 case l.after == nil:
582− ls := dv.style.MissingLine
592+ ls := dv.style.MissingLine
583− if dv.lineNumbers {
593+ if dv.lineNumbers {
584− write(ls.LineNumber.Render(pad(" ", dv.afterNumDigits)))
594+ b.WriteString(ls.LineNumber.Render(pad(" ", dv.afterNumDigits)))
596+ b.WriteString(afterFullContentStyle.Render(
597+ ls.Code.Width(dv.fullCodeWidth + btoi(dv.extraColOnAfter)).Render(" "),
586− write(afterFullContentStyle.Render(
587− ls.Code.Width(dv.fullCodeWidth + btoi(dv.extraColOnAfter)).Render(" "),
589 case l.after.Kind == udiff.Equal:
600 case l.after.Kind == udiff.Equal:
590− ls := dv.style.EqualLine
602+ ls := dv.style.EqualLine
591− content := getContent(l.after.Content, ls)
603+ content, leadingEllipsis := getContent(l.after.Content, ls)
592− leadingEllipsis := getLeadingEllipsis(content)
593− if dv.lineNumbers {
604+ if dv.lineNumbers {
594− write(ls.LineNumber.Render(pad(afterLine, dv.afterNumDigits)))
605+ b.WriteString(ls.LineNumber.Render(pad(afterLine, dv.afterNumDigits)))
607+ b.WriteString(afterFullContentStyle.Render(
608+ ls.Code.Width(dv.fullCodeWidth + btoi(dv.extraColOnAfter)).Render(ternary(leadingEllipsis, " …", " ") + content),
596− write(afterFullContentStyle.Render(
597− ls.Code.Width(dv.fullCodeWidth + btoi(dv.extraColOnAfter)).Render(ternary(leadingEllipsis, " …", " ") + content),
599 afterLine++
611 afterLine++
600 case l.after.Kind == udiff.Insert:
612 case l.after.Kind == udiff.Insert:
601− ls := dv.style.InsertLine
614+ ls := dv.style.InsertLine
602− content := getContent(l.after.Content, ls)
615+ content, leadingEllipsis := getContent(l.after.Content, ls)
603− leadingEllipsis := getLeadingEllipsis(content)
604− if dv.lineNumbers {
616+ if dv.lineNumbers {
605− write(ls.LineNumber.Render(pad(afterLine, dv.afterNumDigits)))
617+ b.WriteString(ls.LineNumber.Render(pad(afterLine, dv.afterNumDigits)))
619+ b.WriteString(afterFullContentStyle.Render(
620+ ls.Symbol.Render(ternary(leadingEllipsis, "+…", "+ ")) +
621+ ls.Code.Width(dv.codeWidth+btoi(dv.extraColOnAfter)).Render(content),
607− write(afterFullContentStyle.Render(
608− ls.Symbol.Render(ternary(leadingEllipsis, "+…", "+ ")) +
609− ls.Code.Width(dv.codeWidth+btoi(dv.extraColOnAfter)).Render(content),
611 afterLine++
624 afterLine++
614− write("\n")
627+ if shouldWrite() {
616 printedLines++
631 printedLines++
620 for printedLines < dv.height {
635 for printedLines < dv.height {
621− ls := dv.style.MissingLine
637+ ls := dv.style.MissingLine
622− if dv.lineNumbers {
638+ if dv.lineNumbers {
623− write(ls.LineNumber.Render(pad(" ", dv.beforeNumDigits)))
639+ b.WriteString(ls.LineNumber.Render(pad(" ", dv.beforeNumDigits)))
625− write(ls.Code.Width(dv.fullCodeWidth).Render(" "))
641+ b.WriteString(ls.Code.Width(dv.fullCodeWidth).Render(" "))
626− if dv.lineNumbers {
642+ if dv.lineNumbers {
627− write(ls.LineNumber.Render(pad(" ", dv.afterNumDigits)))
643+ b.WriteString(ls.LineNumber.Render(pad(" ", dv.afterNumDigits)))
645+ b.WriteString(ls.Code.Width(dv.fullCodeWidth + btoi(dv.extraColOnAfter)).Render(" "))
629− write(ls.Code.Width(dv.fullCodeWidth + btoi(dv.extraColOnAfter)).Render(" "))
631 printedLines++
648 printedLines++