diff --git a/cwriter/export_test.go b/cwriter/export_test.go deleted file mode 100644 index 1219508..0000000 --- a/cwriter/export_test.go +++ /dev/null @@ -1,3 +0,0 @@ -package cwriter - -var ClearCursorAndLine = clearCursorAndLine diff --git a/cwriter/writer.go b/cwriter/writer.go index 638237c..2ffc6c6 100644 --- a/cwriter/writer.go +++ b/cwriter/writer.go @@ -7,51 +7,39 @@ "io" "os" - isatty "github.com/mattn/go-isatty" "golang.org/x/crypto/ssh/terminal" ) -// ESC is the ASCII code for escape character -const ESC = 27 - var NotATTY = errors.New("not a terminal") -var ( - cursorUp = fmt.Sprintf("%c[%dA", ESC, 1) - clearLine = fmt.Sprintf("%c[2K\r", ESC) - clearCursorAndLine = cursorUp + clearLine -) +var cuuAndEd = fmt.Sprintf("%c[%%dA%[1]c[J", 27) -// Writer is a buffered the writer that updates the terminal. The +// 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 buf bytes.Buffer + lineCount int + fd uintptr isTerminal bool - fd int - lineCount int } // New returns a new Writer with defaults func New(out io.Writer) *Writer { w := &Writer{out: out} if f, ok := out.(*os.File); ok { - fd := f.Fd() - w.isTerminal = isatty.IsTerminal(fd) - w.fd = int(fd) + w.fd = f.Fd() + w.isTerminal = terminal.IsTerminal(int(w.fd)) } return w } // Flush flushes the underlying buffer -func (w *Writer) Flush(lineCount int) error { - err := w.clearLines() +func (w *Writer) Flush(lineCount int) (err error) { + w.clearLines() w.lineCount = lineCount - // WriteTo takes care of w.buf.Reset - if _, e := w.buf.WriteTo(w.out); err == nil { - err = e - } - return err + _, err = w.buf.WriteTo(w.out) + return } // Write appends the contents of p to the underlying buffer @@ -73,7 +61,7 @@ // GetWidth returns width of underlying terminal. func (w *Writer) GetWidth() (int, error) { if w.isTerminal { - tw, _, err := terminal.GetSize(w.fd) + tw, _, err := terminal.GetSize(int(w.fd)) return tw, err } return -1, NotATTY diff --git a/cwriter/writer_posix.go b/cwriter/writer_posix.go index 05e31c4..3fb8b7d 100644 --- a/cwriter/writer_posix.go +++ b/cwriter/writer_posix.go @@ -2,12 +2,8 @@ package cwriter -import ( - "io" - "strings" -) +import "fmt" -func (w *Writer) clearLines() error { - _, err := io.WriteString(w.out, strings.Repeat(clearCursorAndLine, w.lineCount)) - return err +func (w *Writer) clearLines() { + fmt.Fprintf(w.out, cuuAndEd, w.lineCount) } diff --git a/cwriter/writer_posix_test.go b/cwriter/writer_posix_test.go deleted file mode 100644 index 31e9d22..0000000 --- a/cwriter/writer_posix_test.go +++ /dev/null @@ -1,36 +0,0 @@ -// +build !windows - -package cwriter_test - -import ( - "bytes" - "testing" - - . "github.com/vbauerster/mpb/v4/cwriter" -) - -// TestWriterPosix by writing and flushing many times. The output buffer -// must contain the clearCursor and clearLine sequences. -func TestWriterPosix(t *testing.T) { - out := new(bytes.Buffer) - w := New(out) - - for _, tcase := range []struct { - input, expectedOutput string - }{ - {input: "foo\n", expectedOutput: "foo\n"}, - {input: "bar\n", expectedOutput: "foo\n" + ClearCursorAndLine + "bar\n"}, - {input: "fizz\n", expectedOutput: "foo\n" + ClearCursorAndLine + "bar\n" + ClearCursorAndLine + "fizz\n"}, - } { - t.Run(tcase.input, func(t *testing.T) { - s := []byte(tcase.input) - lc := bytes.Count(s, []byte("\n")) - w.Write(s) - w.Flush(lc) - output := out.String() - if output != tcase.expectedOutput { - t.Fatalf("want %q, got %q", tcase.expectedOutput, output) - } - }) - } -} diff --git a/cwriter/writer_windows.go b/cwriter/writer_windows.go index 747a634..dd5b10c 100644 --- a/cwriter/writer_windows.go +++ b/cwriter/writer_windows.go @@ -3,12 +3,9 @@ package cwriter import ( - "io" - "strings" + "fmt" "syscall" "unsafe" - - isatty "github.com/mattn/go-isatty" ) var kernel32 = syscall.NewLazyDLL("kernel32.dll") @@ -20,58 +17,48 @@ procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute") ) -type ( - short int16 - word uint16 - dword uint32 +type short int16 +type dword uint32 +type word uint16 - coord struct { - x short - y short - } - smallRect struct { - left short - top short - right short - bottom short - } - consoleScreenBufferInfo struct { - size coord - cursorPosition coord - attributes word - window smallRect - maximumWindowSize coord - } -) - -// FdWriter is a writer with a file descriptor. -type FdWriter interface { - io.Writer - Fd() uintptr +type coord struct { + x short + y short } -func (w *Writer) clearLines() error { - f, ok := w.out.(FdWriter) - if ok && !isatty.IsTerminal(f.Fd()) { - _, err := io.WriteString(w.out, strings.Repeat(clearCursorAndLine, w.lineCount)) - return err +type smallRect struct { + left short + top short + right short + bottom short +} + +type consoleScreenBufferInfo struct { + size coord + cursorPosition coord + attributes word + window smallRect + maximumWindowSize coord +} + +func (w *Writer) clearLines() { + if !w.isTerminal { + fmt.Fprintf(w.out, cuuAndEd, w.lineCount) } - fd := f.Fd() var info consoleScreenBufferInfo - procGetConsoleScreenBufferInfo.Call(fd, uintptr(unsafe.Pointer(&info))) + procGetConsoleScreenBufferInfo.Call(w.fd, uintptr(unsafe.Pointer(&info))) for i := 0; i < w.lineCount; i++ { // move the cursor up info.cursorPosition.y-- - procSetConsoleCursorPosition.Call(fd, uintptr(*(*int32)(unsafe.Pointer(&info.cursorPosition)))) + procSetConsoleCursorPosition.Call(w.fd, uintptr(*(*int32)(unsafe.Pointer(&info.cursorPosition)))) // clear the line cursor := coord{ x: info.window.left, y: info.window.top + info.cursorPosition.y, } - var count, w dword + var count, dw dword count = dword(info.size.x) - procFillConsoleOutputCharacter.Call(fd, uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&w))) + procFillConsoleOutputCharacter.Call(w.fd, uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&dw))) } - return nil }