diff --git a/bar_filler_bar.go b/bar_filler_bar.go index 8979417..c930ac3 100644 --- a/bar_filler_bar.go +++ b/bar_filler_bar.go @@ -27,14 +27,17 @@ Refiller(string) BarStyleComposer Padding(string) BarStyleComposer Tip(...string) BarStyleComposer + Inclusive(tipOnCompleteIndex uint) BarStyleComposer Reverse() BarStyleComposer } type bFiller struct { components [components]*component tip struct { - count uint - frames []*component + count uint + onCompleteIndex uint + inclusive bool + frames []*component } flush func(dst io.Writer, filling, padding [][]byte) } @@ -45,13 +48,15 @@ } type barStyle struct { - lbound string - rbound string - filler string - refiller string - padding string - tip []string - rev bool + lbound string + rbound string + filler string + refiller string + padding string + tip []string + tipOnCompleteIndex uint + tipInclusive bool + rev bool } // BarStyle constructs default bar style which can be altered via @@ -99,6 +104,12 @@ return s } +func (s *barStyle) Inclusive(tipOnCompleteIndex uint) BarStyleComposer { + s.tipInclusive = true + s.tipOnCompleteIndex = tipOnCompleteIndex + return s +} + func (s *barStyle) Reverse() BarStyleComposer { s.rev = true return s @@ -133,6 +144,8 @@ width: runewidth.StringWidth(stripansi.Strip(s.padding)), bytes: []byte(s.padding), } + bf.tip.inclusive = s.tipInclusive + bf.tip.onCompleteIndex = s.tipOnCompleteIndex bf.tip.frames = make([]*component, len(s.tip)) for i, t := range s.tip { bf.tip.frames[i] = &component{ @@ -155,14 +168,31 @@ w.Write(s.components[iLbound].bytes) defer w.Write(s.components[iRbound].bytes) + var filling [][]byte + var padding [][]byte + var tip *component + var refWidth int curWidth := int(internal.PercentageRound(stat.Total, stat.Current, width)) - refWidth, filled := 0, curWidth - filling := make([][]byte, 0, curWidth) - - if curWidth > 0 && curWidth != width { - tipFrame := s.tip.frames[s.tip.count%uint(len(s.tip.frames))] - filling = append(filling, tipFrame.bytes) - curWidth -= tipFrame.width + filled := curWidth + + if stat.Current == stat.Total { + if s.tip.onCompleteIndex >= uint(len(s.tip.frames)) { + s.tip.onCompleteIndex = 0 + } + tip = s.tip.frames[s.tip.onCompleteIndex] + } else { + tip = s.tip.frames[s.tip.count%uint(len(s.tip.frames))] + } + + if s.tip.inclusive { + if curWidth > 0 { + filling = append(filling, tip.bytes) + curWidth -= tip.width + s.tip.count++ + } + } else if curWidth < width { + filling = append(filling, tip.bytes) + filled += tip.width s.tip.count++ } @@ -192,7 +222,6 @@ filled -= curWidth + refWidth padWidth := width - filled - padding := make([][]byte, 0, padWidth) for padWidth > 0 && padWidth >= s.components[iPadding].width { padding = append(padding, s.components[iPadding].bytes) padWidth -= s.components[iPadding].width diff --git a/draw_test.go b/draw_test.go index dbd3bc0..87865de 100644 --- a/draw_test.go +++ b/draw_test.go @@ -75,7 +75,7 @@ total: 60, current: 20, trim: true, - want: "[-]", + want: "[>]", }, }, 4: { @@ -90,7 +90,7 @@ total: 60, current: 20, trim: true, - want: "[>-]", + want: "[=>]", }, }, 5: { @@ -98,14 +98,14 @@ name: "t,c{60,20}", total: 60, current: 20, - want: " [-] ", - }, - { - name: "t,c{60,20}trim", - total: 60, - current: 20, - trim: true, - want: "[>--]", + want: " [>] ", + }, + { + name: "t,c{60,20}trim", + total: 60, + current: 20, + trim: true, + want: "[=>-]", }, }, 6: { @@ -113,14 +113,14 @@ name: "t,c{60,20}", total: 60, current: 20, - want: " [>-] ", - }, - { - name: "t,c{60,20}trim", - total: 60, - current: 20, - trim: true, - want: "[>---]", + want: " [=>] ", + }, + { + name: "t,c{60,20}trim", + total: 60, + current: 20, + trim: true, + want: "[=>--]", }, }, 7: { @@ -128,14 +128,14 @@ name: "t,c{60,20}", total: 60, current: 20, - want: " [>--] ", - }, - { - name: "t,c{60,20}trim", - total: 60, - current: 20, - trim: true, - want: "[=>---]", + want: " [=>-] ", + }, + { + name: "t,c{60,20}trim", + total: 60, + current: 20, + trim: true, + want: "[==>--]", }, }, 8: { @@ -143,14 +143,14 @@ name: "t,c{60,20}", total: 60, current: 20, - want: " [>---] ", - }, - { - name: "t,c{60,20}trim", - total: 60, - current: 20, - trim: true, - want: "[=>----]", + want: " [=>--] ", + }, + { + name: "t,c{60,20}trim", + total: 60, + current: 20, + trim: true, + want: "[==>---]", }, }, 80: { @@ -158,21 +158,21 @@ name: "t,c{60,20}", total: 60, current: 20, - want: " [========================>---------------------------------------------------] ", - }, - { - name: "t,c{60,20}trim", - total: 60, - current: 20, - trim: true, - want: "[=========================>----------------------------------------------------]", + want: " [=========================>--------------------------------------------------] ", + }, + { + name: "t,c{60,20}trim", + total: 60, + current: 20, + trim: true, + want: "[==========================>---------------------------------------------------]", }, { name: "t,c,bw{60,20,60}", total: 60, current: 20, barWidth: 60, - want: " [==================>---------------------------------------] ", + want: " [===================>--------------------------------------] ", }, { name: "t,c,bw{60,20,60}trim", @@ -180,7 +180,7 @@ current: 20, barWidth: 60, trim: true, - want: "[==================>---------------------------------------]", + want: "[===================>--------------------------------------]", }, }, 99: { @@ -196,42 +196,42 @@ name: "[のだつ] t,c{99,2}", total: 99, current: 2, - want: " [だつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ", + want: " [のだつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ", }, { style: BarStyle().Lbound("[").Filler("の").Tip("だ").Padding("つ").Rbound("]"), name: "[のだつ] t,c{99,3}", total: 99, current: 3, - want: " [だつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ", + want: " [のだつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ", }, { style: BarStyle().Lbound("[").Filler("の").Tip("だ").Padding("つ").Rbound("]"), name: "[のだつ] t,c{99,4}", total: 99, current: 4, - want: " [のだつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ", + want: " [ののだつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ", }, { style: BarStyle().Lbound("[").Filler("の").Tip("だ").Padding("つ").Rbound("]"), name: "[のだつ] t,c{99,5}", total: 99, current: 5, - want: " [のだつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ", + want: " [ののだつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ", }, { style: BarStyle().Lbound("[").Filler("の").Tip("だ").Padding("つ").Rbound("]"), name: "[のだつ] t,c{99,6}", total: 99, current: 6, - want: " [ののだつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ", + want: " [のののだつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ", }, { style: BarStyle().Lbound("[").Filler("の").Tip("だ").Padding("つ").Rbound("]").Reverse(), name: "[のだつ] t,c{99,6}rev", total: 99, current: 6, - want: " […つつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつだのの] ", + want: " […つつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつだののの] ", }, }, 100: { @@ -239,40 +239,40 @@ name: "t,c{100,0}", total: 100, current: 0, - want: " [------------------------------------------------------------------------------------------------] ", + want: " [>-----------------------------------------------------------------------------------------------] ", }, { name: "t,c{100,0}trim", total: 100, current: 0, trim: true, - want: "[--------------------------------------------------------------------------------------------------]", + want: "[>-------------------------------------------------------------------------------------------------]", }, { name: "t,c{100,1}", total: 100, current: 1, - want: " [>-----------------------------------------------------------------------------------------------] ", + want: " [=>----------------------------------------------------------------------------------------------] ", }, { name: "t,c{100,1}trim", total: 100, current: 1, trim: true, - want: "[>-------------------------------------------------------------------------------------------------]", + want: "[=>------------------------------------------------------------------------------------------------]", }, { name: "t,c{100,99}", total: 100, current: 99, - want: " [==============================================================================================>-] ", + want: " [===============================================================================================>] ", }, { name: "t,c{100,99}trim", total: 100, current: 99, trim: true, - want: "[================================================================================================>-]", + want: "[=================================================================================================>]", }, { name: "t,c{100,100}", @@ -299,14 +299,14 @@ name: "t,c{100,33}", total: 100, current: 33, - want: " [===============================>----------------------------------------------------------------] ", + want: " [================================>---------------------------------------------------------------] ", }, { name: "t,c{100,33}trim", total: 100, current: 33, trim: true, - want: "[===============================>------------------------------------------------------------------]", + want: "[================================>-----------------------------------------------------------------]", }, { style: BarStyle().Tip("<").Reverse(), @@ -314,14 +314,14 @@ total: 100, current: 33, trim: true, - want: "[------------------------------------------------------------------<===============================]", + want: "[-----------------------------------------------------------------<================================]", }, { name: "t,c,r{100,33,33}", total: 100, current: 33, refill: 33, - want: " [+++++++++++++++++++++++++++++++>----------------------------------------------------------------] ", + want: " [++++++++++++++++++++++++++++++++>---------------------------------------------------------------] ", }, { name: "t,c,r{100,33,33}trim", @@ -329,7 +329,7 @@ current: 33, refill: 33, trim: true, - want: "[+++++++++++++++++++++++++++++++>------------------------------------------------------------------]", + want: "[++++++++++++++++++++++++++++++++>-----------------------------------------------------------------]", }, { style: BarStyle().Tip("<").Reverse(), @@ -338,14 +338,14 @@ current: 33, refill: 33, trim: true, - want: "[------------------------------------------------------------------<+++++++++++++++++++++++++++++++]", + want: "[-----------------------------------------------------------------<++++++++++++++++++++++++++++++++]", }, { name: "t,c,r{100,40,33}", total: 100, current: 40, refill: 33, - want: " [++++++++++++++++++++++++++++++++=====>----------------------------------------------------------] ", + want: " [++++++++++++++++++++++++++++++++======>---------------------------------------------------------] ", }, { name: "t,c,r{100,40,33}trim", @@ -353,7 +353,7 @@ current: 40, refill: 33, trim: true, - want: "[++++++++++++++++++++++++++++++++======>-----------------------------------------------------------]", + want: "[++++++++++++++++++++++++++++++++=======>----------------------------------------------------------]", }, { style: BarStyle().Tip("<").Reverse(), @@ -361,7 +361,7 @@ total: 100, current: 40, refill: 33, - want: " [----------------------------------------------------------<=====++++++++++++++++++++++++++++++++] ", + want: " [---------------------------------------------------------<======++++++++++++++++++++++++++++++++] ", }, { style: BarStyle().Tip("<").Reverse(), @@ -370,18 +370,7 @@ current: 40, refill: 33, trim: true, - want: "[-----------------------------------------------------------<======++++++++++++++++++++++++++++++++]", - }, - }, - 197: { - { - name: "t,c,r{97486999,2805950,2805483}trim", - total: 97486999, - current: 2805950, - refill: 2805483, - barWidth: 60, - trim: true, - want: "[+>--------------------------------------------------------]", + want: "[----------------------------------------------------------<=======++++++++++++++++++++++++++++++++]", }, }, } @@ -413,6 +402,402 @@ } } +func TestDrawTipInclusive(t *testing.T) { + // key is termWidth + testSuite := map[int][]struct { + style BarStyleComposer + name string + total int64 + current int64 + refill int64 + barWidth int + trim bool + want string + }{ + 0: { + { + name: "t,c{60,20}", + total: 60, + current: 20, + want: "", + }, + { + name: "t,c{60,20}trim", + total: 60, + current: 20, + trim: true, + want: "", + }, + }, + 1: { + { + name: "t,c{60,20}", + total: 60, + current: 20, + want: "", + }, + { + name: "t,c{60,20}trim", + total: 60, + current: 20, + trim: true, + want: "", + }, + }, + 2: { + { + name: "t,c{60,20}", + total: 60, + current: 20, + want: " ", + }, + { + name: "t,c{60,20}trim", + total: 60, + current: 20, + trim: true, + want: "[]", + }, + }, + 3: { + { + name: "t,c{60,20}", + total: 60, + current: 20, + want: " ", + }, + { + name: "t,c{60,20}trim", + total: 60, + current: 20, + trim: true, + want: "[-]", + }, + }, + 4: { + { + name: "t,c{60,20}", + total: 60, + current: 20, + want: " [] ", + }, + { + name: "t,c{60,20}trim", + total: 60, + current: 20, + trim: true, + want: "[>-]", + }, + }, + 5: { + { + name: "t,c{60,20}", + total: 60, + current: 20, + want: " [-] ", + }, + { + name: "t,c{60,20}trim", + total: 60, + current: 20, + trim: true, + want: "[>--]", + }, + }, + 6: { + { + name: "t,c{60,20}", + total: 60, + current: 20, + want: " [>-] ", + }, + { + name: "t,c{60,20}trim", + total: 60, + current: 20, + trim: true, + want: "[>---]", + }, + }, + 7: { + { + name: "t,c{60,20}", + total: 60, + current: 20, + want: " [>--] ", + }, + { + name: "t,c{60,20}trim", + total: 60, + current: 20, + trim: true, + want: "[=>---]", + }, + }, + 8: { + { + name: "t,c{60,20}", + total: 60, + current: 20, + want: " [>---] ", + }, + { + name: "t,c{60,20}trim", + total: 60, + current: 20, + trim: true, + want: "[=>----]", + }, + }, + 80: { + { + name: "t,c{60,20}", + total: 60, + current: 20, + want: " [========================>---------------------------------------------------] ", + }, + { + name: "t,c{60,20}trim", + total: 60, + current: 20, + trim: true, + want: "[=========================>----------------------------------------------------]", + }, + { + name: "t,c,bw{60,20,60}", + total: 60, + current: 20, + barWidth: 60, + want: " [==================>---------------------------------------] ", + }, + { + name: "t,c,bw{60,20,60}trim", + total: 60, + current: 20, + barWidth: 60, + trim: true, + want: "[==================>---------------------------------------]", + }, + }, + 99: { + { + style: BarStyle().Lbound("[").Filler("の").Tip("だ").Padding("つ").Rbound("]"), + name: "[のだつ] t,c{99,1}", + total: 99, + current: 1, + want: " [だつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ", + }, + { + style: BarStyle().Lbound("[").Filler("の").Tip("だ").Padding("つ").Rbound("]"), + name: "[のだつ] t,c{99,2}", + total: 99, + current: 2, + want: " [だつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ", + }, + { + style: BarStyle().Lbound("[").Filler("の").Tip("だ").Padding("つ").Rbound("]"), + name: "[のだつ] t,c{99,3}", + total: 99, + current: 3, + want: " [だつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ", + }, + { + style: BarStyle().Lbound("[").Filler("の").Tip("だ").Padding("つ").Rbound("]"), + name: "[のだつ] t,c{99,4}", + total: 99, + current: 4, + want: " [のだつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ", + }, + { + style: BarStyle().Lbound("[").Filler("の").Tip("だ").Padding("つ").Rbound("]"), + name: "[のだつ] t,c{99,5}", + total: 99, + current: 5, + want: " [のだつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ", + }, + { + style: BarStyle().Lbound("[").Filler("の").Tip("だ").Padding("つ").Rbound("]"), + name: "[のだつ] t,c{99,6}", + total: 99, + current: 6, + want: " [ののだつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ", + }, + { + style: BarStyle().Lbound("[").Filler("の").Tip("だ").Padding("つ").Rbound("]").Reverse(), + name: "[のだつ] t,c{99,6}rev", + total: 99, + current: 6, + want: " […つつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつだのの] ", + }, + }, + 100: { + { + name: "t,c{100,0}", + total: 100, + current: 0, + want: " [------------------------------------------------------------------------------------------------] ", + }, + { + name: "t,c{100,0}trim", + total: 100, + current: 0, + trim: true, + want: "[--------------------------------------------------------------------------------------------------]", + }, + { + name: "t,c{100,1}", + total: 100, + current: 1, + want: " [>-----------------------------------------------------------------------------------------------] ", + }, + { + name: "t,c{100,1}trim", + total: 100, + current: 1, + trim: true, + want: "[>-------------------------------------------------------------------------------------------------]", + }, + { + name: "t,c{100,99}", + total: 100, + current: 99, + want: " [==============================================================================================>-] ", + }, + { + name: "t,c{100,99}trim", + total: 100, + current: 99, + trim: true, + want: "[================================================================================================>-]", + }, + { + name: "t,c{100,100}", + total: 100, + current: 100, + want: " [===============================================================================================>] ", + }, + { + name: "t,c{100,100}trim", + total: 100, + current: 100, + trim: true, + want: "[=================================================================================================>]", + }, + { + name: "t,c,r{100,100,100}trim", + total: 100, + current: 100, + refill: 100, + trim: true, + want: "[+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>]", + }, + { + name: "t,c{100,33}", + total: 100, + current: 33, + want: " [===============================>----------------------------------------------------------------] ", + }, + { + name: "t,c{100,33}trim", + total: 100, + current: 33, + trim: true, + want: "[===============================>------------------------------------------------------------------]", + }, + { + style: BarStyle().Tip("<").Reverse(), + name: "t,c{100,33}trim,rev", + total: 100, + current: 33, + trim: true, + want: "[------------------------------------------------------------------<===============================]", + }, + { + name: "t,c,r{100,33,33}", + total: 100, + current: 33, + refill: 33, + want: " [+++++++++++++++++++++++++++++++>----------------------------------------------------------------] ", + }, + { + name: "t,c,r{100,33,33}trim", + total: 100, + current: 33, + refill: 33, + trim: true, + want: "[+++++++++++++++++++++++++++++++>------------------------------------------------------------------]", + }, + { + style: BarStyle().Tip("<").Reverse(), + name: "t,c,r{100,33,33}trim,rev", + total: 100, + current: 33, + refill: 33, + trim: true, + want: "[------------------------------------------------------------------<+++++++++++++++++++++++++++++++]", + }, + { + name: "t,c,r{100,40,33}", + total: 100, + current: 40, + refill: 33, + want: " [++++++++++++++++++++++++++++++++=====>----------------------------------------------------------] ", + }, + { + name: "t,c,r{100,40,33}trim", + total: 100, + current: 40, + refill: 33, + trim: true, + want: "[++++++++++++++++++++++++++++++++======>-----------------------------------------------------------]", + }, + { + style: BarStyle().Tip("<").Reverse(), + name: "t,c,r{100,40,33},rev", + total: 100, + current: 40, + refill: 33, + want: " [----------------------------------------------------------<=====++++++++++++++++++++++++++++++++] ", + }, + { + style: BarStyle().Tip("<").Reverse(), + name: "t,c,r{100,40,33}trim,rev", + total: 100, + current: 40, + refill: 33, + trim: true, + want: "[-----------------------------------------------------------<======++++++++++++++++++++++++++++++++]", + }, + }, + } + + var tmpBuf bytes.Buffer + for tw, cases := range testSuite { + for _, tc := range cases { + if tc.style == nil { + tc.style = BarStyle() + } + s := newTestState(NewBarFiller(tc.style.Inclusive(0))) + s.reqWidth = tc.barWidth + s.total = tc.total + s.current = tc.current + s.trimSpace = tc.trim + s.refill = tc.refill + tmpBuf.Reset() + tmpBuf.ReadFrom(s.draw(newStatistics(tw, s))) + by := tmpBuf.Bytes() + + got := string(by[:len(by)-1]) + if !utf8.ValidString(got) { + t.Fail() + } + if got != tc.want { + t.Errorf("termWidth:%d %q want: %q %d, got: %q %d\n", tw, tc.name, tc.want, utf8.RuneCountInString(tc.want), got, utf8.RuneCountInString(got)) + } + } + } +} + func newTestState(filler BarFiller) *bState { s := &bState{ filler: filler,