-
Notifications
You must be signed in to change notification settings - Fork 2.3k
/
highlighting.go
121 lines (106 loc) · 2.32 KB
/
highlighting.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 scan
import (
"bytes"
"fmt"
"strings"
"sync"
"github.com/alecthomas/chroma"
"github.com/alecthomas/chroma/formatters"
"github.com/alecthomas/chroma/lexers"
"github.com/alecthomas/chroma/styles"
)
type cache struct {
sync.RWMutex
data map[string][]string
}
func (c *cache) Get(key string) ([]string, bool) {
c.RLock()
defer c.RUnlock()
data, ok := c.data[key]
return data, ok
}
func (c *cache) Set(key string, data []string) {
c.Lock()
defer c.Unlock()
c.data[key] = data
}
var globalCache = &cache{
data: make(map[string][]string),
}
func highlight(fsKey, filename string, startLine, endLine int, input, theme string) []string {
key := fmt.Sprintf("%s|%s|%d-%d", fsKey, filename, startLine, endLine)
if lines, ok := globalCache.Get(key); ok {
return lines
}
lexer := lexers.Match(filename)
if lexer == nil {
lexer = lexers.Fallback
}
lexer = chroma.Coalesce(lexer)
style := styles.Get(theme)
if style == nil {
style = styles.Fallback
}
formatter := formatters.Get("terminal256")
if formatter == nil {
formatter = formatters.Fallback
}
iterator, err := lexer.Tokenise(nil, input)
if err != nil {
return nil
}
var buffer bytes.Buffer
if err := formatter.Format(&buffer, style, iterator); err != nil {
return nil
}
raw := shiftANSIOverLineEndings(buffer.Bytes())
lines := strings.Split(string(raw), "\n")
globalCache.Set(key, lines)
return lines
}
func shiftANSIOverLineEndings(input []byte) []byte {
var output []byte
prev := byte(0)
inCSI := false
csiShouldCarry := false
var csi []byte
var skipOutput bool
for _, r := range input {
skipOutput = false
if !inCSI {
switch {
case r == '\n':
if csiShouldCarry && len(csi) > 0 {
skipOutput = true
output = append(output, '\n')
output = append(output, csi...)
csi = nil
csiShouldCarry = false
}
case r == '[' && prev == 0x1b:
inCSI = true
csi = append(csi, 0x1b, '[')
output = output[:len(output)-1]
skipOutput = true
default:
csiShouldCarry = false
if len(csi) > 0 {
output = append(output, csi...)
csi = nil
}
}
} else {
csi = append(csi, r)
skipOutput = true
if r >= 0x40 && r <= 0x7E {
csiShouldCarry = true
inCSI = false
}
}
if !skipOutput {
output = append(output, r)
}
prev = r
}
return append(output, csi...)
}