diff --git a/bar.go b/bar.go index 3b946de..1fa6725 100644 --- a/bar.go +++ b/bar.go @@ -4,6 +4,7 @@ "bytes" "fmt" "io" + "strings" "sync" "time" "unicode/utf8" @@ -68,9 +69,9 @@ char rune till int64 } - writeBuf struct { - buf []byte - completeAfterFlush bool + bufReader struct { + io.Reader + complete bool } ) @@ -286,8 +287,8 @@ } } -func (b *Bar) render(tw int, prependWs, appendWs *widthSync) <-chan *writeBuf { - ch := make(chan *writeBuf, 1) +func (b *Bar) render(tw int, prependWs, appendWs *widthSync) <-chan *bufReader { + ch := make(chan *bufReader, 1) go func() { select { @@ -299,34 +300,28 @@ s.prependFuncs = nil s.appendFuncs = nil - ch <- &writeBuf{[]byte(s.panic), true} + ch <- &bufReader{strings.NewReader(s.panic), true} } close(ch) }() s.draw(tw, prependWs, appendWs) - ch <- &writeBuf{s.toBytes(), s.completed} + ch <- &bufReader{io.MultiReader(s.bufP, s.bufB, s.bufA), s.completed} }: case <-b.done: s := b.cacheState - var buf []byte + var r io.Reader if s.panic != "" { - buf = []byte(s.panic) + r = strings.NewReader(s.panic) } else { s.draw(tw, prependWs, appendWs) - buf = s.toBytes() + r = io.MultiReader(s.bufP, s.bufB, s.bufA) } - ch <- &writeBuf{buf, false} + ch <- &bufReader{r, false} close(ch) } }() return ch -} - -func (s *state) toBytes() []byte { - buf := make([]byte, 0, s.bufP.Len()+s.bufB.Len()+s.bufA.Len()) - buf = concatenateBlocks(buf, s.bufP.Bytes(), s.bufB.Bytes(), s.bufA.Bytes()) - return buf } func (s *state) updateTimePerItemEstimate(amount int, now, next time.Time) { @@ -433,13 +428,6 @@ } } -func concatenateBlocks(buf []byte, blocks ...[]byte) []byte { - for _, block := range blocks { - buf = append(buf, block...) - } - return buf -} - func (s *state) updateFormat(format string) { for i, n := 0, 0; len(format) > 0; i++ { s.format[i], n = utf8.DecodeRuneInString(format) diff --git a/cwriter/writer.go b/cwriter/writer.go index 1de453a..050f93e 100644 --- a/cwriter/writer.go +++ b/cwriter/writer.go @@ -18,37 +18,38 @@ // Writer is a buffered the writer that updates the terminal. // The contents of writer will be flushed when Flush is called. type Writer struct { - out io.Writer - + out io.Writer buf bytes.Buffer lineCount int } // New returns a new Writer with defaults func New(w io.Writer) *Writer { - return &Writer{ - out: w, - } + return &Writer{out: w} } // Flush flushes the underlying buffer func (w *Writer) Flush() error { - // Do nothing if buffer is empty - if w.buf.Len() == 0 { - return nil + err := w.clearLines() + w.lineCount = bytes.Count(w.buf.Bytes(), []byte("\n")) + // WriteTo takes care of w.buf.Reset + if _, e := w.buf.WriteTo(w.out); err == nil { + err = e } - w.clearLines() - w.lineCount = bytes.Count(w.buf.Bytes(), []byte("\n")) - _, err := w.out.Write(w.buf.Bytes()) - w.buf.Reset() return err } -// Write save the contents of b to its buffers. The only errors returned are ones encountered while writing to the underlying buffer. -func (w *Writer) Write(b []byte) (n int, err error) { - return w.buf.Write(b) +// Write appends the contents of p to the underlying buffer +func (w *Writer) Write(p []byte) (n int, err error) { + return w.buf.Write(p) } +// WriteString writes string to the underlying buffer func (w *Writer) WriteString(s string) (n int, err error) { return w.buf.WriteString(s) } + +// ReadFrom reads from the provided io.Reader and writes to the underlying buffer. +func (w *Writer) ReadFrom(r io.Reader) (n int64, err error) { + return w.buf.ReadFrom(r) +} diff --git a/cwriter/writer_posix.go b/cwriter/writer_posix.go index c6a1551..7d5c1f7 100644 --- a/cwriter/writer_posix.go +++ b/cwriter/writer_posix.go @@ -3,14 +3,15 @@ package cwriter import ( - "fmt" + "io" "strings" "syscall" "unsafe" ) -func (w *Writer) clearLines() { - fmt.Fprint(w.out, strings.Repeat(clearCursorAndLine, w.lineCount)) +func (w *Writer) clearLines() error { + _, err := io.WriteString(w.out, strings.Repeat(clearCursorAndLine, w.lineCount)) + return err } // TermSize returns the dimensions of the given terminal. diff --git a/cwriter/writer_windows.go b/cwriter/writer_windows.go index a08f37a..951933d 100644 --- a/cwriter/writer_windows.go +++ b/cwriter/writer_windows.go @@ -3,11 +3,11 @@ package cwriter import ( - "fmt" "io" + "strings" "syscall" "unsafe" - "strings" + "github.com/mattn/go-isatty" ) @@ -50,11 +50,11 @@ Fd() uintptr } -func (w *Writer) clearLines() { +func (w *Writer) clearLines() error { f, ok := w.out.(FdWriter) if ok && !isatty.IsTerminal(f.Fd()) { - fmt.Fprint(w.out, strings.Repeat(clearCursorAndLine, w.lineCount)) - return + _, err := io.WriteString(w.out, strings.Repeat(clearCursorAndLine, w.lineCount)) + return err } fd := f.Fd() var info consoleScreenBufferInfo @@ -73,11 +73,12 @@ count = dword(info.size.x) procFillConsoleOutputCharacter.Call(fd, uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&w))) } + return nil } -// GetTermSize returns the dimensions of the given terminal. +// TermSize returns the dimensions of the given terminal. // the code is stolen from "golang.org/x/crypto/ssh/terminal" -func GetTermSize() (width, height int, err error) { +func TermSize() (width, height int, err error) { var info consoleScreenBufferInfo _, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(syscall.Stdout), uintptr(unsafe.Pointer(&info)), 0) if e != 0 { diff --git a/progress.go b/progress.go index 61bf563..975d25e 100644 --- a/progress.go +++ b/progress.go @@ -295,19 +295,19 @@ prependWs := newWidthSync(wSyncTimeout, len(p.bars), numP) appendWs := newWidthSync(wSyncTimeout, len(p.bars), numA) - sequence := make([]<-chan *writeBuf, len(p.bars)) + sequence := make([]<-chan *bufReader, len(p.bars)) for i, b := range p.bars { sequence[i] = b.render(tw, prependWs, appendWs) } var i int - for b := range fanIn(sequence...) { - _, err = p.cw.Write(b.buf) + for r := range fanIn(sequence...) { + _, err = p.cw.ReadFrom(r) defer func(bar *Bar, complete bool) { if complete { bar.Complete() } - }(p.bars[i], b.completeAfterFlush) + }(p.bars[i], r.complete) i++ } @@ -321,8 +321,8 @@ return } -func fanIn(inputs ...<-chan *writeBuf) <-chan *writeBuf { - ch := make(chan *writeBuf) +func fanIn(inputs ...<-chan *bufReader) <-chan *bufReader { + ch := make(chan *bufReader) go func() { defer close(ch)