Skip to content

Commit

Permalink
Merge pull request #196 from luo-cheng-xi/not-now
Browse files Browse the repository at this point in the history
fix: change some attribute about time to time.Time{}
  • Loading branch information
schollz authored Sep 15, 2024
2 parents 4c1391b + 243aacb commit dcb591b
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 4 deletions.
46 changes: 42 additions & 4 deletions progressbar.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ type state struct {
isAltSaucerHead bool

lastShown time.Time
startTime time.Time
startTime time.Time // time when the progress bar start working

counterTime time.Time
counterNumSinceLast int64
Expand Down Expand Up @@ -331,7 +331,11 @@ func NewOptions(max int, options ...Option) *ProgressBar {
// NewOptions64 constructs a new instance of ProgressBar, with any options you specify
func NewOptions64(max int64, options ...Option) *ProgressBar {
b := ProgressBar{
state: getBasicState(),
state: state{
startTime: time.Time{},
lastShown: time.Time{},
counterTime: time.Time{},
},
config: config{
writer: os.Stdout,
theme: defaultTheme,
Expand Down Expand Up @@ -503,6 +507,24 @@ func (p *ProgressBar) RenderBlank() error {
return p.render()
}

// StartWithoutRender will start the progress bar without rendering it
// this method is created for the use case where you want to start the progress
// but don't want to render it immediately.
// If you want to start the progress and render it immediately, use RenderBlank instead,
// or maybe you can use Add to start it automatically, but it will make the time calculation less precise.
func (p *ProgressBar) StartWithoutRender() {
p.lock.Lock()
defer p.lock.Unlock()

if p.IsStarted() {
return
}

p.state.startTime = time.Now()
// the counterTime should be set to the current time
p.state.counterTime = time.Now()
}

// Reset will reset the clock that is used
// to calculate current time and the time left.
func (p *ProgressBar) Reset() {
Expand Down Expand Up @@ -584,6 +606,10 @@ func (p *ProgressBar) Add64(num int64) error {

p.state.currentBytes += float64(num)

if p.state.counterTime.IsZero() {
p.state.counterTime = time.Now()
}

// reset the countdown timer every second to take rolling average
p.state.counterNumSinceLast += num
if time.Since(p.state.counterTime).Seconds() > 0.5 {
Expand Down Expand Up @@ -746,13 +772,20 @@ func (p *ProgressBar) IsFinished() bool {
return p.state.finished
}

// IsStarted returns true if progress bar is started
func (p *ProgressBar) IsStarted() bool {
return !p.state.startTime.IsZero()
}

// render renders the progress bar, updating the maximum
// rendered line width. this function is not thread-safe,
// so it must be called with an acquired lock.
func (p *ProgressBar) render() error {
// make sure that the rendering is not happening too quickly
// but always show if the currentNum reaches the max
if time.Since(p.state.lastShown).Nanoseconds() < p.config.throttleDuration.Nanoseconds() &&
if !p.IsStarted() {
p.state.startTime = time.Now()
} else if time.Since(p.state.lastShown).Nanoseconds() < p.config.throttleDuration.Nanoseconds() &&
p.state.currentNum < p.config.max {
return nil
}
Expand Down Expand Up @@ -820,7 +853,12 @@ func (p *ProgressBar) State() State {
}
s.CurrentPercent = float64(p.state.currentNum) / float64(p.config.max)
s.CurrentBytes = p.state.currentBytes
s.SecondsSince = time.Since(p.state.startTime).Seconds()
if p.IsStarted() {
s.SecondsSince = time.Since(p.state.startTime).Seconds()
} else {
s.SecondsSince = 0
}

if p.state.currentNum > 0 {
s.SecondsLeft = s.SecondsSince / float64(p.state.currentNum) * (float64(p.config.max) - float64(p.state.currentNum))
}
Expand Down
16 changes: 16 additions & 0 deletions progressbar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ func TestBarSmallBytes(t *testing.T) {
func TestBarFastBytes(t *testing.T) {
buf := strings.Builder{}
bar := NewOptions64(1e8, OptionShowBytes(true), OptionShowCount(), OptionSetWidth(10), OptionSetWriter(&buf))
bar.StartWithoutRender()
time.Sleep(time.Millisecond)
bar.Add(1e7)
if !strings.Contains(buf.String(), " GB/s)") {
Expand Down Expand Up @@ -889,6 +890,7 @@ func TestOptionFullWidth(t *testing.T) {
t.Parallel()
buf := strings.Builder{}
bar := NewOptions(100, append(test.opts, []Option{OptionFullWidth(), OptionSetWriter(&buf)}...)...)
bar.StartWithoutRender()
time.Sleep(1 * time.Second)
bar.Add(10)
time.Sleep(1 * time.Second)
Expand All @@ -913,3 +915,17 @@ func TestHumanizeBytesIEC(t *testing.T) {
amount, suffix = humanizeBytes(float64(56.78)*1024*1024*1024, true)
assert.Equal(t, "57 GiB", fmt.Sprintf("%s%s", amount, suffix))
}

func TestProgressBar_StartWithoutRender(t *testing.T) {
buf := strings.Builder{}
bar := NewOptions(100, OptionSetWriter(&buf))
time.Sleep(1 * time.Second)
bar.StartWithoutRender()
time.Sleep(1 * time.Second)
bar.Add(10)
result := strings.TrimSpace(buf.String())
expect := "10% |████ | [1s:9s]"
if result != expect {
t.Errorf("Render miss-match\nResult: '%s'\nExpect: '%s'\n%+v", result, expect, bar)
}
}

0 comments on commit dcb591b

Please sign in to comment.