diff --git a/bar_filler.go b/bar_filler.go index 2a7d0e8..d26f4a9 100644 --- a/bar_filler.go +++ b/bar_filler.go @@ -1,8 +1,9 @@ package mpb import ( + "bytes" "io" - "strings" + "unicode/utf8" "github.com/vbauerster/mpb/v4/decor" "github.com/vbauerster/mpb/v4/internal" @@ -17,54 +18,67 @@ rRefill ) -var defaultBarStyle = []rune("[=>-]+") +var defaultBarStyle = "[=>-]+" type barFiller struct { - format []rune + format [][]byte rup int +} + +func newDefaultBarFiller() Filler { + bf := &barFiller{ + format: make([][]byte, utf8.RuneCountInString(defaultBarStyle)), + } + bf.setStyle(defaultBarStyle) + return bf +} + +func (s *barFiller) setStyle(style string) { + if !utf8.ValidString(style) { + style = defaultBarStyle + } + src := make([][]byte, 0, utf8.RuneCountInString(style)) + for _, r := range style { + src = append(src, []byte(string(r))) + } + copy(s.format, src) } func (s *barFiller) Fill(w io.Writer, width int, stat *decor.Statistics) { - str := string(s.format[rLeft]) + b := s.format[rLeft] // don't count rLeft and rRight [brackets] width -= 2 - if width <= 2 { - io.WriteString(w, str+string(s.format[rRight])) + if width < 2 { + return + } else if width == 2 { + w.Write(append(b, s.format[rRight]...)) return } - progressWidth := internal.Percentage(stat.Total, stat.Current, int64(width)) - needTip := progressWidth < int64(width) && progressWidth > 0 + cwidth := internal.Percentage(stat.Total, stat.Current, int64(width)) - if needTip { - progressWidth-- + if s.rup > 0 { + rwidth := internal.Percentage(stat.Total, int64(s.rup), int64(width)) + b = append(b, bytes.Repeat(s.format[rRefill], int(rwidth))...) + rest := cwidth - rwidth + b = append(b, bytes.Repeat(s.format[rFill], int(rest))...) + } else { + b = append(b, bytes.Repeat(s.format[rFill], int(cwidth))...) } - if s.rup > 0 { - refillCount := internal.Percentage(stat.Total, int64(s.rup), int64(width)) - rest := progressWidth - refillCount - str += runeRepeat(s.format[rRefill], int(refillCount)) + runeRepeat(s.format[rFill], int(rest)) - } else { - str += runeRepeat(s.format[rFill], int(progressWidth)) + if cwidth < int64(width) && cwidth > 0 { + _, size := utf8.DecodeLastRune(b) + b = append(b[:len(b)-size], s.format[rTip]...) } - if needTip { - str += string(s.format[rTip]) - progressWidth++ - } - - rest := int64(width) - progressWidth - str += runeRepeat(s.format[rEmpty], int(rest)) + string(s.format[rRight]) - io.WriteString(w, str) + rest := int64(width) - cwidth + b = append(b, bytes.Repeat(s.format[rEmpty], int(rest))...) + w.Write(append(b, s.format[rRight]...)) } func (s *barFiller) SetRefill(upto int) { s.rup = upto } - -func runeRepeat(r rune, count int) string { - return strings.Repeat(string(r), count) -} diff --git a/bar_option.go b/bar_option.go index 8449350..b85a99d 100644 --- a/bar_option.go +++ b/bar_option.go @@ -1,8 +1,6 @@ package mpb import ( - "unicode/utf8" - "github.com/vbauerster/mpb/v4/decor" ) @@ -116,10 +114,7 @@ return t, ok } cb := func(t interface{}) { - if !utf8.ValidString(style) { - return - } - copy(t.(*barFiller).format, []rune(style)) + t.(*barFiller).setStyle(style) } return MakeFillerTypeSpecificBarOption(chk, cb) } diff --git a/bar_test.go b/bar_test.go index cf7e02f..a2a44b1 100644 --- a/bar_test.go +++ b/bar_test.go @@ -61,7 +61,7 @@ total := 100 till := 30 - refillRune := DefaultBarStyle[len(DefaultBarStyle)-1] + refillRune, _ := utf8.DecodeLastRuneInString(DefaultBarStyle) bar := p.AddBar(int64(total), TrimSpace()) diff --git a/draw_test.go b/draw_test.go index 6867f81..456f5ce 100644 --- a/draw_test.go +++ b/draw_test.go @@ -3,6 +3,7 @@ import ( "bytes" "testing" + "unicode/utf8" ) func TestDraw(t *testing.T) { @@ -15,6 +16,142 @@ rup int want string }{ + 2: { + { + name: "t,c,bw{60,20,80}", + total: 60, + current: 20, + barWidth: 80, + want: " ", + }, + { + name: "t,c,bw,trim{60,20,80,true}", + total: 60, + current: 20, + barWidth: 80, + trimSpace: true, + want: "", + }, + }, + 3: { + { + name: "t,c,bw{60,20,80}", + total: 60, + current: 20, + barWidth: 80, + want: " ", + }, + { + name: "t,c,bw,trim{60,20,80,true}", + total: 60, + current: 20, + barWidth: 80, + trimSpace: true, + want: "", + }, + }, + 4: { + { + name: "t,c,bw{60,20,80}", + total: 60, + current: 20, + barWidth: 80, + want: " ", + }, + { + name: "t,c,bw,trim{60,20,80,true}", + total: 60, + current: 20, + barWidth: 80, + trimSpace: true, + want: "[]", + }, + }, + 5: { + { + name: "t,c,bw{60,20,80}", + total: 60, + current: 20, + barWidth: 80, + want: " ", + }, + { + name: "t,c,bw,trim{60,20,80,true}", + total: 60, + current: 20, + barWidth: 80, + trimSpace: true, + want: "[>--]", + }, + }, + 6: { + { + name: "t,c,bw{60,20,80}", + total: 60, + current: 20, + barWidth: 80, + want: " [] ", + }, + { + name: "t,c,bw,trim{60,20,80,true}", + total: 60, + current: 20, + barWidth: 80, + trimSpace: true, + want: "[>---]", + }, + }, + 7: { + { + name: "t,c,bw{60,20,80}", + total: 60, + current: 20, + barWidth: 80, + want: " [>--] ", + }, + { + name: "t,c,bw,trim{60,20,80,true}", + total: 60, + current: 20, + barWidth: 80, + trimSpace: true, + want: "[=>---]", + }, + }, + 8: { + { + name: "t,c,bw{60,20,80}", + total: 60, + current: 20, + barWidth: 80, + want: " [>---] ", + }, + { + name: "t,c,bw,trim{60,20,80,true}", + total: 60, + current: 20, + barWidth: 80, + trimSpace: true, + want: "[=>----]", + }, + }, + 80: { + { + name: "t,c,bw{60,20,80}", + total: 60, + current: 20, + barWidth: 80, + want: " [========================>---------------------------------------------------] ", + }, + { + name: "t,c,bw,trim{60,20,80,true}", + total: 60, + current: 20, + barWidth: 80, + trimSpace: true, + want: "[=========================>----------------------------------------------------]", + }, + }, 100: { { name: "t,c,bw{100,100,0}", @@ -24,7 +161,7 @@ want: " [------------------------------------------------------------------------------------------------] ", }, { - name: "t,c,bw{100,100,0}:trimSpace", + name: "t,c,bw,trim{100,100,0,true}", total: 100, current: 0, barWidth: 100, @@ -39,7 +176,7 @@ want: " [>-----------------------------------------------------------------------------------------------] ", }, { - name: "t,c,bw{100,1,100}:trimSpace", + name: "t,c,bw,trim{100,1,100,true}", total: 100, current: 1, barWidth: 100, @@ -47,28 +184,53 @@ want: "[>-------------------------------------------------------------------------------------------------]", }, { - name: "t,c,bw{100,40,100}", + name: "t,c,bw{100,33,100}", + total: 100, + current: 33, + barWidth: 100, + want: " [===============================>----------------------------------------------------------------] ", + }, + { + name: "t,c,bw,trim{100,33,100,true}", + total: 100, + current: 33, + barWidth: 100, + trimSpace: true, + want: "[===============================>------------------------------------------------------------------]", + }, + { + name: "t,c,bw,rup{100,33,100,33}", + total: 100, + current: 33, + barWidth: 100, + rup: 33, + want: " [+++++++++++++++++++++++++++++++>----------------------------------------------------------------] ", + }, + { + name: "t,c,bw,rup,trim{100,33,100,33,true}", + total: 100, + current: 33, + barWidth: 100, + rup: 33, + trimSpace: true, + want: "[+++++++++++++++++++++++++++++++>------------------------------------------------------------------]", + }, + { + name: "t,c,bw,rup{100,40,100,32}", total: 100, current: 40, barWidth: 100, - want: " [=====================================>----------------------------------------------------------] ", - }, - { - name: "t,c,bw,rup{100,40,100,32}", - total: 100, - current: 40, - barWidth: 100, - rup: 32, - want: " [+++++++++++++++++++++++++++++++======>----------------------------------------------------------] ", - }, - { - name: "t,c,bw,rup{100,40,100,32}:trimSpace", + rup: 33, + want: " [++++++++++++++++++++++++++++++++=====>----------------------------------------------------------] ", + }, + { + name: "t,c,bw,rup,trim{100,40,100,32,true}", total: 100, current: 40, barWidth: 100, - rup: 32, - trimSpace: true, - want: "[+++++++++++++++++++++++++++++++=======>-----------------------------------------------------------]", + rup: 33, + trimSpace: true, + want: "[++++++++++++++++++++++++++++++++======>-----------------------------------------------------------]", }, { name: "t,c,bw{100,99,100}", @@ -78,131 +240,27 @@ want: " [==============================================================================================>-] ", }, { + name: "t,c,bw,trim{100,99,100,true}", + total: 100, + current: 99, + barWidth: 100, + trimSpace: true, + want: "[================================================================================================>-]", + }, + { name: "t,c,bw{100,100,100}", total: 100, current: 100, barWidth: 100, want: " [================================================================================================] ", }, - }, - 2: { - { - name: "t,c,bw{0,0,100}", - barWidth: 100, - want: " [] ", - }, - { - name: "t,c,bw{60,20,80}", - total: 60, - current: 20, - barWidth: 80, - want: " [] ", - }, - }, - 4: { - { - name: "t,c,bw{100,20,100}", - total: 100, - current: 20, - barWidth: 100, - want: " [] ", - }, - { - name: "t,c,bw{100,98,100}", - total: 100, - current: 98, - barWidth: 100, - want: " [] ", - }, - { - name: "t,c,bw{100,100,100}", - total: 100, - current: 100, - barWidth: 100, - want: " [] ", - }, - }, - 8: { - { - name: "t,c,bw{100,20,100}", - total: 100, - current: 20, - barWidth: 100, - want: " [>---] ", - }, - { - name: "t,c,bw{100,98,100}", - total: 100, - current: 98, - barWidth: 100, - want: " [====] ", - }, - { - name: "t,c,bw{100,100,100}", - total: 100, - current: 100, - barWidth: 100, - want: " [====] ", - }, - }, - 20: { - { - name: "t,c,bw{100,20,100}", - total: 100, - current: 20, - barWidth: 100, - want: " [==>-------------] ", - }, - { - name: "t,c,bw{100,60,100}", - total: 100, - current: 60, - barWidth: 100, - want: " [=========>------] ", - }, - { - name: "t,c,bw{100,98,100}", - total: 100, - current: 98, - barWidth: 100, - want: " [================] ", - }, - { - name: "t,c,bw{100,100,100}", - total: 100, - current: 100, - barWidth: 100, - want: " [================] ", - }, - }, - 50: { - { - name: "t,c,bw{100,20,100}", - total: 100, - current: 20, - barWidth: 100, - want: " [========>-------------------------------------] ", - }, - { - name: "t,c,bw{100,60,100}", - total: 100, - current: 60, - barWidth: 100, - want: " [===========================>------------------] ", - }, - { - name: "t,c,bw{100,98,100}", - total: 100, - current: 98, - barWidth: 100, - want: " [============================================>-] ", - }, - { - name: "t,c,bw{100,100,100}", - total: 100, - current: 100, - barWidth: 100, - want: " [==============================================] ", + { + name: "t,c,bw,trim{100,100,100,true}", + total: 100, + current: 100, + barWidth: 100, + trimSpace: true, + want: "[==================================================================================================]", }, }, } @@ -224,9 +282,14 @@ tmpBuf.ReadFrom(s.draw(termWidth)) by := tmpBuf.Bytes() by = by[:len(by)-1] + + if utf8.RuneCount(by) > termWidth { + t.Errorf("termWidth:%d %q barWidth:%d overflow termWidth\n", termWidth, tc.name, utf8.RuneCount(by)) + } + got := string(by) if got != tc.want { - t.Errorf("termWidth %d; %s: want: %q %d, got: %q %d\n", termWidth, tc.name, tc.want, len(tc.want), got, len(got)) + t.Errorf("termWidth:%d %q want: %q %d, got: %q %d\n", termWidth, tc.name, tc.want, len(tc.want), got, len(got)) } } } @@ -234,7 +297,7 @@ func newTestState() *bState { s := &bState{ - filler: &barFiller{format: defaultBarStyle}, + filler: newDefaultBarFiller(), bufP: new(bytes.Buffer), bufB: new(bytes.Buffer), bufA: new(bytes.Buffer), diff --git a/progress.go b/progress.go index 494441b..eee86ee 100644 --- a/progress.go +++ b/progress.go @@ -83,10 +83,7 @@ // AddBar creates a new progress bar and adds to the container. func (p *Progress) AddBar(total int64, options ...BarOption) *Bar { - filler := &barFiller{ - format: defaultBarStyle, - } - return p.Add(total, filler, options...) + return p.Add(total, newDefaultBarFiller(), options...) } // AddSpinner creates a new spinner bar and adds to the container.