package renderer
import (
"bufio"
"io"
"sync"
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/util"
)
type Config struct {
Options map [OptionName ]interface {}
NodeRenderers util .PrioritizedSlice
}
func NewConfig () *Config {
return &Config {
Options : map [OptionName ]interface {}{},
NodeRenderers : util .PrioritizedSlice {},
}
}
type OptionName string
type Option interface {
SetConfig (*Config )
}
type withNodeRenderers struct {
value []util .PrioritizedValue
}
func (o *withNodeRenderers ) SetConfig (c *Config ) {
c .NodeRenderers = append (c .NodeRenderers , o .value ...)
}
func WithNodeRenderers (ps ...util .PrioritizedValue ) Option {
return &withNodeRenderers {ps }
}
type withOption struct {
name OptionName
value interface {}
}
func (o *withOption ) SetConfig (c *Config ) {
c .Options [o .name ] = o .value
}
func WithOption (name OptionName , value interface {}) Option {
return &withOption {name , value }
}
type SetOptioner interface {
SetOption (name OptionName , value interface {})
}
type NodeRendererFunc func (writer util .BufWriter , source []byte , n ast .Node , entering bool ) (ast .WalkStatus , error )
type NodeRenderer interface {
RegisterFuncs (NodeRendererFuncRegisterer )
}
type NodeRendererFuncRegisterer interface {
Register (ast .NodeKind , NodeRendererFunc )
}
type Renderer interface {
Render (w io .Writer , source []byte , n ast .Node ) error
AddOptions (...Option )
}
type renderer struct {
config *Config
options map [OptionName ]interface {}
nodeRendererFuncsTmp map [ast .NodeKind ]NodeRendererFunc
maxKind int
nodeRendererFuncs []NodeRendererFunc
initSync sync .Once
}
func NewRenderer (options ...Option ) Renderer {
config := NewConfig ()
for _ , opt := range options {
opt .SetConfig (config )
}
r := &renderer {
options : map [OptionName ]interface {}{},
config : config ,
nodeRendererFuncsTmp : map [ast .NodeKind ]NodeRendererFunc {},
}
return r
}
func (r *renderer ) AddOptions (opts ...Option ) {
for _ , opt := range opts {
opt .SetConfig (r .config )
}
}
func (r *renderer ) Register (kind ast .NodeKind , v NodeRendererFunc ) {
r .nodeRendererFuncsTmp [kind ] = v
if int (kind ) > r .maxKind {
r .maxKind = int (kind )
}
}
func (r *renderer ) Render (w io .Writer , source []byte , n ast .Node ) error {
r .initSync .Do (func () {
r .options = r .config .Options
r .config .NodeRenderers .Sort ()
l := len (r .config .NodeRenderers )
for i := l - 1 ; i >= 0 ; i -- {
v := r .config .NodeRenderers [i ]
nr , _ := v .Value .(NodeRenderer )
if se , ok := v .Value .(SetOptioner ); ok {
for oname , ovalue := range r .options {
se .SetOption (oname , ovalue )
}
}
nr .RegisterFuncs (r )
}
r .nodeRendererFuncs = make ([]NodeRendererFunc , r .maxKind +1 )
for kind , nr := range r .nodeRendererFuncsTmp {
r .nodeRendererFuncs [kind ] = nr
}
r .config = nil
r .nodeRendererFuncsTmp = nil
})
writer , ok := w .(util .BufWriter )
if !ok {
writer = bufio .NewWriter (w )
}
err := ast .Walk (n , func (n ast .Node , entering bool ) (ast .WalkStatus , error ) {
s := ast .WalkStatus (ast .WalkContinue )
var err error
f := r .nodeRendererFuncs [n .Kind ()]
if f != nil {
s , err = f (writer , source , n , entering )
}
return s , err
})
if err != nil {
return err
}
return writer .Flush ()
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .