From 50f17e4c219d13e8ba27225be9f1394323c53d9a Mon Sep 17 00:00:00 2001 From: Naveen Mahalingam Date: Sun, 7 Jan 2024 14:42:08 -0800 Subject: [PATCH] progress: auto-trim output to fit terminal width (#290) --- .github/workflows/ci.yml | 6 +-- cmd/demo-progress/demo.go | 4 +- go.mod | 20 +++++++--- go.sum | 29 ++++++++++---- progress/progress.go | 81 +++++++++++++++++++++++++++++++++------ progress/progress_test.go | 17 ++++++-- progress/render.go | 37 +++++++++++------- progress/render_test.go | 53 ++++++++++++++++++------- progress/writer.go | 14 ++++--- 9 files changed, 194 insertions(+), 67 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 779b58c..12cc358 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,13 +25,13 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.16 + go-version: 1.17 # Download all the tools used in the steps that follow - name: Set up Tools run: | - go get -u github.com/fzipp/gocyclo/cmd/gocyclo - go get -u github.com/mattn/goveralls + go install github.com/fzipp/gocyclo/cmd/gocyclo@v0.6.0 + go install github.com/mattn/goveralls@v0.0.12 # Run all the unit-tests - name: Test diff --git a/cmd/demo-progress/demo.go b/cmd/demo-progress/demo.go index 0bc6b1d..f141e89 100644 --- a/cmd/demo-progress/demo.go +++ b/cmd/demo-progress/demo.go @@ -118,11 +118,11 @@ func main() { // instantiate a Progress Writer and set up the options pw := progress.NewWriter() pw.SetAutoStop(*flagAutoStop) - pw.SetTrackerLength(25) - pw.SetMessageWidth(24) + pw.SetMessageLength(24) pw.SetNumTrackersExpected(*flagNumTrackers) pw.SetSortBy(progress.SortByPercentDsc) pw.SetStyle(progress.StyleDefault) + pw.SetTrackerLength(25) pw.SetTrackerPosition(progress.PositionRight) pw.SetUpdateFrequency(time.Millisecond * 100) pw.Style().Colors = progress.StyleColorsExample diff --git a/go.mod b/go.mod index 1b19eb9..bc0a3d2 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,20 @@ module github.com/jedib0t/go-pretty/v6 -go 1.16 +go 1.17 require ( - github.com/mattn/go-runewidth v0.0.13 - github.com/pkg/profile v1.6.0 - github.com/stretchr/testify v1.7.4 - golang.org/x/sys v0.1.0 + github.com/mattn/go-runewidth v0.0.15 + github.com/pkg/profile v1.7.0 + github.com/stretchr/testify v1.8.4 + golang.org/x/sys v0.16.0 + golang.org/x/term v0.16.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/felixge/fgprof v0.9.3 // indirect + github.com/google/pprof v0.0.0-20211214055906-6f57359322fd // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 697cf25..2b8531e 100644 --- a/go.sum +++ b/go.sum @@ -1,21 +1,34 @@ +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= -github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/pkg/profile v1.6.0 h1:hUDfIISABYI59DyeB3OTay/HxSRwTQ8rB/H83k6r5dM= -github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= +github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= +github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= +github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y= +github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= +github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA= +github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.4 h1:wZRexSlwd7ZXfKINDLsO4r7WBt3gTKONc6K/VesHvHM= -github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/progress/progress.go b/progress/progress.go index 26960c9..58eb0c5 100644 --- a/progress/progress.go +++ b/progress/progress.go @@ -1,14 +1,15 @@ package progress import ( + "context" "fmt" "io" "os" "sync" "time" - "unicode/utf8" "github.com/jedib0t/go-pretty/v6/text" + "golang.org/x/term" ) var ( @@ -23,13 +24,12 @@ var ( // Progress helps track progress for one or more tasks. type Progress struct { autoStop bool - done chan bool + lengthMessage int lengthProgress int lengthProgressOverall int lengthTracker int logsToRender []string logsToRenderMutex sync.RWMutex - messageWidth int numTrackersExpected int64 outputWriter io.Writer overallTracker *Tracker @@ -37,10 +37,15 @@ type Progress struct { pinnedMessages []string pinnedMessageMutex sync.RWMutex pinnedMessageNumLines int + renderContext context.Context + renderContextCancel context.CancelFunc renderInProgress bool renderInProgressMutex sync.RWMutex sortBy SortBy style *Style + terminalWidth int + terminalWidthMutex sync.RWMutex + terminalWidthOverride int trackerPosition Position trackersActive []*Tracker trackersActiveMutex sync.RWMutex @@ -168,11 +173,19 @@ func (p *Progress) SetAutoStop(autoStop bool) { p.autoStop = autoStop } -// SetMessageWidth sets the (printed) length of the tracker message. Any message -// longer the specified width will be snipped abruptly. Any message shorter than +// SetMessageLength sets the (printed) length of the tracker message. Any +// message longer the specified length will be snipped. Any message shorter than // the specified width will be padded with spaces. +func (p *Progress) SetMessageLength(length int) { + p.lengthMessage = length +} + +// SetMessageWidth sets the (printed) length of the tracker message. Any message +// longer the specified width will be snipped. Any message shorter than the +// specified width will be padded with spaces. +// Deprecated: in favor of SetMessageLength(length) func (p *Progress) SetMessageWidth(width int) { - p.messageWidth = width + p.lengthMessage = width } // SetNumTrackersExpected sets the expected number of trackers to be tracked. @@ -209,6 +222,12 @@ func (p *Progress) SetStyle(style Style) { p.style = &style } +// SetTerminalWidth sets up a sticky terminal width and prevents the Progress +// Writer from polling for the real width during render. +func (p *Progress) SetTerminalWidth(width int) { + p.terminalWidthOverride = width +} + // SetTrackerLength sets the text-length of all the Trackers. func (p *Progress) SetTrackerLength(length int) { p.lengthTracker = length @@ -266,7 +285,7 @@ func (p *Progress) ShowValue(show bool) { // Stop stops the Render() logic that is in progress. func (p *Progress) Stop() { if p.IsRenderInProgress() { - p.done <- true + p.renderContextCancel() } } @@ -279,6 +298,16 @@ func (p *Progress) Style() *Style { return p.style } +func (p *Progress) getTerminalWidth() int { + p.terminalWidthMutex.RLock() + defer p.terminalWidthMutex.RUnlock() + + if p.terminalWidthOverride > 0 { + return p.terminalWidthOverride + } + return p.terminalWidth +} + func (p *Progress) initForRender() { // pick a default style p.Style() @@ -287,7 +316,7 @@ func (p *Progress) initForRender() { } // reset the signals - p.done = make(chan bool, 1) + p.renderContext, p.renderContextCancel = context.WithCancel(context.Background()) // pick default lengths if no valid ones set if p.lengthTracker <= 0 { @@ -297,13 +326,15 @@ func (p *Progress) initForRender() { // calculate length of the actual progress bar by discounting the left/right // border/box chars p.lengthProgress = p.lengthTracker - - utf8.RuneCountInString(p.style.Chars.BoxLeft) - - utf8.RuneCountInString(p.style.Chars.BoxRight) - p.lengthProgressOverall = p.messageWidth + + text.RuneWidthWithoutEscSequences(p.style.Chars.BoxLeft) - + text.RuneWidthWithoutEscSequences(p.style.Chars.BoxRight) + p.lengthProgressOverall = p.lengthMessage + text.RuneWidthWithoutEscSequences(p.style.Options.Separator) + p.lengthProgress + 1 if p.style.Visibility.Percentage { - p.lengthProgressOverall += text.RuneWidthWithoutEscSequences(fmt.Sprintf(p.style.Options.PercentFormat, 0.0)) + p.lengthProgressOverall += text.RuneWidthWithoutEscSequences( + fmt.Sprintf(p.style.Options.PercentFormat, 0.0), + ) } // if not output write has been set, output to STDOUT @@ -315,6 +346,32 @@ func (p *Progress) initForRender() { if p.updateFrequency <= 0 { p.updateFrequency = DefaultUpdateFrequency } + + // get the current terminal size for preventing roll-overs, and do this in a + // background loop until end of render + go p.watchTerminalSize() // needs p.updateFrequency +} + +func (p *Progress) updateTerminalSize() { + p.terminalWidthMutex.Lock() + defer p.terminalWidthMutex.Unlock() + + p.terminalWidth, _, _ = term.GetSize(int(os.Stdout.Fd())) +} + +func (p *Progress) watchTerminalSize() { + // once + p.updateTerminalSize() + // until end of time + ticker := time.NewTicker(time.Second / 10) + for { + select { + case <-ticker.C: + p.updateTerminalSize() + case <-p.renderContext.Done(): + return + } + } } // renderHint has hints for the Render*() logic diff --git a/progress/progress_test.go b/progress/progress_test.go index e5f94de..a43891f 100644 --- a/progress/progress_test.go +++ b/progress/progress_test.go @@ -1,6 +1,7 @@ package progress import ( + "context" "math" "os" "testing" @@ -143,6 +144,16 @@ func TestProgress_SetStyle(t *testing.T) { assert.Equal(t, StyleCircle.Name, p.Style().Name) } +func TestProgress_SetMessageLength(t *testing.T) { + p := Progress{} + assert.Equal(t, 0, p.lengthMessage) + + p.SetMessageLength(80) + assert.Equal(t, 80, p.lengthMessage) + p.SetMessageWidth(81) + assert.Equal(t, 81, p.lengthMessage) +} + func TestProgress_SetTrackerLength(t *testing.T) { p := Progress{} assert.Equal(t, 0, p.lengthTracker) @@ -222,13 +233,11 @@ func TestProgress_ShowValue(t *testing.T) { } func TestProgress_Stop(t *testing.T) { - doneChannel := make(chan bool, 1) - p := Progress{} - p.done = doneChannel + p.renderContext, p.renderContextCancel = context.WithCancel(context.Background()) p.renderInProgress = true p.Stop() - assert.True(t, <-doneChannel) + assert.NotNil(t, <-p.renderContext.Done()) } func TestProgress_Style(t *testing.T) { diff --git a/progress/render.go b/progress/render.go index fe77501..7ee4f15 100644 --- a/progress/render.go +++ b/progress/render.go @@ -22,7 +22,7 @@ func (p *Progress) Render() { select { case <-ticker.C: lastRenderLength = p.renderTrackers(lastRenderLength) - case <-p.done: + case <-p.renderContext.Done(): // always render the current state before finishing render in // case it hasn't been shown yet p.renderTrackers(lastRenderLength) @@ -180,7 +180,11 @@ func (p *Progress) renderPinnedMessages(out *strings.Builder) { numLines := len(p.pinnedMessages) for _, msg := range p.pinnedMessages { msg = strings.TrimSpace(msg) - out.WriteString(p.style.Colors.Pinned.Sprint(msg)) + msg = p.style.Colors.Pinned.Sprint(msg) + if width := p.getTerminalWidth(); width > 0 { + msg = text.Trim(msg, width) + } + out.WriteString(msg) out.WriteRune('\n') numLines += strings.Count(msg, "\n") @@ -192,28 +196,37 @@ func (p *Progress) renderTracker(out *strings.Builder, t *Tracker, hint renderHi message := t.message() message = strings.ReplaceAll(message, "\t", " ") message = strings.ReplaceAll(message, "\r", "") - if p.messageWidth > 0 { + if p.lengthMessage > 0 { messageLen := text.RuneWidthWithoutEscSequences(message) - if messageLen < p.messageWidth { - message = text.Pad(message, p.messageWidth, ' ') + if messageLen < p.lengthMessage { + message = text.Pad(message, p.lengthMessage, ' ') } else { - message = text.Snip(message, p.messageWidth, p.style.Options.SnipIndicator) + message = text.Snip(message, p.lengthMessage, p.style.Options.SnipIndicator) } } + tOut := &strings.Builder{} + tOut.Grow(p.lengthProgressOverall) if hint.isOverallTracker { if !t.IsDone() { hint := renderHint{hideValue: true, isOverallTracker: true} - p.renderTrackerProgress(out, t, message, p.generateTrackerStr(t, p.lengthProgressOverall, hint), hint) + p.renderTrackerProgress(tOut, t, message, p.generateTrackerStr(t, p.lengthProgressOverall, hint), hint) } } else { if t.IsDone() { - p.renderTrackerDone(out, t, message) + p.renderTrackerDone(tOut, t, message) } else { hint := renderHint{hideTime: !p.style.Visibility.Time, hideValue: !p.style.Visibility.Value} - p.renderTrackerProgress(out, t, message, p.generateTrackerStr(t, p.lengthProgress, hint), hint) + p.renderTrackerProgress(tOut, t, message, p.generateTrackerStr(t, p.lengthProgress, hint), hint) } } + + outStr := tOut.String() + if width := p.getTerminalWidth(); width > 0 { + outStr = text.Trim(outStr, width) + } + out.WriteString(outStr) + out.WriteRune('\n') } func (p *Progress) renderTrackerDone(out *strings.Builder, t *Tracker, message string) { @@ -225,7 +238,6 @@ func (p *Progress) renderTrackerDone(out *strings.Builder, t *Tracker, message s out.WriteString(p.style.Colors.Error.Sprint(p.style.Options.ErrorString)) } p.renderTrackerStats(out, t, renderHint{hideTime: !p.style.Visibility.Time, hideValue: !p.style.Visibility.Value}) - out.WriteRune('\n') } func (p *Progress) renderTrackerMessage(out *strings.Builder, t *Tracker, message string) { @@ -252,7 +264,6 @@ func (p *Progress) renderTrackerProgress(out *strings.Builder, t *Tracker, messa if hint.isOverallTracker { out.WriteString(p.style.Colors.Tracker.Sprint(trackerStr)) p.renderTrackerStats(out, t, hint) - out.WriteRune('\n') } else if p.trackerPosition == PositionRight { p.renderTrackerMessage(out, t, message) out.WriteString(p.style.Colors.Message.Sprint(p.style.Options.Separator)) @@ -261,7 +272,6 @@ func (p *Progress) renderTrackerProgress(out *strings.Builder, t *Tracker, messa out.WriteString(p.style.Colors.Tracker.Sprint(" " + trackerStr)) } p.renderTrackerStats(out, t, hint) - out.WriteRune('\n') } else { p.renderTrackerPercentage(out, t) if p.style.Visibility.Tracker { @@ -270,7 +280,6 @@ func (p *Progress) renderTrackerProgress(out *strings.Builder, t *Tracker, messa p.renderTrackerStats(out, t, hint) out.WriteString(p.style.Colors.Message.Sprint(p.style.Options.Separator)) p.renderTrackerMessage(out, t, message) - out.WriteRune('\n') } } @@ -301,7 +310,7 @@ func (p *Progress) renderTrackers(lastRenderLength int) int { // stop if auto stop is enabled and there are no more active trackers if p.autoStop && p.LengthActive() == 0 { - p.done <- true + p.renderContextCancel() } return out.Len() diff --git a/progress/render_test.go b/progress/render_test.go index df0adc8..c815e5f 100644 --- a/progress/render_test.go +++ b/progress/render_test.go @@ -544,7 +544,7 @@ func TestProgress_RenderSomeTrackers_WithLineWidth1(t *testing.T) { renderOutput := outputWriter{} pw := generateWriter() - pw.SetMessageWidth(5) + pw.SetMessageLength(5) pw.SetOutputWriter(&renderOutput) pw.SetTrackerPosition(PositionRight) go trackSomething(pw, &Tracker{Message: "Calculating Total # 1\r", Total: 1000, Units: UnitsDefault}) @@ -573,7 +573,7 @@ func TestProgress_RenderSomeTrackers_WithLineWidth2(t *testing.T) { renderOutput := outputWriter{} pw := generateWriter() - pw.SetMessageWidth(50) + pw.SetMessageLength(50) pw.SetOutputWriter(&renderOutput) pw.SetTrackerPosition(PositionRight) go trackSomething(pw, &Tracker{Message: "Calculating Total # 1\r", Total: 1000, Units: UnitsDefault}) @@ -598,6 +598,33 @@ func TestProgress_RenderSomeTrackers_WithLineWidth2(t *testing.T) { showOutputOnFailure(t, out) } +func TestProgress_RenderSomeTrackers_WithTerminalWidth(t *testing.T) { + renderOutput := outputWriter{} + + pw := generateWriter() + pw.SetMessageLength(5) + pw.SetOutputWriter(&renderOutput) + pw.SetTerminalWidth(10) + pw.SetTrackerPosition(PositionRight) + go trackSomething(pw, &Tracker{Message: "Calculating Total # 1\r", Total: 1000, Units: UnitsDefault}) + go trackSomething(pw, &Tracker{Message: "Downloading File\t# 2", Total: 1000, Units: UnitsBytes}) + go trackSomething(pw, &Tracker{Message: "Transferring Amount # 3", Total: 1000, Units: UnitsCurrencyDollar}) + renderAndWait(pw, false) + + expectedOutPatterns := []*regexp.Regexp{ + regexp.MustCompile(`Calc~ \.\.\. \n`), + regexp.MustCompile(`Down~ \.\.\. \n`), + regexp.MustCompile(`Tran~ \.\.\. \n`), + } + out := renderOutput.String() + for _, expectedOutPattern := range expectedOutPatterns { + if !expectedOutPattern.MatchString(out) { + assert.Fail(t, "Failed to find a pattern in the Output.", expectedOutPattern.String()) + } + } + showOutputOnFailure(t, out) +} + func TestProgress_RenderSomeTrackers_WithOverallTracker(t *testing.T) { renderOutput := outputWriter{} @@ -812,28 +839,26 @@ func TestProgress_RenderSomeTrackers_WithOverallTracker_WithSpeedOverall_Without showOutputOnFailure(t, out) } -func TestProgress_RenderSomeTrackers_WithPinMessage_OneLine(t *testing.T) { +func TestProgress_RenderSomeTrackers_WithPinnedMessages_OneLine(t *testing.T) { renderOutput := outputWriter{} pw := generateWriter() - pw.SetMessageWidth(5) + pw.SetMessageLength(5) pw.SetOutputWriter(&renderOutput) + pw.SetTerminalWidth(10) pw.SetTrackerPosition(PositionRight) pw.Style().Visibility.Pinned = true - pw.SetPinnedMessages("PIN") + pw.SetPinnedMessages("PINNED MESSAGE #1") go trackSomething(pw, &Tracker{Message: "Calculating Total # 1\r", Total: 1000, Units: UnitsDefault}) go trackSomething(pw, &Tracker{Message: "Downloading File\t# 2", Total: 1000, Units: UnitsBytes}) go trackSomething(pw, &Tracker{Message: "Transferring Amount # 3", Total: 1000, Units: UnitsCurrencyDollar}) renderAndWait(pw, false) expectedOutPatterns := []*regexp.Regexp{ - regexp.MustCompile(`PIN`), - regexp.MustCompile(`Calc~ \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+ in [\d.]+ms]`), - regexp.MustCompile(`Down~ \.\.\. \d+\.\d+% \[[#.]{23}] \[\d+B in [\d.]+ms]`), - regexp.MustCompile(`Tran~ \.\.\. \d+\.\d+% \[[#.]{23}] \[\$\d+ in [\d.]+ms]`), - regexp.MustCompile(`Calc~ \.\.\. done! \[\d+\.\d+K in [\d.]+ms]`), - regexp.MustCompile(`Down~ \.\.\. done! \[\d+\.\d+KB in [\d.]+ms]`), - regexp.MustCompile(`Tran~ \.\.\. done! \[\$\d+\.\d+K in [\d.]+ms]`), + regexp.MustCompile(`PINNED MES\n`), + regexp.MustCompile(`Calc~ \.\.\. \n`), + regexp.MustCompile(`Down~ \.\.\. \n`), + regexp.MustCompile(`Tran~ \.\.\. \n`), } out := renderOutput.String() for _, expectedOutPattern := range expectedOutPatterns { @@ -844,11 +869,11 @@ func TestProgress_RenderSomeTrackers_WithPinMessage_OneLine(t *testing.T) { showOutputOnFailure(t, out) } -func TestProgress_RenderSomeTrackers_WithPinMessage_MultiLines(t *testing.T) { +func TestProgress_RenderSomeTrackers_WithPinnedMessages_MultiLines(t *testing.T) { renderOutput := outputWriter{} pw := generateWriter() - pw.SetMessageWidth(5) + pw.SetMessageLength(5) pw.SetOutputWriter(&renderOutput) pw.SetTrackerPosition(PositionRight) pw.Style().Visibility.Pinned = true diff --git a/progress/writer.go b/progress/writer.go index 0e6d7bc..3368854 100644 --- a/progress/writer.go +++ b/progress/writer.go @@ -17,14 +17,22 @@ type Writer interface { LengthInQueue() int Log(msg string, a ...interface{}) SetAutoStop(autoStop bool) - SetMessageWidth(width int) + SetMessageLength(length int) SetNumTrackersExpected(numTrackers int) SetOutputWriter(output io.Writer) SetPinnedMessages(messages ...string) SetSortBy(sortBy SortBy) SetStyle(style Style) + SetTerminalWidth(width int) SetTrackerLength(length int) SetTrackerPosition(position Position) + SetUpdateFrequency(frequency time.Duration) + Stop() + Style() *Style + Render() + + // Deprecated: in favor of SetMessageLength(length) + SetMessageWidth(width int) // Deprecated: in favor of Style().Visibility.ETA ShowETA(show bool) // Deprecated: in favor of Style().Visibility.TrackerOverall @@ -37,10 +45,6 @@ type Writer interface { ShowTracker(show bool) // Deprecated: in favor of Style().Visibility.Value ShowValue(show bool) - SetUpdateFrequency(frequency time.Duration) - Stop() - Style() *Style - Render() } // NewWriter initializes and returns a Writer.