-
Notifications
You must be signed in to change notification settings - Fork 2
/
ui.go
203 lines (164 loc) · 4.37 KB
/
ui.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
package gommander
import (
"bytes"
"fmt"
"sort"
"strings"
"github.com/fatih/color"
)
type Designation byte
type PredefinedTheme byte
const (
Keyword Designation = iota
Headline
Description
ErrorMsg
Other
)
const (
ColorfulTheme PredefinedTheme = iota
PlainTheme
)
type Formatter struct {
theme Theme
buffer bytes.Buffer
prevOffset int
appRef *Command
}
type Theme = map[Designation]color.Attribute
type FormatGenerator interface {
generate(*Command) (string, string)
}
func NewFormatter(cmd *Command) Formatter {
return Formatter{
theme: cmd.theme,
appRef: cmd,
}
}
func GetPredefinedTheme(val PredefinedTheme) Theme {
switch val {
case ColorfulTheme:
return NewTheme(color.FgGreen, color.FgMagenta, color.FgHiBlue, color.FgHiRed, color.FgHiWhite)
case PlainTheme:
return NewTheme(color.FgWhite, color.FgWhite, color.FgWhite, color.FgWhite, color.FgWhite)
default:
return DefaultTheme()
}
}
// A constructor function that takes in color attributes in a specific order and creates a new theme from the provided color attributes from the `fatih/color` package
func NewTheme(keyword, headline, description, errors, others color.Attribute) Theme {
theme := make(map[Designation]color.Attribute)
theme[Keyword] = keyword
theme[Headline] = headline
theme[Description] = description
theme[ErrorMsg] = errors
theme[Other] = others
return theme
}
// A simple function that returns the default package-defined theme
func DefaultTheme() Theme {
return NewTheme(color.FgCyan, color.FgGreen, color.FgWhite, color.FgRed, color.FgWhite)
}
func (f *Formatter) section(val string) {
f.Add(Headline, fmt.Sprintf("\n%v: \n", strings.ToUpper(val)))
}
func (f *Formatter) discussion(val string) {
// Dedent, indent then word-wrap
text := dedent(val)
// text = fillContent(text, 80)
text = dedent(text)
text = indent(text, " ")
f.Add(Description, text)
f.close()
}
func (f *Formatter) close() {
f.Add(Other, "\n")
}
func (f *Formatter) Add(dsgn Designation, val string) {
c := color.New(f.theme[dsgn])
body := c.Sprintf(val)
if f.appRef.settings[DisableColor] {
body = val
}
f.buffer.WriteString(body)
}
func (f *Formatter) AddAndPrint(dsgn Designation, val string) {
f.Add(dsgn, val)
f.Print()
}
func (f *Formatter) Color(color color.Color, val string) {
colored := color.Sprintf(val)
f.buffer.WriteString(colored)
}
func (f *Formatter) ColorAndPrint(color color.Color, val string) {
f.Color(color, val)
f.Print()
}
func (f *Formatter) Print() {
if isTestMode() {
if f.appRef.settings[DisableColor] {
fmt.Print(f.buffer.String())
} else {
fmt.Print(color.New().Sprintf(f.buffer.String()))
}
} else {
color.New().Printf(f.buffer.String())
}
}
func (f *Formatter) GetString() string {
return color.New().Sprintf(f.buffer.String())
}
func (f *Formatter) format(items []FormatGenerator) {
values := []([2]string){}
// TODO: check for sort alphabetically setting
if f.appRef.settings[SortItemsAlphabetically] {
sort.Slice(items, func(i, j int) bool {
second, _ := items[j].generate(f.appRef)
first, _ := items[i].generate(f.appRef)
return second > first
})
}
for _, i := range items {
leading, floating := i.generate(f.appRef)
temp := [2]string{leading, floating}
values = append(values, temp)
}
maxOffset := 0
currentOffset := 0
// Finds the longest value, adds some padding to it and sets it as the max offset
for _, v := range values {
capacity := len([]byte(v[0]))
if capacity > currentOffset {
currentOffset = capacity + 8 // Padding
}
if capacity > f.prevOffset {
f.prevOffset = currentOffset
}
}
// If different sections have almost similar max_offsets, use equal values
diff := f.prevOffset - currentOffset
if diff < 8 && diff > -8 {
maxOffset = f.prevOffset
} else {
maxOffset = currentOffset
}
for _, v := range values {
leading := v[0]
floating := v[1]
f.printOutput(leading, floating, maxOffset)
}
}
func (f *Formatter) printOutput(leading string, floating string, offset int) {
// TODO: Add support for sentence wrap
buffer := make([]byte, offset)
reader := strings.NewReader(leading)
var tempStr strings.Builder
numBytes, _ := reader.Read(buffer)
tempStr.Write(buffer[:numBytes])
diff := len(buffer) - numBytes
for i := 0; i < diff; i++ {
tempStr.Write([]byte(" "))
}
f.Add(Keyword, fmt.Sprintf(" %v", tempStr.String()))
f.Add(Description, fmt.Sprintf("%v\n", floating))
}