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