Skip to content

Commit f1bd95b

Browse files
feat: add 'new line' capability to spinner (#506)
we should be able to create new spinners based on what is being printed on the screen. this is useful when we want to create multiple spinners while running commands like `kots install`.
1 parent 1291e25 commit f1bd95b

File tree

3 files changed

+53
-2
lines changed

3 files changed

+53
-2
lines changed

pkg/spinner/options.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@ func WithWriter(w WriteFn) Option {
1010
}
1111
}
1212

13+
// WithLineBreaker sets a function that determines if if is time
14+
// to break the line thus creating a new spinner line. The previous
15+
// step is flagged as success.
16+
func WithLineBreaker(lb LineBreakerFn) Option {
17+
return func(m *MessageWriter) {
18+
m.lbreak = lb
19+
}
20+
}
21+
1322
// WithMask sets the MaskFn on the MessageWriter.
1423
func WithMask(mfn MaskFn) Option {
1524
return func(m *MessageWriter) {

pkg/spinner/spinner.go

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ var blocks = []string{"◐", "◓", "◑", "◒"}
1212
// WriteFn is a function that writes a formatted string.
1313
type WriteFn func(string, ...any) (int, error)
1414

15+
// LineBreakerFn is a function that determines if it is time to break the
16+
// line thus creating a new spinner line. The previous step is flagged as
17+
// success.
18+
type LineBreakerFn func(string) bool
19+
1520
// MaskFn is a function that masks a message. Receives a string and
1621
// returns a string, the returned string is printed to the terminal.
1722
type MaskFn func(string) string
@@ -23,6 +28,7 @@ type MessageWriter struct {
2328
err bool
2429
printf WriteFn
2530
mask MaskFn
31+
lbreak LineBreakerFn
2632
}
2733

2834
// Write implements io.Writer for the MessageWriter.
@@ -100,19 +106,31 @@ func (m *MessageWriter) loop() {
100106
case <-ticker.C:
101107
counter++
102108
}
109+
110+
var lbreak bool
111+
if m.lbreak != nil {
112+
lbreak = m.lbreak(message)
113+
}
114+
103115
if m.mask != nil {
104116
message = m.mask(message)
105117
}
118+
106119
pos := counter % len(blocks)
107120
if !end {
108-
_, _ = m.printf("\033[K\r%s %s ", blocks[pos], message)
121+
prefix, suffix := blocks[pos], ""
122+
if lbreak {
123+
prefix, suffix = "✔", "\n"
124+
}
125+
m.printf("\033[K\r%s %s %s", prefix, message, suffix)
109126
continue
110127
}
128+
111129
prefix := "✔"
112130
if m.err {
113131
prefix = "✗"
114132
}
115-
_, _ = m.printf("\033[K\r%s %s\n", prefix, message)
133+
m.printf("\033[K\r%s %s\n", prefix, message)
116134
close(m.end)
117135
return
118136
}

pkg/spinner/spinner_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"fmt"
66
"io"
7+
"strings"
78
"testing"
89
"time"
910

@@ -107,3 +108,26 @@ func TestMask(t *testing.T) {
107108
assert.Contains(t, buf.String(), "masked 0")
108109
assert.Contains(t, buf.String(), "masked 1")
109110
}
111+
112+
func TestLineBreak(t *testing.T) {
113+
buf := bytes.NewBuffer(nil)
114+
lbreak := func(s string) bool {
115+
return s == "test 3" || s == "test 8"
116+
}
117+
pb := Start(
118+
WithWriter(WriteTo(buf)),
119+
WithLineBreaker(lbreak),
120+
)
121+
for i := 0; i < 100; i++ {
122+
pb.Infof("test %d", i)
123+
}
124+
pb.Close()
125+
// we expect the following output:
126+
// ✔ test 3 (\n)
127+
// ✔ test 8 (\n)
128+
// ✔ test 99 (\n)
129+
assert.Equal(t, strings.Count(buf.String(), "\n"), 3)
130+
assert.Contains(t, buf.String(), "test 3")
131+
assert.Contains(t, buf.String(), "test 8")
132+
assert.Contains(t, buf.String(), "test 99")
133+
}

0 commit comments

Comments
 (0)