forked from Code-Hex/Neo-cowsay
/
buffer.go
146 lines (131 loc) 路 2.65 KB
/
buffer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package screen
import (
"bytes"
"fmt"
"io"
"strings"
)
// buffer is the global screen buffer
// Its not recommended write to buffer dirrectly, use package Print,Printf,Println functions instead.
var buffer strings.Builder
// Flush buffer and ensure that it will not overflow screen
func Flush() string {
defer buffer.Reset()
return buffer.String()
}
// MoveWriter is implemented io.Writer and io.StringWriter.
type MoveWriter struct {
idx int
x, y int
w io.Writer
buf bytes.Buffer
}
var _ interface {
io.Writer
io.StringWriter
} = (*MoveWriter)(nil)
// NewMoveWriter creates a new MoveWriter.
func NewMoveWriter(w io.Writer, x, y int) *MoveWriter {
x, y = getXY(x, y)
return &MoveWriter{
w: w,
x: x,
y: y,
}
}
// SetPosx sets pos x
func (m *MoveWriter) SetPosx(x int) {
x, _ = getXY(x, 0)
m.x = x
}
// Reset resets
func (m *MoveWriter) Reset() {
m.idx = 0
m.buf.Reset()
}
// Write writes bytes. which is implemented io.Writer.
func (m *MoveWriter) Write(bs []byte) (nn int, _ error) {
br := bytes.NewReader(bs)
for {
b, err := br.ReadByte()
if err != nil && err != io.EOF {
return 0, err
}
if err == io.EOF {
n, _ := fmt.Fprintf(m.w, "\x1b[%d;%dH%s\x1b[0K",
m.y+m.idx,
m.x,
m.buf.String(),
)
nn += n
return
}
if b == '\n' {
n, _ := fmt.Fprintf(m.w, "\x1b[%d;%dH%s\x1b[0K",
m.y+m.idx,
m.x,
m.buf.String(),
)
m.buf.Reset()
m.idx++
nn += n
} else {
m.buf.WriteByte(b)
}
}
}
// WriteString writes string. which is implemented io.StringWriter.
func (m *MoveWriter) WriteString(s string) (nn int, _ error) {
for _, char := range s {
if char == '\n' {
n, _ := fmt.Fprintf(m.w, "\x1b[%d;%dH%s\x1b[0K",
m.y+m.idx,
m.x,
m.buf.String(),
)
m.buf.Reset()
m.idx++
nn += n
} else {
m.buf.WriteRune(char)
}
}
if m.buf.Len() > 0 {
n, _ := fmt.Fprintf(m.w, "\x1b[%d;%dH%s\x1b[0K",
m.y+m.idx,
m.x,
m.buf.String(),
)
nn += n
}
return
}
// getXY gets relative or absolute coorditantes
// To get relative, set PCT flag to number:
//
// // Get 10% of total width to `x` and 20 to y
// x, y = tm.GetXY(10|tm.PCT, 20)
//
func getXY(x int, y int) (int, int) {
// Set percent flag: num | PCT
//
// Check percent flag: num & PCT
//
// Reset percent flag: num & 0xFF
const shift = uint(^uint(0)>>63) << 4
const PCT = 0x8000 << shift
if y == -1 {
y = currentHeight() + 1
}
if x&PCT != 0 {
x = int((x & 0xFF) * Width() / 100)
}
if y&PCT != 0 {
y = int((y & 0xFF) * Height() / 100)
}
return x, y
}
// currentHeight returns current height. Line count in Screen buffer.
func currentHeight() int {
return strings.Count(buffer.String(), "\n")
}