-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
/
ansi_renderer.go
121 lines (106 loc) · 3.01 KB
/
ansi_renderer.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
package main
import (
"bytes"
"fmt"
"strings"
"golang.org/x/text/unicode/norm"
)
func lenWithoutANSI(text, shell string) int {
rANSI := "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))"
stripped := replaceAllString(rANSI, text, "")
switch shell {
case zsh:
stripped = strings.ReplaceAll(stripped, "%{", "")
stripped = strings.ReplaceAll(stripped, "%}", "")
case bash:
stripped = strings.ReplaceAll(stripped, "\\[", "")
stripped = strings.ReplaceAll(stripped, "\\]", "")
}
var i norm.Iter
i.InitString(norm.NFD, stripped)
var count int
for !i.Done() {
i.Next()
count++
}
return count
}
type formats struct {
linechange string
left string
right string
creset string
clearOEL string
saveCursorPosition string
restoreCursorPosition string
}
// AnsiRenderer exposes functionality using ANSI
type AnsiRenderer struct {
buffer *bytes.Buffer
formats *formats
shell string
}
func (r *AnsiRenderer) init(shell string) {
r.shell = shell
r.formats = &formats{}
switch shell {
case zsh:
r.formats.linechange = "%%{\x1b[%d%s%%}"
r.formats.left = "%%{\x1b[%dC%%}"
r.formats.right = "%%{\x1b[%dD%%}"
r.formats.creset = "%{\x1b[0m%}"
r.formats.clearOEL = "%{\x1b[K%}"
r.formats.saveCursorPosition = "%{\x1b7%}"
r.formats.restoreCursorPosition = "%{\x1b8%}"
case bash:
r.formats.linechange = "\\[\x1b[%d%s\\]"
r.formats.left = "\\[\x1b[%dC\\]"
r.formats.right = "\\[\x1b[%dD\\]"
r.formats.creset = "\\[\x1b[0m\\]"
r.formats.clearOEL = "\\[\x1b[K\\]"
r.formats.saveCursorPosition = "\\[\x1b7\\]"
r.formats.restoreCursorPosition = "\\[\x1b8\\]"
default:
r.formats.linechange = "\x1b[%d%s"
r.formats.left = "\x1b[%dC"
r.formats.right = "\x1b[%dD"
r.formats.creset = "\x1b[0m"
r.formats.clearOEL = "\x1b[K"
r.formats.saveCursorPosition = "\x1b7"
r.formats.restoreCursorPosition = "\x1b8"
}
}
func (r *AnsiRenderer) carriageForward() {
r.buffer.WriteString(fmt.Sprintf(r.formats.left, 1000))
}
func (r *AnsiRenderer) setCursorForRightWrite(text string, offset int) {
strippedLen := lenWithoutANSI(text, r.shell) + -offset
r.buffer.WriteString(fmt.Sprintf(r.formats.right, strippedLen))
}
func (r *AnsiRenderer) changeLine(numberOfLines int) {
position := "B"
if numberOfLines < 0 {
position = "F"
numberOfLines = -numberOfLines
}
r.buffer.WriteString(fmt.Sprintf(r.formats.linechange, numberOfLines, position))
}
func (r *AnsiRenderer) creset() {
r.buffer.WriteString(r.formats.creset)
}
func (r *AnsiRenderer) print(text string) {
r.buffer.WriteString(text)
r.clearEOL()
}
func (r *AnsiRenderer) clearEOL() {
r.buffer.WriteString(r.formats.clearOEL)
}
func (r *AnsiRenderer) string() string {
return r.buffer.String()
}
func (r *AnsiRenderer) saveCursorPosition() {
r.buffer.WriteString(r.formats.saveCursorPosition)
}
func (r *AnsiRenderer) restoreCursorPosition() {
r.buffer.WriteString(r.formats.restoreCursorPosition)
}