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 given NodeRendererFunc to this object.
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}