Skip to content
Open
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
25 changes: 24 additions & 1 deletion progressbar.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ type state struct {
finished bool
exit bool // Progress bar exit halfway

// knownTermWidth is the terminal width observed on the most recent
// render that queried it (0 if never successfully queried). clear uses
// last cycle's value rather than querying termWidth itself, so a resize
// is picked up within one render without adding a new termWidth call.
knownTermWidth int

details []string // details to show,only used when detail row is set to more than 0

rendered string
Expand Down Expand Up @@ -1214,6 +1220,7 @@ func fitProgressBarWidth(c config, s *state, barStart, barEnd, stats, leftBrac,
if err != nil || terminalWidth <= 0 {
return c.width
}
s.knownTermWidth = terminalWidth

bar := barStart + strings.Repeat(c.theme.SaucerPadding, c.width) + barEnd
lineWidth := getStringWidth(c, renderDeterminateProgressBar(c, s, bar, stats, leftBrac, rightBrac))
Expand Down Expand Up @@ -1336,6 +1343,8 @@ func renderProgressBar(c config, s *state) (int, error) {
width, err := termWidth(c.writer)
if err != nil {
width = 80
} else {
s.knownTermWidth = width
}

amend := 1 // an extra space at eol
Expand Down Expand Up @@ -1484,12 +1493,26 @@ func clearProgressBar(c config, s state) error {
if runtime.GOOS == "windows" {
return writeString(c, "\r")
}
str := fmt.Sprintf("\r%s\r", strings.Repeat(" ", s.maxLineWidth))
str := fmt.Sprintf("\r%s\r", strings.Repeat(" ", clampClearWidth(s.maxLineWidth, s.knownTermWidth)))
return writeString(c, str)
// the following does not show correctly if the previous line is longer than subsequent line
// return writeString(c, "\r")
}

// clampClearWidth returns maxLineWidth, capped down to knownTermWidth when
// the terminal is narrower than the widest bar ever rendered. Padding the
// clear string to the old (wider) maxLineWidth would overflow the current
// line, so the terminal wraps it onto a new line instead of clearing in
// place -- the bar then appears to restart on every render (#106).
// knownTermWidth <= 0 (never successfully queried) or not narrower than
// maxLineWidth leaves it unchanged.
func clampClearWidth(maxLineWidth, knownTermWidth int) int {
if knownTermWidth > 0 && knownTermWidth < maxLineWidth {
return knownTermWidth
}
return maxLineWidth
}

func writeString(c config, str string) error {
if _, err := io.WriteString(c.writer, str); err != nil {
return err
Expand Down
23 changes: 23 additions & 0 deletions progressbar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1048,6 +1048,29 @@ func TestFixedWidthBarFitsTerminal(t *testing.T) {
assert.NotContains(t, bar.String(), "| |")
}

// If the terminal has shrunk since the widest bar was rendered, padding the
// clear string to that old (wider) maxLineWidth overflows the current line:
// the terminal wraps it onto a new line instead of clearing in place, so the
// bar appears to restart on every render (#106).
func TestClampClearWidth(t *testing.T) {
tests := []struct {
name string
maxLineWidth int
knownTermWidth int
want int
}{
{"terminal shrunk narrower than maxLineWidth", 80, 20, 20},
{"terminal still wider than maxLineWidth", 20, 80, 20},
{"terminal width never known falls back to maxLineWidth", 80, 0, 80},
{"terminal width exactly equal is left alone", 80, 80, 80},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, clampClearWidth(tt.maxLineWidth, tt.knownTermWidth))
})
}
}

func TestHumanizeBytesSI(t *testing.T) {
amount, suffix := humanizeBytes(float64(12.34)*1000*1000, false)
assert.Equal(t, "12 MB", fmt.Sprintf("%s%s", amount, suffix))
Expand Down