-
Notifications
You must be signed in to change notification settings - Fork 0
/
wrap.go
134 lines (112 loc) · 3.11 KB
/
wrap.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
package dsts
import (
"context"
"time"
)
const (
// wrapDelay specifies for how long the text will "stay" at the beginning
// and at the end, before starting to scroll to the opposite extreme.
wrapDelay = 1 * time.Second
// wrapSpeed specifies how much to wait for before "scrolling" to the next
// character.
wrapSpeed = 300 * time.Millisecond
)
// Wrap limits the width of a provider's statuses.
//
// When the underlying provider returns a status that surpasses the given width
// in characters, the wrapper will output at most `width` characters, and will
// automatically "scroll" back and forth, so the full status can be eventually
// read.
func Wrap(p StatusLineBlockProvider, width int) StatusLineBlockProvider {
return func(ctx context.Context, chOut chan<- StatusLineBlock) error {
chError := make(chan error)
chIn := make(chan StatusLineBlock)
// Start the inner provider.
go func(chError chan<- error) {
chError <- p(ctx, chIn)
}(chError)
var (
sts StatusLineBlock
prevSts StatusLineBlock
)
// Main loop for the outer provider.
//
// Each message from the inner provider triggers a new iteration of
// this loop.
for {
since := time.Now()
offset := 0
direction := 1
var innerError error
Scroll:
for {
select {
case innerError = <-chError:
return innerError
// The underlying provider sent a new message.
case sts = <-chIn:
// If the current message is the same as the previous one,
// ignore it because we don't want to "reset" the current
// position of the scroll.
if sts != prevSts {
prevSts = sts
rs := []rune(sts.FullText)
// Messages shorter or equal than the maximum width
// don't need scrolling, so we just pass them as-is.
if len(rs) <= width {
chOut <- sts
continue
}
text := string(rs[:width])
chOut <- StatusLineBlock{
FullText: text,
Color: sts.Color,
}
break Scroll
}
// Time to "scroll" to the next character.
case <-time.After(wrapSpeed):
rs := []rune(sts.FullText)
// Messages shorter or equal than the maximum width don't
// need scrolling, so we just pass them as-is.
if len(rs) <= width {
continue
}
if time.Now().Sub(since) < wrapDelay {
continue
}
if direction == 1 {
// We're displaying the rightmost part of the status.
//
// Wait for a bit and then reverse the direction of the
// scrolling.
if offset+width >= len(rs) {
direction = -1
since = time.Now()
time.Sleep(wrapDelay)
}
} else {
// We're displaying the leftmost part of the status.
//
// Wait for a bit and then reverse the direction of the
// scrolling.
if offset == 0 {
direction = 1
since = time.Now()
time.Sleep(wrapDelay)
}
}
//
// Scroll by one character and send an update.
//
offset += direction
text := string(rs[offset : offset+width])
chOut <- StatusLineBlock{
FullText: text,
Color: sts.Color,
}
}
}
}
}
}