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 } |
