forked from c-bata/go-prompt
-
Notifications
You must be signed in to change notification settings - Fork 0
/
status_bar.go
113 lines (100 loc) · 2.01 KB
/
status_bar.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
package prompt
import (
"strings"
runewidth "github.com/mattn/go-runewidth"
)
type StatusBar []StatusElement
func (sb StatusBar) Equals(o StatusBar) bool {
if len(sb) != len(o) {
return false
}
for i := 0; i < len(sb); i++ {
if !sb[i].Equals(o[i]) {
return false
}
}
return true
}
func (els StatusBar) fit(maxWidth int) StatusBar {
if len(els) == 0 {
return StatusBar{{Text: strings.Repeat(" ", maxWidth)}}
}
contentWidth := els.contentWidth()
if contentWidth == maxWidth {
return els
}
factor := 1
delta := maxWidth - contentWidth
if delta < 0 {
factor = -1
// delta = -delta
}
realElastic, adjustedElastic := els.countElastic()
elastics, elasticsIndex := distribute(delta, adjustedElastic), 0
excess := 0
l := len(els)
clones := make(StatusBar, l)
for i, el := range els {
clone := el
clones[i] = clone
if !el.isElastic(i, l, realElastic) {
continue
}
remainder := 0
clones[i], remainder = clone.fit(elastics[elasticsIndex])
remainder *= factor
if remainder > 0 {
if i+1 < adjustedElastic {
elastics[elasticsIndex+1] += remainder
} else {
excess += remainder
}
}
elasticsIndex++
}
// TODO: distribute excess across non-elastic elements and truncate them as well
// TODO: finally, any remaining excess should be distributed across all elements
// (applied in reverse) without adding more ellipses
return clones
}
func (els StatusBar) contentWidth() (w int) {
for _, el := range els {
w += runewidth.StringWidth(el.Text)
}
return w
}
func (els StatusBar) countElastic() (real, adjusted int) {
for _, el := range els {
if el.Elastic {
real++
}
}
adjusted = real
if real == 0 {
adjusted = 1 // At least one element must be elastic, defaulting to the last element
}
return
}
func distribute(v, n int) []int {
if n <= 0 {
panic("n <= 0")
}
r := make([]int, n)
if v == 0 {
return r
}
f := 1
if v < 0 {
f = -1
v = -v
}
d := v / n
m := v % n
for i := 0; i < n; i++ {
r[i] = f * d
if i < m {
r[i] += f
}
}
return r
}