-
Notifications
You must be signed in to change notification settings - Fork 47
/
table.go
146 lines (121 loc) · 2.86 KB
/
table.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
package dstask
import (
"fmt"
"strconv"
"strings"
)
type Table struct {
Header []string
Rows [][]string
RowStyles []RowStyle
Width int
}
type RowStyle struct {
// ansi mode
Mode int
// xterm 256-colour palette
Fg int
Bg int
}
// header may havetruncated words
func NewTable(w int, header ...string) *Table {
if w > TABLE_MAX_WIDTH {
w = TABLE_MAX_WIDTH
}
return &Table{
Header: header,
Width: w,
RowStyles: []RowStyle{
{
Mode: MODE_HEADER,
},
},
}
}
func FixStr(text string, width int) string {
// remove after newline
text = strings.Split(text, "\n")[0]
if len(text) <= width {
return fmt.Sprintf("%-"+strconv.Itoa(width)+"v", text)
}
return text[:width]
}
func (t *Table) AddRow(row []string, style RowStyle) {
if len(row) != len(t.Header) {
panic("Row is incorrect length")
}
t.Rows = append(t.Rows, row)
t.RowStyles = append(t.RowStyles, style)
}
// render table, returning count of rows rendered
// gap of zero means fit terminal exactly by truncating table -- you will want
// a larger gap to account for prompt or other text. A gap of -1 means the row
// count is not limited -- useful for reports or inspecting tasks.
func (t *Table) Render() {
originalWidths := make([]int, len(t.Header))
for _, row := range t.Rows {
for j, cell := range row {
if originalWidths[j] < len(cell) {
originalWidths[j] = len(cell)
}
}
}
// initialise with original size and reduce interatively
widths := originalWidths[:]
// account for gaps of 2 chrs
widthBudget := t.Width - TABLE_COL_GAP*(len(t.Header)-1)
for SumInts(widths...) > widthBudget {
// find max col width index
var max, maxi int
for i, w := range widths {
if w > max {
max = w
maxi = i
}
}
// decrement, if 0 abort
if widths[maxi] == 0 {
break
}
widths[maxi] = widths[maxi] - 1
}
rows := append([][]string{t.Header}, t.Rows...)
for i, row := range rows {
mode := t.RowStyles[i].Mode
fg := t.RowStyles[i].Fg
bg := t.RowStyles[i].Bg
// defaults
if mode == 0 {
mode = MODE_DEFAULT
}
if fg == 0 {
fg = FG_DEFAULT
}
if bg == 0 {
/// alternate if not specified
if i%2 != 0 {
bg = BG_DEFAULT_1
} else {
bg = BG_DEFAULT_2
}
}
cells := row[:]
for i, w := range widths {
trimmed := FixStr(cells[i], w)
// support ' / ' markup -- show notes faded. Insert ANSI escape
// formatting, ensuring reset to original colour for given row.
if strings.Contains(trimmed, " "+NOTE_MODE_KEYWORD+" ") {
trimmed = strings.Replace(
FixStr(cells[i], w+2),
" "+NOTE_MODE_KEYWORD+" ",
fmt.Sprintf("\033[38;5;%dm ", FG_NOTE),
1,
) + fmt.Sprintf("\033[38;5;%dm", fg)
}
cells[i] = trimmed
}
line := strings.Join(cells, strings.Repeat(" ", TABLE_COL_GAP))
// print style, line then reset
fmt.Printf("\033[%d;38;5;%d;48;5;%dm%s\033[0m\n", mode, fg, bg, line)
}
}