gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

renderer.go (4563B)


      1 // Package renderer renders the given AST to certain formats.
      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 // A Config struct is a data structure that holds configuration of the Renderer.
     14 type Config struct {
     15 	Options       map[OptionName]interface{}
     16 	NodeRenderers util.PrioritizedSlice
     17 }
     18 
     19 // NewConfig returns a new Config
     20 func 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.
     28 type OptionName string
     29 
     30 // An Option interface is a functional option type for the Renderer.
     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 // WithNodeRenderers is a functional option that allow you to add
     44 // NodeRenderers to the renderer.
     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 // WithOption is a functional option that allow you to set
     59 // an arbitrary option to the parser.
     60 func WithOption(name OptionName, value interface{}) Option {
     61 	return &withOption{name, value}
     62 }
     63 
     64 // A SetOptioner interface sets given option to the object.
     65 type 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.
     73 type NodeRendererFunc func(writer util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error)
     74 
     75 // A NodeRenderer interface offers NodeRendererFuncs.
     76 type NodeRenderer interface {
     77 	// RendererFuncs registers NodeRendererFuncs to given NodeRendererFuncRegisterer.
     78 	RegisterFuncs(NodeRendererFuncRegisterer)
     79 }
     80 
     81 // A NodeRendererFuncRegisterer registers
     82 type 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.
     89 type 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 
     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 // NewRenderer returns a new Renderer with given options.
    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 // Render renders the given AST node to the given writer with the given Renderer.
    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 }