Mercurial > yakumo_izuru > aya
comparison vendor/github.com/Depado/bfchroma/v2/renderer.go @ 66:787b5ee0289d draft
Use vendored modules
Signed-off-by: Izuru Yakumo <yakumo.izuru@chaotic.ninja>
author | yakumo.izuru |
---|---|
date | Sun, 23 Jul 2023 13:18:53 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
65:6d985efa0f7a | 66:787b5ee0289d |
---|---|
1 // Package bfchroma provides an easy and extensible blackfriday renderer that | |
2 // uses the chroma syntax highlighter to render code blocks. | |
3 package bfchroma | |
4 | |
5 import ( | |
6 "io" | |
7 | |
8 "github.com/alecthomas/chroma/v2" | |
9 "github.com/alecthomas/chroma/v2/formatters/html" | |
10 "github.com/alecthomas/chroma/v2/lexers" | |
11 "github.com/alecthomas/chroma/v2/styles" | |
12 bf "github.com/russross/blackfriday/v2" | |
13 ) | |
14 | |
15 // Option defines the functional option type | |
16 type Option func(r *Renderer) | |
17 | |
18 // Style is a function option allowing to set the style used by chroma | |
19 // Default : "monokai" | |
20 func Style(s string) Option { | |
21 return func(r *Renderer) { | |
22 r.Style = styles.Get(s) | |
23 } | |
24 } | |
25 | |
26 // ChromaStyle is an option to directly set the style of the renderer using a | |
27 // chroma style instead of a string | |
28 func ChromaStyle(s *chroma.Style) Option { | |
29 return func(r *Renderer) { | |
30 r.Style = s | |
31 } | |
32 } | |
33 | |
34 // WithoutAutodetect disables chroma's language detection when no codeblock | |
35 // extra information is given. It will fallback to a sane default instead of | |
36 // trying to detect the language. | |
37 func WithoutAutodetect() Option { | |
38 return func(r *Renderer) { | |
39 r.Autodetect = false | |
40 } | |
41 } | |
42 | |
43 // EmbedCSS will embed CSS needed for html.WithClasses() in beginning of the document | |
44 func EmbedCSS() Option { | |
45 return func(r *Renderer) { | |
46 r.embedCSS = true | |
47 } | |
48 } | |
49 | |
50 // ChromaOptions allows to pass Chroma html.Option such as Standalone() | |
51 // WithClasses(), ClassPrefix(prefix)... | |
52 func ChromaOptions(options ...html.Option) Option { | |
53 return func(r *Renderer) { | |
54 r.ChromaOptions = options | |
55 } | |
56 } | |
57 | |
58 // Extend allows to specify the blackfriday renderer which is extended | |
59 func Extend(br bf.Renderer) Option { | |
60 return func(r *Renderer) { | |
61 r.Base = br | |
62 } | |
63 } | |
64 | |
65 // NewRenderer will return a new bfchroma renderer with sane defaults | |
66 func NewRenderer(options ...Option) *Renderer { | |
67 r := &Renderer{ | |
68 Base: bf.NewHTMLRenderer(bf.HTMLRendererParameters{ | |
69 Flags: bf.CommonHTMLFlags, | |
70 }), | |
71 Style: styles.Monokai, | |
72 Autodetect: true, | |
73 } | |
74 for _, option := range options { | |
75 option(r) | |
76 } | |
77 r.Formatter = html.New(r.ChromaOptions...) | |
78 return r | |
79 } | |
80 | |
81 // RenderWithChroma will render the given text to the w io.Writer | |
82 func (r *Renderer) RenderWithChroma(w io.Writer, text []byte, data bf.CodeBlockData) error { | |
83 var lexer chroma.Lexer | |
84 | |
85 // Determining the lexer to use | |
86 if len(data.Info) > 0 { | |
87 lexer = lexers.Get(string(data.Info)) | |
88 } else if r.Autodetect { | |
89 lexer = lexers.Analyse(string(text)) | |
90 } | |
91 if lexer == nil { | |
92 lexer = lexers.Fallback | |
93 } | |
94 | |
95 // Tokenize the code | |
96 iterator, err := lexer.Tokenise(nil, string(text)) | |
97 if err != nil { | |
98 return err | |
99 } | |
100 return r.Formatter.Format(w, r.Style, iterator) | |
101 } | |
102 | |
103 // Renderer is a custom Blackfriday renderer that uses the capabilities of | |
104 // chroma to highlight code with triple backtick notation | |
105 type Renderer struct { | |
106 Base bf.Renderer | |
107 Autodetect bool | |
108 ChromaOptions []html.Option | |
109 Style *chroma.Style | |
110 Formatter *html.Formatter | |
111 embedCSS bool | |
112 } | |
113 | |
114 // RenderNode satisfies the Renderer interface | |
115 func (r *Renderer) RenderNode(w io.Writer, node *bf.Node, entering bool) bf.WalkStatus { | |
116 switch node.Type { | |
117 case bf.Document: | |
118 if entering && r.embedCSS { | |
119 w.Write([]byte("<style>")) // nolint: errcheck | |
120 r.Formatter.WriteCSS(w, r.Style) // nolint: errcheck | |
121 w.Write([]byte("</style>")) // nolint: errcheck | |
122 } | |
123 return r.Base.RenderNode(w, node, entering) | |
124 case bf.CodeBlock: | |
125 if err := r.RenderWithChroma(w, node.Literal, node.CodeBlockData); err != nil { | |
126 return r.Base.RenderNode(w, node, entering) | |
127 } | |
128 return bf.SkipChildren | |
129 default: | |
130 return r.Base.RenderNode(w, node, entering) | |
131 } | |
132 } | |
133 | |
134 // RenderHeader satisfies the Renderer interface | |
135 func (r *Renderer) RenderHeader(w io.Writer, ast *bf.Node) { | |
136 r.Base.RenderHeader(w, ast) | |
137 } | |
138 | |
139 // RenderFooter satisfies the Renderer interface | |
140 func (r *Renderer) RenderFooter(w io.Writer, ast *bf.Node) { | |
141 r.Base.RenderFooter(w, ast) | |
142 } | |
143 | |
144 // ChromaCSS returns CSS used with chroma's html.WithClasses() option | |
145 func (r *Renderer) ChromaCSS(w io.Writer) error { | |
146 return r.Formatter.WriteCSS(w, r.Style) | |
147 } |