fix: tab characters breaking the layout

This commit is contained in:
2026-01-24 02:03:41 +02:00
parent b8be28d92b
commit c09d3e41dd
2 changed files with 70 additions and 3 deletions

View File

@@ -12,6 +12,15 @@ import (
"sync"
)
// sanitizeLine removes control sequences that can corrupt terminal rendering
func sanitizeLine(s string) string {
// Remove carriage returns
s = strings.ReplaceAll(s, "\r", "")
// Convert tabs to spaces (tabs cause width calculation issues)
s = strings.ReplaceAll(s, "\t", " ")
return s
}
// Line represents a single line of output with its line number
type Line struct {
Number int
@@ -141,7 +150,7 @@ func (r *Runner) Run(ctx context.Context) (Result, error) {
for scanner.Scan() {
lines = append(lines, Line{
Number: lineNum,
Content: scanner.Text(),
Content: sanitizeLine(scanner.Text()),
})
lineNum++
}
@@ -151,7 +160,7 @@ func (r *Runner) Run(ctx context.Context) (Result, error) {
for stderrScanner.Scan() {
lines = append(lines, Line{
Number: lineNum,
Content: stderrScanner.Text(),
Content: sanitizeLine(stderrScanner.Text()),
})
lineNum++
}
@@ -265,7 +274,7 @@ func (r *Runner) RunStreaming(ctx context.Context) *StreamingResult {
result.mu.Lock()
*result.Lines = append(*result.Lines, Line{
Number: currentLineNum,
Content: scanner.Text(),
Content: sanitizeLine(scanner.Text()),
})
result.mu.Unlock()
}

View File

@@ -320,3 +320,61 @@ func TestSplitLines(t *testing.T) {
})
}
}
func TestSanitizeLine(t *testing.T) {
tests := []struct {
name string
input string
want string
}{
{
name: "plain text unchanged",
input: "hello world",
want: "hello world",
},
{
name: "tabs converted to spaces",
input: "col1\tcol2\tcol3",
want: "col1 col2 col3",
},
{
name: "carriage returns removed",
input: "line with\r\nwindows ending",
want: "line with\nwindows ending",
},
{
name: "carriage return only removed",
input: "progress\roverwrite",
want: "progressoverwrite",
},
{
name: "ANSI color codes preserved",
input: "\x1b[32mgreen text\x1b[0m",
want: "\x1b[32mgreen text\x1b[0m",
},
{
name: "mixed tabs and colors",
input: "\x1b[1m?\x1b[0m\tpackage\t[no test files]",
want: "\x1b[1m?\x1b[0m package [no test files]",
},
{
name: "empty string",
input: "",
want: "",
},
{
name: "multiple tabs",
input: "\t\t\t",
want: " ",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := sanitizeLine(tt.input)
if got != tt.want {
t.Errorf("sanitizeLine(%q) = %q, want %q", tt.input, got, tt.want)
}
})
}
}