...
1
2 package renderer
3
4 import (
5 "bufio"
6 "io"
7 "sync"
8
9 "github.com/yuin/goldmark/ast"
10 "github.com/yuin/goldmark/util"
11 )
12
13
14 type Config struct {
15 Options map[OptionName]interface{}
16 NodeRenderers util.PrioritizedSlice
17 }
18
19
20 func NewConfig() *Config {
21 return &Config{
22 Options: map[OptionName]interface{}{},
23 NodeRenderers: util.PrioritizedSlice{},
24 }
25 }
26
27
28 type OptionName string
29
30
31 type Option interface {
32 SetConfig(*Config)
33 }
34
35 type withNodeRenderers struct {
36 value []util.PrioritizedValue
37 }
38
39 func (o *withNodeRenderers) SetConfig(c *Config) {
40 c.NodeRenderers = append(c.NodeRenderers, o.value...)
41 }
42
43
44
45 func WithNodeRenderers(ps ...util.PrioritizedValue) Option {
46 return &withNodeRenderers{ps}
47 }
48
49 type withOption struct {
50 name OptionName
51 value interface{}
52 }
53
54 func (o *withOption) SetConfig(c *Config) {
55 c.Options[o.name] = o.value
56 }
57
58
59
60 func WithOption(name OptionName, value interface{}) Option {
61 return &withOption{name, value}
62 }
63
64
65 type SetOptioner interface {
66
67
68
69 SetOption(name OptionName, value interface{})
70 }
71
72
73 type NodeRendererFunc func(writer util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error)
74
75
76 type NodeRenderer interface {
77
78 RegisterFuncs(NodeRendererFuncRegisterer)
79 }
80
81
82 type NodeRendererFuncRegisterer interface {
83
84 Register(ast.NodeKind, NodeRendererFunc)
85 }
86
87
88
89 type Renderer interface {
90 Render(w io.Writer, source []byte, n ast.Node) error
91
92
93 AddOptions(...Option)
94 }
95
96 type 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
106 func 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
121 func (r *renderer) AddOptions(opts ...Option) {
122 for _, opt := range opts {
123 opt.SetConfig(r.config)
124 }
125 }
126
127 func (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
135 func (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
View as plain text