Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions src/output/interactive_display.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,25 +265,37 @@ func (d *interactiveDisplay) flush() {
}

func (d *interactiveDisplay) lprintfPrepare(cols int, s string) string {
if len(s) < cols {
if len(s) < cols && !strings.ContainsRune(s, '\t') {
return s // it's short enough, nice and simple
}
// Okay, it's too long. Tricky thing: ANSI escape codes don't count for width
// so we need to count without those. Bonus: make an effort to be unicode-aware.
written := 0
inAnsiCode := false
ansiStart := false
d.lineBuf.Reset()
for _, rune := range s {
if inAnsiCode {
d.lineBuf.WriteRune(rune)
if rune == 'm' {
// CSI sequences (e.g. \x1b[1m, \x1b[K) end on any byte in the range 0x40-0x7E,
// excluding the opening [.
if (!ansiStart || rune != '[') && rune >= 0x40 && rune <= 0x7E {
inAnsiCode = false
}
ansiStart = false
} else if rune == '\x1b' {
d.lineBuf.WriteRune(rune)
inAnsiCode = true
ansiStart = true
} else if rune == '\n' {
d.lineBuf.WriteRune(rune)
written = 0
} else if rune == '\t' {
// Tabs are one character but can take up to 8 columns. Expand them so we know exactly how wide they are.
for n := 8 - written%8; n > 0 && written < cols-3; n-- {
d.lineBuf.WriteByte(' ')
written++
}
} else if written == cols-3 {
d.lineBuf.WriteString("...")
written += 3
Expand Down
16 changes: 16 additions & 0 deletions src/output/interactive_display_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,19 @@ func TestNewlinesStillWritte(t *testing.T) {
// Test that newline still gets written (it doesn't count as horizontal space and is Very Important)
assert.Equal(t, "\x1b[30mwibble ...\x1b[1m\n", d.lprintfPrepare(10, "\x1b[30mwibble wobble\x1b[1m\n"))
}

func TestLimitedPrintfEraseAfter(t *testing.T) {
var d interactiveDisplay
assert.Equal(t, "\x1b[Kmmmmmmm...\n", d.lprintfPrepare(10, "\x1b[Kmmmmmmmmmmmmmmmmmmmm\n"))
}

func TestLimitedPrintfTabsExpanded(t *testing.T) {
var d interactiveDisplay
assert.Equal(t, "a b", d.lprintfPrepare(20, "a\tb"))
assert.Equal(t, "a b...", d.lprintfPrepare(12, "a\tbcdefghijk"))
}

func TestLimitedPrintfNewlineResetsWidth(t *testing.T) {
var d interactiveDisplay
assert.Equal(t, "wibble ...\nwibble ...\n", d.lprintfPrepare(10, "wibble wobble\nwibble wobble\n"))
}
Loading