-
Notifications
You must be signed in to change notification settings - Fork 45
/
Copy pathoutput.go
125 lines (111 loc) · 2.47 KB
/
output.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
package terminal
import (
"fmt"
"html"
"sort"
"strings"
)
type outputBuffer struct {
buf strings.Builder
}
func (b *outputBuffer) appendNodeStyle(n node) {
b.buf.WriteString(`<span class="`)
for idx, class := range n.style.asClasses() {
if idx > 0 {
b.buf.WriteString(" ")
}
b.buf.WriteString(class)
}
b.buf.WriteString(`">`)
}
func (b *outputBuffer) closeStyle() {
b.buf.WriteString("</span>")
}
func (b *outputBuffer) appendMeta(namespace string, data map[string]string) {
// We pre-sort the keys to guarantee alphabetical output,
// because Golang `map`s have guaranteed disorder
keys := make([]string, len(data))
// Make a list of the map's keys
i := 0
for key := range data {
keys[i] = key
i++
}
sort.Strings(keys)
b.buf.WriteString("<?" + namespace)
for i := range keys {
key := keys[i]
fmt.Fprintf(&b.buf, ` %s="%s"`, key, html.EscapeString(data[key]))
}
b.buf.WriteString("?>")
}
// Append a character to our outputbuffer, escaping HTML bits as necessary.
func (b *outputBuffer) appendChar(char rune) {
switch char {
case '&':
b.buf.WriteString("&")
case '\'':
b.buf.WriteString("'")
case '<':
b.buf.WriteString("<")
case '>':
b.buf.WriteString(">")
case '"':
b.buf.WriteString(""")
case '/':
b.buf.WriteString("/")
default:
b.buf.WriteRune(char)
}
}
// asHTML returns the line with HTML formatting.
func (l *screenLine) asHTML() string {
var spanOpen bool
var lineBuf outputBuffer
if data, ok := l.metadata[bkNamespace]; ok {
lineBuf.appendMeta(bkNamespace, data)
}
for idx, node := range l.nodes {
if idx == 0 {
if !node.style.isPlain() {
lineBuf.appendNodeStyle(node)
spanOpen = true
}
} else {
previous := l.nodes[idx-1]
if !node.hasSameStyle(previous) {
if spanOpen {
lineBuf.closeStyle()
spanOpen = false
}
if !node.style.isPlain() {
lineBuf.appendNodeStyle(node)
spanOpen = true
}
}
}
if node.style.element() {
lineBuf.buf.WriteString(l.elements[node.blob].asHTML())
} else {
lineBuf.appendChar(node.blob)
}
}
if spanOpen {
lineBuf.closeStyle()
}
line := strings.TrimRight(lineBuf.buf.String(), " \t")
if line == "" {
return " "
}
return line
}
// asPlain returns the line contents without any added HTML.
func (l *screenLine) asPlain() string {
var buf strings.Builder
for _, node := range l.nodes {
if !node.style.element() {
buf.WriteRune(node.blob)
}
}
return strings.TrimRight(buf.String(), " \t")
}