-
-
Notifications
You must be signed in to change notification settings - Fork 41
/
padding.go
143 lines (121 loc) · 2.76 KB
/
padding.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
package padding
import (
"bytes"
"io"
"strings"
"github.com/mattn/go-runewidth"
"github.com/muesli/reflow/ansi"
)
type PaddingFunc func(w io.Writer)
type Writer struct {
Padding uint
PadFunc PaddingFunc
ansiWriter *ansi.Writer
buf bytes.Buffer
cache bytes.Buffer
lineLen int
ansi bool
}
func NewWriter(width uint, paddingFunc PaddingFunc) *Writer {
w := &Writer{
Padding: width,
PadFunc: paddingFunc,
}
w.ansiWriter = &ansi.Writer{
Forward: &w.buf,
}
return w
}
func NewWriterPipe(forward io.Writer, width uint, paddingFunc PaddingFunc) *Writer {
return &Writer{
Padding: width,
PadFunc: paddingFunc,
ansiWriter: &ansi.Writer{
Forward: forward,
},
}
}
// Bytes is shorthand for declaring a new default padding-writer instance,
// used to immediately pad a byte slice.
func Bytes(b []byte, width uint) []byte {
f := NewWriter(width, nil)
_, _ = f.Write(b)
_ = f.Flush()
return f.Bytes()
}
// String is shorthand for declaring a new default padding-writer instance,
// used to immediately pad a string.
func String(s string, width uint) string {
return string(Bytes([]byte(s), width))
}
// Write is used to write content to the padding buffer.
func (w *Writer) Write(b []byte) (int, error) {
for _, c := range string(b) {
if c == '\x1B' {
// ANSI escape sequence
w.ansi = true
} else if w.ansi {
if (c >= 0x41 && c <= 0x5a) || (c >= 0x61 && c <= 0x7a) {
// ANSI sequence terminated
w.ansi = false
}
} else {
w.lineLen += runewidth.StringWidth(string(c))
if c == '\n' {
// end of current line
err := w.pad()
if err != nil {
return 0, err
}
w.ansiWriter.ResetAnsi()
w.lineLen = 0
}
}
_, err := w.ansiWriter.Write([]byte(string(c)))
if err != nil {
return 0, err
}
}
return len(b), nil
}
func (w *Writer) pad() error {
if w.Padding > 0 && uint(w.lineLen) < w.Padding {
if w.PadFunc != nil {
for i := 0; i < int(w.Padding)-w.lineLen; i++ {
w.PadFunc(w.ansiWriter)
}
} else {
_, err := w.ansiWriter.Write([]byte(strings.Repeat(" ", int(w.Padding)-w.lineLen)))
if err != nil {
return err
}
}
}
return nil
}
// Close will finish the padding operation.
func (w *Writer) Close() (err error) {
return w.Flush()
}
// Bytes returns the padded result as a byte slice.
func (w *Writer) Bytes() []byte {
return w.cache.Bytes()
}
// String returns the padded result as a string.
func (w *Writer) String() string {
return w.cache.String()
}
// Flush will finish the padding operation. Always call it before trying to
// retrieve the final result.
func (w *Writer) Flush() (err error) {
if w.lineLen != 0 {
if err = w.pad(); err != nil {
return
}
}
w.cache.Reset()
_, err = w.buf.WriteTo(&w.cache)
w.lineLen = 0
w.ansi = false
return
}