aya/vendor/github.com/Depado/bfchroma/v2/renderer.go

148 行
4.0 KiB
Go

// Package bfchroma provides an easy and extensible blackfriday renderer that
// uses the chroma syntax highlighter to render code blocks.
package bfchroma
import (
"io"
"github.com/alecthomas/chroma/v2"
"github.com/alecthomas/chroma/v2/formatters/html"
"github.com/alecthomas/chroma/v2/lexers"
"github.com/alecthomas/chroma/v2/styles"
bf "github.com/russross/blackfriday/v2"
)
// Option defines the functional option type
type Option func(r *Renderer)
// Style is a function option allowing to set the style used by chroma
// Default : "monokai"
func Style(s string) Option {
return func(r *Renderer) {
r.Style = styles.Get(s)
}
}
// ChromaStyle is an option to directly set the style of the renderer using a
// chroma style instead of a string
func ChromaStyle(s *chroma.Style) Option {
return func(r *Renderer) {
r.Style = s
}
}
// WithoutAutodetect disables chroma's language detection when no codeblock
// extra information is given. It will fallback to a sane default instead of
// trying to detect the language.
func WithoutAutodetect() Option {
return func(r *Renderer) {
r.Autodetect = false
}
}
// EmbedCSS will embed CSS needed for html.WithClasses() in beginning of the document
func EmbedCSS() Option {
return func(r *Renderer) {
r.embedCSS = true
}
}
// ChromaOptions allows to pass Chroma html.Option such as Standalone()
// WithClasses(), ClassPrefix(prefix)...
func ChromaOptions(options ...html.Option) Option {
return func(r *Renderer) {
r.ChromaOptions = options
}
}
// Extend allows to specify the blackfriday renderer which is extended
func Extend(br bf.Renderer) Option {
return func(r *Renderer) {
r.Base = br
}
}
// NewRenderer will return a new bfchroma renderer with sane defaults
func NewRenderer(options ...Option) *Renderer {
r := &Renderer{
Base: bf.NewHTMLRenderer(bf.HTMLRendererParameters{
Flags: bf.CommonHTMLFlags,
}),
Style: styles.Monokai,
Autodetect: true,
}
for _, option := range options {
option(r)
}
r.Formatter = html.New(r.ChromaOptions...)
return r
}
// RenderWithChroma will render the given text to the w io.Writer
func (r *Renderer) RenderWithChroma(w io.Writer, text []byte, data bf.CodeBlockData) error {
var lexer chroma.Lexer
// Determining the lexer to use
if len(data.Info) > 0 {
lexer = lexers.Get(string(data.Info))
} else if r.Autodetect {
lexer = lexers.Analyse(string(text))
}
if lexer == nil {
lexer = lexers.Fallback
}
// Tokenize the code
iterator, err := lexer.Tokenise(nil, string(text))
if err != nil {
return err
}
return r.Formatter.Format(w, r.Style, iterator)
}
// Renderer is a custom Blackfriday renderer that uses the capabilities of
// chroma to highlight code with triple backtick notation
type Renderer struct {
Base bf.Renderer
Autodetect bool
ChromaOptions []html.Option
Style *chroma.Style
Formatter *html.Formatter
embedCSS bool
}
// RenderNode satisfies the Renderer interface
func (r *Renderer) RenderNode(w io.Writer, node *bf.Node, entering bool) bf.WalkStatus {
switch node.Type {
case bf.Document:
if entering && r.embedCSS {
w.Write([]byte("<style>")) // nolint: errcheck
r.Formatter.WriteCSS(w, r.Style) // nolint: errcheck
w.Write([]byte("</style>")) // nolint: errcheck
}
return r.Base.RenderNode(w, node, entering)
case bf.CodeBlock:
if err := r.RenderWithChroma(w, node.Literal, node.CodeBlockData); err != nil {
return r.Base.RenderNode(w, node, entering)
}
return bf.SkipChildren
default:
return r.Base.RenderNode(w, node, entering)
}
}
// RenderHeader satisfies the Renderer interface
func (r *Renderer) RenderHeader(w io.Writer, ast *bf.Node) {
r.Base.RenderHeader(w, ast)
}
// RenderFooter satisfies the Renderer interface
func (r *Renderer) RenderFooter(w io.Writer, ast *bf.Node) {
r.Base.RenderFooter(w, ast)
}
// ChromaCSS returns CSS used with chroma's html.WithClasses() option
func (r *Renderer) ChromaCSS(w io.Writer) error {
return r.Formatter.WriteCSS(w, r.Style)
}