apps/sipp/tui/highlight.go 1.6 K raw
1
package tui
2
3
import (
4
	"bytes"
5
	"strings"
6
7
	"github.com/alecthomas/chroma/v2"
8
	"github.com/alecthomas/chroma/v2/formatters"
9
	"github.com/alecthomas/chroma/v2/lexers"
10
	"github.com/alecthomas/chroma/v2/styles"
11
)
12
13
const highlightCacheMax = 64
14
15
type highlighter struct {
16
	cache map[string]string
17
	order []string
18
}
19
20
func newHighlighter() *highlighter {
21
	return &highlighter{cache: map[string]string{}}
22
}
23
24
func (h *highlighter) render(shortID, name, content string) string {
25
	if v, ok := h.cache[shortID]; ok {
26
		return v
27
	}
28
	out := highlightCode(name, content)
29
	h.cache[shortID] = out
30
	h.order = append(h.order, shortID)
31
	if len(h.order) > highlightCacheMax {
32
		drop := h.order[0]
33
		h.order = h.order[1:]
34
		delete(h.cache, drop)
35
	}
36
	return out
37
}
38
39
func (h *highlighter) invalidate(shortID string) {
40
	if _, ok := h.cache[shortID]; !ok {
41
		return
42
	}
43
	delete(h.cache, shortID)
44
	for i, k := range h.order {
45
		if k == shortID {
46
			h.order = append(h.order[:i], h.order[i+1:]...)
47
			break
48
		}
49
	}
50
}
51
52
func highlightCode(name, content string) string {
53
	var lexer chroma.Lexer
54
	if name != "" {
55
		lexer = lexers.Match(name)
56
	}
57
	if lexer == nil {
58
		lexer = lexers.Analyse(content)
59
	}
60
	if lexer == nil {
61
		lexer = lexers.Fallback
62
	}
63
	style := styles.Get("vim")
64
	if style == nil {
65
		style = styles.Fallback
66
	}
67
	formatter := formatters.Get("terminal16")
68
	if formatter == nil {
69
		formatter = formatters.Fallback
70
	}
71
	iter, err := lexer.Tokenise(nil, content)
72
	if err != nil {
73
		return content
74
	}
75
	var buf bytes.Buffer
76
	if err := formatter.Format(&buf, style, iter); err != nil {
77
		return content
78
	}
79
	return strings.TrimRight(buf.String(), "\n")
80
}