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 }