1// Package renderer renders the given AST to certain formats. 2package renderer 3 4import ( 5 "bufio" 6 "io" 7 "sync" 8 9 "github.com/yuin/goldmark/ast" 10 "github.com/yuin/goldmark/util" 11) 12 13// A Config struct is a data structure that holds configuration of the Renderer. 14type Config struct { 15 Options map[OptionName]interface{} 16 NodeRenderers util.PrioritizedSlice 17} 18 19// NewConfig returns a new Config 20func NewConfig() *Config { 21 return &Config{ 22 Options: map[OptionName]interface{}{}, 23 NodeRenderers: util.PrioritizedSlice{}, 24 } 25} 26 27// An OptionName is a name of the option. 28type OptionName string 29 30// An Option interface is a functional option type for the Renderer. 31type Option interface { 32 SetConfig(*Config) 33} 34 35type withNodeRenderers struct { 36 value []util.PrioritizedValue 37} 38 39func (o *withNodeRenderers) SetConfig(c *Config) { 40 c.NodeRenderers = append(c.NodeRenderers, o.value...) 41} 42 43// WithNodeRenderers is a functional option that allow you to add 44// NodeRenderers to the renderer. 45func WithNodeRenderers(ps ...util.PrioritizedValue) Option { 46 return &withNodeRenderers{ps} 47} 48 49type withOption struct { 50 name OptionName 51 value interface{} 52} 53 54func (o *withOption) SetConfig(c *Config) { 55 c.Options[o.name] = o.value 56} 57 58// WithOption is a functional option that allow you to set 59// an arbitrary option to the parser. 60func WithOption(name OptionName, value interface{}) Option { 61 return &withOption{name, value} 62} 63 64// A SetOptioner interface sets given option to the object. 65type SetOptioner interface { 66 // SetOption sets given option to the object. 67 // Unacceptable options may be passed. 68 // Thus implementations must ignore unacceptable options. 69 SetOption(name OptionName, value interface{}) 70} 71 72// NodeRendererFunc is a function that renders a given node. 73type NodeRendererFunc func(writer util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) 74 75// A NodeRenderer interface offers NodeRendererFuncs. 76type NodeRenderer interface { 77 // RendererFuncs registers NodeRendererFuncs to given NodeRendererFuncRegisterer. 78 RegisterFuncs(NodeRendererFuncRegisterer) 79} 80 81// A NodeRendererFuncRegisterer registers 82type NodeRendererFuncRegisterer interface { 83 // Register registers given NodeRendererFunc to this object. 84 Register(ast.NodeKind, NodeRendererFunc) 85} 86 87// A Renderer interface renders given AST node to given 88// writer with given Renderer. 89type Renderer interface { 90 Render(w io.Writer, source []byte, n ast.Node) error 91 92 // AddOptions adds given option to this renderer. 93 AddOptions(...Option) 94} 95 96type renderer struct { 97 config *Config 98 options map[OptionName]interface{} 99 nodeRendererFuncsTmp map[ast.NodeKind]NodeRendererFunc 100 maxKind int 101 nodeRendererFuncs []NodeRendererFunc 102 initSync sync.Once 103} 104 105// NewRenderer returns a new Renderer with given options. 106func NewRenderer(options ...Option) Renderer { 107 config := NewConfig() 108 for _, opt := range options { 109 opt.SetConfig(config) 110 } 111 112 r := &renderer{ 113 options: map[OptionName]interface{}{}, 114 config: config, 115 nodeRendererFuncsTmp: map[ast.NodeKind]NodeRendererFunc{}, 116 } 117 118 return r 119} 120 121func (r *renderer) AddOptions(opts ...Option) { 122 for _, opt := range opts { 123 opt.SetConfig(r.config) 124 } 125} 126 127func (r *renderer) Register(kind ast.NodeKind, v NodeRendererFunc) { 128 r.nodeRendererFuncsTmp[kind] = v 129 if int(kind) > r.maxKind { 130 r.maxKind = int(kind) 131 } 132} 133 134// Render renders the given AST node to the given writer with the given Renderer. 135func (r *renderer) Render(w io.Writer, source []byte, n ast.Node) error { 136 r.initSync.Do(func() { 137 r.options = r.config.Options 138 r.config.NodeRenderers.Sort() 139 l := len(r.config.NodeRenderers) 140 for i := l - 1; i >= 0; i-- { 141 v := r.config.NodeRenderers[i] 142 nr, _ := v.Value.(NodeRenderer) 143 if se, ok := v.Value.(SetOptioner); ok { 144 for oname, ovalue := range r.options { 145 se.SetOption(oname, ovalue) 146 } 147 } 148 nr.RegisterFuncs(r) 149 } 150 r.nodeRendererFuncs = make([]NodeRendererFunc, r.maxKind+1) 151 for kind, nr := range r.nodeRendererFuncsTmp { 152 r.nodeRendererFuncs[kind] = nr 153 } 154 r.config = nil 155 r.nodeRendererFuncsTmp = nil 156 }) 157 writer, ok := w.(util.BufWriter) 158 if !ok { 159 writer = bufio.NewWriter(w) 160 } 161 err := ast.Walk(n, func(n ast.Node, entering bool) (ast.WalkStatus, error) { 162 s := ast.WalkStatus(ast.WalkContinue) 163 var err error 164 f := r.nodeRendererFuncs[n.Kind()] 165 if f != nil { 166 s, err = f(writer, source, n, entering) 167 } 168 return s, err 169 }) 170 if err != nil { 171 return err 172 } 173 return writer.Flush() 174} 175