/
colour.go
79 lines (73 loc) · 2.27 KB
/
colour.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
package text
import (
"fmt"
"golang.org/x/net/html"
"strings"
)
// ANSI converts all Minecraft text formatting codes in the values passed to ANSI formatting codes, so that
// it may be displayed properly in the terminal.
func ANSI(a ...any) string {
str := make([]string, len(a))
for i, v := range a {
str[i] = minecraftReplacer.Replace(fmt.Sprint(v))
}
return strings.Join(str, " ")
}
// Colourf colours the format string using HTML tags after first escaping all parameters passed and
// substituting them in the format string. The following colours and formatting may be used:
// black, dark-blue, dark-green, dark-aqua, dark-red, dark-purple, gold, grey, dark-grey, blue, green, aqua,
// red, purple, yellow, white, dark-yellow, obfuscated, bold (b), and italic (i).
// These HTML tags may also be nested, like so:
// `<red>Hello <bold>World</bold>!</red>`
func Colourf(format string, a ...any) string {
str := fmt.Sprintf(format, a...)
e := &enc{w: &strings.Builder{}, first: true}
t := html.NewTokenizer(strings.NewReader(str))
for {
if t.Next() == html.ErrorToken {
break
}
e.process(t.Token())
}
return e.w.String()
}
// enc holds the state of a string to be processed for colour substitution.
type enc struct {
w *strings.Builder
formatStack []string
first bool
}
// process handles a single html.Token and either writes the string to the strings.Builder, adds a colour to
// the stack or removes a colour from the stack.
func (e *enc) process(tok html.Token) {
if e.first {
e.w.WriteString(reset)
e.first = false
}
switch tok.Type {
case html.TextToken:
for _, s := range e.formatStack {
e.w.WriteString(s)
}
e.w.WriteString(tok.Data)
if len(e.formatStack) != 0 {
e.w.WriteString(reset)
}
case html.StartTagToken:
if format, ok := strMap[tok.Data]; ok {
e.formatStack = append(e.formatStack, format)
return
}
// Not a known colour, so just write the token as a string.
e.w.WriteString("<" + tok.Data + ">")
case html.EndTagToken:
for i, format := range e.formatStack {
if f, ok := strMap[tok.Data]; ok && f == format {
e.formatStack = append(e.formatStack[:i], e.formatStack[i+1:]...)
return
}
}
// Not a known colour, so just write the token as a string.
e.w.WriteString("</" + tok.Data + ">")
}
}