renderer.go

  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}