Codebase list golang-github-vbauerster-mpb / f7d6154
cuuAndEd Vladimir Bauer 7 years ago
5 changed file(s) with 43 addition(s) and 111 deletion(s). Raw diff Collapse all Expand all
+0
-3
cwriter/export_test.go less more
0 package cwriter
1
2 var ClearCursorAndLine = clearCursorAndLine
66 "io"
77 "os"
88
9 isatty "github.com/mattn/go-isatty"
109 "golang.org/x/crypto/ssh/terminal"
1110 )
1211
13 // ESC is the ASCII code for escape character
14 const ESC = 27
15
1612 var NotATTY = errors.New("not a terminal")
1713
18 var (
19 cursorUp = fmt.Sprintf("%c[%dA", ESC, 1)
20 clearLine = fmt.Sprintf("%c[2K\r", ESC)
21 clearCursorAndLine = cursorUp + clearLine
22 )
14 var cuuAndEd = fmt.Sprintf("%c[%%dA%[1]c[J", 27)
2315
24 // Writer is a buffered the writer that updates the terminal. The
16 // Writer is a buffered the writer that updates the terminal. The
2517 // contents of writer will be flushed when Flush is called.
2618 type Writer struct {
2719 out io.Writer
2820 buf bytes.Buffer
21 lineCount int
22 fd uintptr
2923 isTerminal bool
30 fd int
31 lineCount int
3224 }
3325
3426 // New returns a new Writer with defaults
3527 func New(out io.Writer) *Writer {
3628 w := &Writer{out: out}
3729 if f, ok := out.(*os.File); ok {
38 fd := f.Fd()
39 w.isTerminal = isatty.IsTerminal(fd)
40 w.fd = int(fd)
30 w.fd = f.Fd()
31 w.isTerminal = terminal.IsTerminal(int(w.fd))
4132 }
4233 return w
4334 }
4435
4536 // Flush flushes the underlying buffer
46 func (w *Writer) Flush(lineCount int) error {
47 err := w.clearLines()
37 func (w *Writer) Flush(lineCount int) (err error) {
38 w.clearLines()
4839 w.lineCount = lineCount
49 // WriteTo takes care of w.buf.Reset
50 if _, e := w.buf.WriteTo(w.out); err == nil {
51 err = e
52 }
53 return err
40 _, err = w.buf.WriteTo(w.out)
41 return
5442 }
5543
5644 // Write appends the contents of p to the underlying buffer
7260 // GetWidth returns width of underlying terminal.
7361 func (w *Writer) GetWidth() (int, error) {
7462 if w.isTerminal {
75 tw, _, err := terminal.GetSize(w.fd)
63 tw, _, err := terminal.GetSize(int(w.fd))
7664 return tw, err
7765 }
7866 return -1, NotATTY
11
22 package cwriter
33
4 import (
5 "io"
6 "strings"
7 )
4 import "fmt"
85
9 func (w *Writer) clearLines() error {
10 _, err := io.WriteString(w.out, strings.Repeat(clearCursorAndLine, w.lineCount))
11 return err
6 func (w *Writer) clearLines() {
7 fmt.Fprintf(w.out, cuuAndEd, w.lineCount)
128 }
+0
-36
cwriter/writer_posix_test.go less more
0 // +build !windows
1
2 package cwriter_test
3
4 import (
5 "bytes"
6 "testing"
7
8 . "github.com/vbauerster/mpb/v4/cwriter"
9 )
10
11 // TestWriterPosix by writing and flushing many times. The output buffer
12 // must contain the clearCursor and clearLine sequences.
13 func TestWriterPosix(t *testing.T) {
14 out := new(bytes.Buffer)
15 w := New(out)
16
17 for _, tcase := range []struct {
18 input, expectedOutput string
19 }{
20 {input: "foo\n", expectedOutput: "foo\n"},
21 {input: "bar\n", expectedOutput: "foo\n" + ClearCursorAndLine + "bar\n"},
22 {input: "fizz\n", expectedOutput: "foo\n" + ClearCursorAndLine + "bar\n" + ClearCursorAndLine + "fizz\n"},
23 } {
24 t.Run(tcase.input, func(t *testing.T) {
25 s := []byte(tcase.input)
26 lc := bytes.Count(s, []byte("\n"))
27 w.Write(s)
28 w.Flush(lc)
29 output := out.String()
30 if output != tcase.expectedOutput {
31 t.Fatalf("want %q, got %q", tcase.expectedOutput, output)
32 }
33 })
34 }
35 }
22 package cwriter
33
44 import (
5 "io"
6 "strings"
5 "fmt"
76 "syscall"
87 "unsafe"
9
10 isatty "github.com/mattn/go-isatty"
118 )
129
1310 var kernel32 = syscall.NewLazyDLL("kernel32.dll")
1916 procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute")
2017 )
2118
22 type (
23 short int16
24 word uint16
25 dword uint32
19 type short int16
20 type dword uint32
21 type word uint16
2622
27 coord struct {
28 x short
29 y short
30 }
31 smallRect struct {
32 left short
33 top short
34 right short
35 bottom short
36 }
37 consoleScreenBufferInfo struct {
38 size coord
39 cursorPosition coord
40 attributes word
41 window smallRect
42 maximumWindowSize coord
43 }
44 )
45
46 // FdWriter is a writer with a file descriptor.
47 type FdWriter interface {
48 io.Writer
49 Fd() uintptr
23 type coord struct {
24 x short
25 y short
5026 }
5127
52 func (w *Writer) clearLines() error {
53 f, ok := w.out.(FdWriter)
54 if ok && !isatty.IsTerminal(f.Fd()) {
55 _, err := io.WriteString(w.out, strings.Repeat(clearCursorAndLine, w.lineCount))
56 return err
28 type smallRect struct {
29 left short
30 top short
31 right short
32 bottom short
33 }
34
35 type consoleScreenBufferInfo struct {
36 size coord
37 cursorPosition coord
38 attributes word
39 window smallRect
40 maximumWindowSize coord
41 }
42
43 func (w *Writer) clearLines() {
44 if !w.isTerminal {
45 fmt.Fprintf(w.out, cuuAndEd, w.lineCount)
5746 }
58 fd := f.Fd()
5947 var info consoleScreenBufferInfo
60 procGetConsoleScreenBufferInfo.Call(fd, uintptr(unsafe.Pointer(&info)))
48 procGetConsoleScreenBufferInfo.Call(w.fd, uintptr(unsafe.Pointer(&info)))
6149
6250 for i := 0; i < w.lineCount; i++ {
6351 // move the cursor up
6452 info.cursorPosition.y--
65 procSetConsoleCursorPosition.Call(fd, uintptr(*(*int32)(unsafe.Pointer(&info.cursorPosition))))
53 procSetConsoleCursorPosition.Call(w.fd, uintptr(*(*int32)(unsafe.Pointer(&info.cursorPosition))))
6654 // clear the line
6755 cursor := coord{
6856 x: info.window.left,
6957 y: info.window.top + info.cursorPosition.y,
7058 }
71 var count, w dword
59 var count, dw dword
7260 count = dword(info.size.x)
73 procFillConsoleOutputCharacter.Call(fd, uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&w)))
61 procFillConsoleOutputCharacter.Call(w.fd, uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&dw)))
7462 }
75 return nil
7663 }