okami.mom/glamour

git clone https://git.nkp.pet/okami.mom/glamour

Kieran Klukaschore: fix pre-existing lint failuresc293649

main
4.6 KiB151 linesraw
1package ansi
2
3import (
4	"fmt"
5	"io"
6	"sync"
7
8	"github.com/alecthomas/chroma/v2"
9	"github.com/alecthomas/chroma/v2/quick"
10	"github.com/alecthomas/chroma/v2/styles"
11)
12
13const (
14	// The chroma style theme name used for rendering.
15	chromaStyleTheme = "charm"
16
17	// The chroma formatter name used for rendering.
18	chromaFormatter = "terminal256"
19)
20
21// mutex for synchronizing access to the chroma style registry.
22// Related https://github.com/alecthomas/chroma/pull/650
23var mutex = sync.Mutex{}
24
25// A CodeBlockElement is used to render code blocks.
26type CodeBlockElement struct {
27	Code     string
28	Language string
29}
30
31func chromaStyle(style StylePrimitive) string {
32	var s string
33
34	if style.Color != nil {
35		s = *style.Color
36	}
37	if style.BackgroundColor != nil {
38		if s != "" {
39			s += " "
40		}
41		s += "bg:" + *style.BackgroundColor
42	}
43	if style.Italic != nil && *style.Italic {
44		if s != "" {
45			s += " "
46		}
47		s += "italic"
48	}
49	if style.Bold != nil && *style.Bold {
50		if s != "" {
51			s += " "
52		}
53		s += "bold"
54	}
55	if style.Underline != nil && *style.Underline {
56		if s != "" {
57			s += " "
58		}
59		s += "underline"
60	}
61
62	return s
63}
64
65// Render renders a CodeBlockElement.
66func (e *CodeBlockElement) Render(w io.Writer, ctx RenderContext) error {
67	bs := ctx.blockStack
68
69	var indentation uint
70	var margin uint
71	formatter := chromaFormatter
72	rules := ctx.options.Styles.CodeBlock
73	if rules.Indent != nil {
74		indentation = *rules.Indent
75	}
76	if rules.Margin != nil {
77		margin = *rules.Margin
78	}
79	if len(ctx.options.ChromaFormatter) > 0 {
80		formatter = ctx.options.ChromaFormatter
81	}
82	theme := rules.Theme
83
84	if rules.Chroma != nil {
85		theme = chromaStyleTheme
86		mutex.Lock()
87		// Don't register the style if it's already registered.
88		_, ok := styles.Registry[theme]
89		if !ok {
90			styles.Register(chroma.MustNewStyle(theme,
91				chroma.StyleEntries{
92					chroma.Text:                chromaStyle(rules.Chroma.Text),
93					chroma.Error:               chromaStyle(rules.Chroma.Error),
94					chroma.Comment:             chromaStyle(rules.Chroma.Comment),
95					chroma.CommentPreproc:      chromaStyle(rules.Chroma.CommentPreproc),
96					chroma.Keyword:             chromaStyle(rules.Chroma.Keyword),
97					chroma.KeywordReserved:     chromaStyle(rules.Chroma.KeywordReserved),
98					chroma.KeywordNamespace:    chromaStyle(rules.Chroma.KeywordNamespace),
99					chroma.KeywordType:         chromaStyle(rules.Chroma.KeywordType),
100					chroma.Operator:            chromaStyle(rules.Chroma.Operator),
101					chroma.Punctuation:         chromaStyle(rules.Chroma.Punctuation),
102					chroma.Name:                chromaStyle(rules.Chroma.Name),
103					chroma.NameBuiltin:         chromaStyle(rules.Chroma.NameBuiltin),
104					chroma.NameTag:             chromaStyle(rules.Chroma.NameTag),
105					chroma.NameAttribute:       chromaStyle(rules.Chroma.NameAttribute),
106					chroma.NameClass:           chromaStyle(rules.Chroma.NameClass),
107					chroma.NameConstant:        chromaStyle(rules.Chroma.NameConstant),
108					chroma.NameDecorator:       chromaStyle(rules.Chroma.NameDecorator),
109					chroma.NameException:       chromaStyle(rules.Chroma.NameException),
110					chroma.NameFunction:        chromaStyle(rules.Chroma.NameFunction),
111					chroma.NameOther:           chromaStyle(rules.Chroma.NameOther),
112					chroma.Literal:             chromaStyle(rules.Chroma.Literal),
113					chroma.LiteralNumber:       chromaStyle(rules.Chroma.LiteralNumber),
114					chroma.LiteralDate:         chromaStyle(rules.Chroma.LiteralDate),
115					chroma.LiteralString:       chromaStyle(rules.Chroma.LiteralString),
116					chroma.LiteralStringEscape: chromaStyle(rules.Chroma.LiteralStringEscape),
117					chroma.GenericDeleted:      chromaStyle(rules.Chroma.GenericDeleted),
118					chroma.GenericEmph:         chromaStyle(rules.Chroma.GenericEmph),
119					chroma.GenericInserted:     chromaStyle(rules.Chroma.GenericInserted),
120					chroma.GenericStrong:       chromaStyle(rules.Chroma.GenericStrong),
121					chroma.GenericSubheading:   chromaStyle(rules.Chroma.GenericSubheading),
122					chroma.Background:          chromaStyle(rules.Chroma.Background),
123				}))
124		}
125		mutex.Unlock()
126	}
127
128	iw := NewIndentWriter(w, int(indentation+margin), func(_ io.Writer) {
129		_, _ = renderText(w, bs.Current().Style.StylePrimitive, " ")
130	})
131	defer iw.Close() //nolint:errcheck
132
133	if len(theme) > 0 {
134		_, _ = renderText(iw, bs.Current().Style.StylePrimitive, rules.BlockPrefix)
135
136		err := quick.Highlight(iw, e.Code, e.Language, formatter, theme)
137		if err != nil {
138			return fmt.Errorf("glamour: error highlighting code: %w", err)
139		}
140		_, _ = renderText(iw, bs.Current().Style.StylePrimitive, rules.BlockSuffix)
141		return nil
142	}
143
144	// fallback rendering
145	el := &BaseElement{
146		Token: e.Code,
147		Style: rules.StylePrimitive,
148	}
149
150	return el.Render(iw, ctx)
151}