From ca97d2e746c9024fd119504ae620d786616fef5e Mon Sep 17 00:00:00 2001 From: Laurent Demailly Date: Fri, 24 Oct 2025 20:26:04 -0700 Subject: [PATCH 1/3] Adding NewAutoProgress so total can be set. Made Update() thread safe --- progressbar.go | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/progressbar.go b/progressbar.go index d8d7333..a6d413c 100644 --- a/progressbar.go +++ b/progressbar.go @@ -24,6 +24,7 @@ const ( Space = " " Full = "█" // Green FG, Grey BG. + GreenBar = "\033[32;100m" RedBar = "\033[91;100m" YellowBar = "\033[93;100m" @@ -33,9 +34,9 @@ const ( Reset = "\033[0m" ClearAfter = "\033[J" DoneSpinner = "✓ " - // Default max refresh to avoid slowing down transfers because of progress bar updates. + // DefaultMaxUpdateInterval is the default max refresh to avoid slowing down transfers because of progress bar updates. DefaultMaxUpdateInterval = 100 * time.Millisecond - // Expected max length of a progress bar line (prefix + spinner + bar + percentage + extra). + // ExpectedMaxLength is the expected max length of a progress bar line (prefix + spinner + bar + percentage + extra). // used for initial buffer size, will resize if needed but should be avoided. Note it includes // non printable ANSI sequences and utf8 encoded characters so it's the same as the onscreen width. ExpectedMaxLength = 256 @@ -44,6 +45,7 @@ const ( var ( // 1/8th of a full block to 7/8ths of a full block (ie fractional part of a block to // get 8x resolution per character). + FractionalBlocks = [...]string{"", "▏", "▎", "▍", "▌", "▋", "▊", "▉"} SpinnerChars = [...]string{"⣾ ", "⣷ ", "⣯ ", "⣟ ", "⡿ ", "⢿ ", "⣻ ", "⣽ "} ) @@ -262,7 +264,7 @@ func (w *writer) Write(buf []byte) (n int, err error) { } n, err = w.out.Write(buf) w.Unlock() - return + return n, err } // Global write with lock and reused buffer. @@ -319,13 +321,28 @@ type AutoProgress struct { start time.Time } +// NewAutoProgress returns a new AutoProgress associated with +// the given bar and total size. [AutoProgress.Update] can be called +// from multiple goroutines to increment the progress by . +func NewAutoProgress(bar *Bar, total int64) *AutoProgress { + res := &AutoProgress{} + res.Bar = bar + res.total = total + return res +} + func (a *AutoProgress) Update(n int) { + a.out.Lock() if a.current == 0 { a.start = time.Now() } a.current += int64(n) if a.current > 0 || a.total > 0 { - a.Progress(float64(a.current) * 100. / float64(a.total)) + p := float64(a.current) * 100. / float64(a.total) + a.out.Unlock() + a.Progress(p) + } else { + a.out.Unlock() } } @@ -370,7 +387,7 @@ func (r *AutoProgressReader) Read(p []byte) (n int, err error) { if n > 0 { r.Update(n) } - return + return n, err } // End the progress bar: writes a newline and last update if it was skipped @@ -417,7 +434,7 @@ type AutoProgressWriter struct { func (w *AutoProgressWriter) Write(p []byte) (n int, err error) { n, err = w.w.Write(p) w.Update(n) - return + return n, err } func (w *AutoProgressWriter) Close() error { From 5b06929dfc736d35e202fa22b5bb9a9582607f86 Mon Sep 17 00:00:00 2001 From: Laurent Demailly Date: Fri, 24 Oct 2025 20:45:06 -0700 Subject: [PATCH 2/3] Split locking so Update doesn't need to acquire/release/acquire --- progressbar.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/progressbar.go b/progressbar.go index a6d413c..cd7735c 100644 --- a/progressbar.go +++ b/progressbar.go @@ -118,9 +118,13 @@ func (bar *Bar) UpdateSuffix(s string) { // Of note it will work best if every output to the Writer() ends with a \n. // The bar state must be obtained from NewBar() or cfg.NewBar() to setup the shared lock. func (bar *Bar) Progress(progressPercent float64) { - isDone := isDone(progressPercent) bar.out.Lock() - defer bar.out.Unlock() + bar.progressLocked(progressPercent) + bar.out.Unlock() +} + +func (bar *Bar) progressLocked(progressPercent float64) { + isDone := isDone(progressPercent) bar.percent = progressPercent // Skip if last write was too recent and we're not done and nothing else was written in between. if bar.UpdateInterval > 0 && !isDone && bar.out.needErase { @@ -339,11 +343,9 @@ func (a *AutoProgress) Update(n int) { a.current += int64(n) if a.current > 0 || a.total > 0 { p := float64(a.current) * 100. / float64(a.total) - a.out.Unlock() - a.Progress(p) - } else { - a.out.Unlock() + a.progressLocked(p) } + a.out.Unlock() } // Extra provides the extra information on the right of the progress bar: currrent transfer amount, speed and estimated time left. From a5a97a46cac4f584a0d21eab3dcdb0987396e43d Mon Sep 17 00:00:00 2001 From: Laurent Demailly Date: Fri, 24 Oct 2025 20:50:48 -0700 Subject: [PATCH 3/3] closer to original --- progressbar.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/progressbar.go b/progressbar.go index cd7735c..7566448 100644 --- a/progressbar.go +++ b/progressbar.go @@ -342,8 +342,7 @@ func (a *AutoProgress) Update(n int) { } a.current += int64(n) if a.current > 0 || a.total > 0 { - p := float64(a.current) * 100. / float64(a.total) - a.progressLocked(p) + a.progressLocked(float64(a.current) * 100. / float64(a.total)) } a.out.Unlock() }