chore: fix regression in terminal output (#1567)

This commit is contained in:
Abiola Ibrahim
2026-05-05 23:40:38 +01:00
committed by GitHub
parent af68d6d26b
commit 61d6f7548d
2 changed files with 71 additions and 11 deletions
+38 -11
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"io"
"os"
"regexp"
"strconv"
"strings"
"sync"
@@ -20,15 +21,17 @@ type verboseWriter struct {
buf bytes.Buffer
lines []string
lineHeight int
termWidth int
overflow int
lineHeight int
termWidth int
screenHeight int
lastUpdate time.Time
sync.Mutex
}
var ansiControlSequence = regexp.MustCompile(`\x1b\[[0-?]*[ -/]*[@-~]`)
// NewVerboseWriter creates a new verbose writer.
// A verbose writer pipes the input received to the stdout while tailing the specified lines.
// Calling `Close` when done is recommended to clear the last uncleared output.
@@ -109,6 +112,8 @@ func (v *verboseWriter) sanitizeLine(line string) string {
}
}
line = normalizeDisplayText(line)
return "> " + line
}
@@ -117,15 +122,10 @@ func (v *verboseWriter) printScreen() error {
return err
}
v.overflow = 0
v.screenHeight = 0
for _, line := range v.lines {
line = v.sanitizeLine(line)
if len(line) > v.termWidth {
v.overflow += len(line) / v.termWidth
if len(line)%v.termWidth == 0 {
v.overflow -= 1
}
}
v.screenHeight += countDisplayLines(line, v.termWidth)
line = color.HiBlackString(line)
fmt.Println(line)
}
@@ -133,9 +133,10 @@ func (v *verboseWriter) printScreen() error {
}
func (v *verboseWriter) clearScreen() {
for i := 0; i < len(v.lines)+v.overflow; i++ {
for i := 0; i < v.screenHeight; i++ {
ClearLine()
}
v.screenHeight = 0
}
func (v *verboseWriter) updateTerm() error {
@@ -160,3 +161,29 @@ func (v *verboseWriter) updateTerm() error {
return nil
}
func countDisplayLines(line string, termWidth int) int {
if termWidth <= 0 {
termWidth = 80
}
visibleWidth := len([]rune(normalizeDisplayText(line)))
if visibleWidth == 0 {
return 1
}
return ((visibleWidth - 1) / termWidth) + 1
}
func normalizeDisplayText(line string) string {
line = ansiControlSequence.ReplaceAllString(line, "")
line = strings.ReplaceAll(line, "\r", "")
line = strings.ReplaceAll(line, "\n", "")
line = strings.Map(func(r rune) rune {
if r < 32 && r != '\t' {
return -1
}
return r
}, line)
return line
}
+33
View File
@@ -0,0 +1,33 @@
package terminal
import "testing"
func TestCountDisplayLines(t *testing.T) {
tests := []struct {
name string
line string
termWidth int
want int
}{
{name: "short line", line: "> short", termWidth: 80, want: 1},
{name: "wrapped line", line: "> 12345678901", termWidth: 10, want: 2},
{name: "exact width", line: "> 12345678", termWidth: 10, want: 1},
{name: "carriage return ignored", line: "> abc\rdef", termWidth: 80, want: 1},
{name: "ansi ignored", line: "> \x1b[90mabcdef\x1b[0m", termWidth: 4, want: 2},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := countDisplayLines(tt.line, tt.termWidth); got != tt.want {
t.Fatalf("countDisplayLines(%q, %d) = %d, want %d", tt.line, tt.termWidth, got, tt.want)
}
})
}
}
func TestNormalizeDisplayText(t *testing.T) {
input := "\x1b[90mhello\r\n\x07world\x1b[0m"
if got, want := normalizeDisplayText(input), "helloworld"; got != want {
t.Fatalf("normalizeDisplayText(%q) = %q, want %q", input, got, want)
}
}