on main help sign in
fix(ui): cache glamour renderers
19197e3Christian Rocha
internal/ui/common/markdown.go+34−2
@@ -2,6 +2,7 @@ package common
2
2
3import (
3import (
4 "image/color"
4 "image/color"
5+ "sync"
5
6
6 "charm.land/glamour/v2"
7 "charm.land/glamour/v2"
7 "github.com/alecthomas/chroma/v2/formatters"
8 "github.com/alecthomas/chroma/v2/formatters"
@@ -18,24 +19,55 @@ func init() {
18 formatters.Register(formatterName, xchroma.Formatter(zero, nil))
19 formatters.Register(formatterName, xchroma.Formatter(zero, nil))
19}
20}
20
21
22+var (
23+ mdCacheMu sync.Mutex
24+ mdCache = map[int]*glamour.TermRenderer{}
25+ quietMDCache = map[int]*glamour.TermRenderer{}
26+)
27+
21// MarkdownRenderer returns a glamour [glamour.TermRenderer] configured with
28// MarkdownRenderer returns a glamour [glamour.TermRenderer] configured with
22−// the given styles and width.
29+// the given styles and width. Renderers are memoized per width and shared
30+// across callers; call InvalidateMarkdownRendererCache when the active
31+// styles change.
23func MarkdownRenderer(sty *styles.Styles, width int) *glamour.TermRenderer {
32func MarkdownRenderer(sty *styles.Styles, width int) *glamour.TermRenderer {
33+ mdCacheMu.Lock()
34+ defer mdCacheMu.Unlock()
35+ if r, ok := mdCache[width]; ok {
36+ return r
37+ }
24 r, _ := glamour.NewTermRenderer(
38 r, _ := glamour.NewTermRenderer(
25 glamour.WithStyles(sty.Markdown),
39 glamour.WithStyles(sty.Markdown),
26 glamour.WithWordWrap(width),
40 glamour.WithWordWrap(width),
27 glamour.WithChromaFormatter(formatterName),
41 glamour.WithChromaFormatter(formatterName),
28 )
42 )
43+ mdCache[width] = r
29 return r
44 return r
30}
45}
31
46
32// QuietMarkdownRenderer returns a glamour [glamour.TermRenderer] with no colors
47// QuietMarkdownRenderer returns a glamour [glamour.TermRenderer] with no colors
33−// (plain text with structure) and the given width.
48+// (plain text with structure) and the given width. Renderers are memoized per
49+// width and shared across callers.
34func QuietMarkdownRenderer(sty *styles.Styles, width int) *glamour.TermRenderer {
50func QuietMarkdownRenderer(sty *styles.Styles, width int) *glamour.TermRenderer {
51+ mdCacheMu.Lock()
52+ defer mdCacheMu.Unlock()
53+ if r, ok := quietMDCache[width]; ok {
54+ return r
55+ }
35 r, _ := glamour.NewTermRenderer(
56 r, _ := glamour.NewTermRenderer(
36 glamour.WithStyles(sty.QuietMarkdown),
57 glamour.WithStyles(sty.QuietMarkdown),
37 glamour.WithWordWrap(width),
58 glamour.WithWordWrap(width),
38 glamour.WithChromaFormatter(formatterName),
59 glamour.WithChromaFormatter(formatterName),
39 )
60 )
61+ quietMDCache[width] = r
40 return r
62 return r
41}
63}
64+
65+// InvalidateMarkdownRendererCache drops every cached renderer. Call this
66+// whenever the active styles change so subsequent renderers pick up the new
67+// ansi.StyleConfig.
68+func InvalidateMarkdownRendererCache() {
69+ mdCacheMu.Lock()
70+ defer mdCacheMu.Unlock()
71+ mdCache = map[int]*glamour.TermRenderer{}
72+ quietMDCache = map[int]*glamour.TermRenderer{}
73+}
internal/ui/model/ui.go+1
@@ -3116,6 +3116,7 @@ func (m *UI) refreshStyles() {
3116 )
3116 )
3117 m.todoSpinner.Style = t.Pills.TodoSpinner
3117 m.todoSpinner.Style = t.Pills.TodoSpinner
3118 m.status.help.Styles = t.Help
3118 m.status.help.Styles = t.Help
3119+ common.InvalidateMarkdownRendererCache()
3119 m.chat.InvalidateRenderCaches()
3120 m.chat.InvalidateRenderCaches()
3120}
3121}
3121
3122