mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-03 22:22:25 -06:00 
			
		
		
		
	
		
			
				
	
	
		
			174 lines
		
	
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			174 lines
		
	
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Package renderer renders the given AST to certain formats.
 | 
						|
package renderer
 | 
						|
 | 
						|
import (
 | 
						|
	"bufio"
 | 
						|
	"io"
 | 
						|
	"sync"
 | 
						|
 | 
						|
	"github.com/yuin/goldmark/ast"
 | 
						|
	"github.com/yuin/goldmark/util"
 | 
						|
)
 | 
						|
 | 
						|
// A Config struct is a data structure that holds configuration of the Renderer.
 | 
						|
type Config struct {
 | 
						|
	Options       map[OptionName]interface{}
 | 
						|
	NodeRenderers util.PrioritizedSlice
 | 
						|
}
 | 
						|
 | 
						|
// NewConfig returns a new Config.
 | 
						|
func NewConfig() *Config {
 | 
						|
	return &Config{
 | 
						|
		Options:       map[OptionName]interface{}{},
 | 
						|
		NodeRenderers: util.PrioritizedSlice{},
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// An OptionName is a name of the option.
 | 
						|
type OptionName string
 | 
						|
 | 
						|
// An Option interface is a functional option type for the Renderer.
 | 
						|
type Option interface {
 | 
						|
	SetConfig(*Config)
 | 
						|
}
 | 
						|
 | 
						|
type withNodeRenderers struct {
 | 
						|
	value []util.PrioritizedValue
 | 
						|
}
 | 
						|
 | 
						|
func (o *withNodeRenderers) SetConfig(c *Config) {
 | 
						|
	c.NodeRenderers = append(c.NodeRenderers, o.value...)
 | 
						|
}
 | 
						|
 | 
						|
// WithNodeRenderers is a functional option that allow you to add
 | 
						|
// NodeRenderers to the renderer.
 | 
						|
func WithNodeRenderers(ps ...util.PrioritizedValue) Option {
 | 
						|
	return &withNodeRenderers{ps}
 | 
						|
}
 | 
						|
 | 
						|
type withOption struct {
 | 
						|
	name  OptionName
 | 
						|
	value interface{}
 | 
						|
}
 | 
						|
 | 
						|
func (o *withOption) SetConfig(c *Config) {
 | 
						|
	c.Options[o.name] = o.value
 | 
						|
}
 | 
						|
 | 
						|
// WithOption is a functional option that allow you to set
 | 
						|
// an arbitrary option to the parser.
 | 
						|
func WithOption(name OptionName, value interface{}) Option {
 | 
						|
	return &withOption{name, value}
 | 
						|
}
 | 
						|
 | 
						|
// A SetOptioner interface sets given option to the object.
 | 
						|
type SetOptioner interface {
 | 
						|
	// SetOption sets given option to the object.
 | 
						|
	// Unacceptable options may be passed.
 | 
						|
	// Thus implementations must ignore unacceptable options.
 | 
						|
	SetOption(name OptionName, value interface{})
 | 
						|
}
 | 
						|
 | 
						|
// NodeRendererFunc is a function that renders a given node.
 | 
						|
type NodeRendererFunc func(writer util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error)
 | 
						|
 | 
						|
// A NodeRenderer interface offers NodeRendererFuncs.
 | 
						|
type NodeRenderer interface {
 | 
						|
	// RendererFuncs registers NodeRendererFuncs to given NodeRendererFuncRegisterer.
 | 
						|
	RegisterFuncs(NodeRendererFuncRegisterer)
 | 
						|
}
 | 
						|
 | 
						|
// A NodeRendererFuncRegisterer registers given NodeRendererFunc to this object.
 | 
						|
type NodeRendererFuncRegisterer interface {
 | 
						|
	// Register registers given NodeRendererFunc to this object.
 | 
						|
	Register(ast.NodeKind, NodeRendererFunc)
 | 
						|
}
 | 
						|
 | 
						|
// A Renderer interface renders given AST node to given
 | 
						|
// writer with given Renderer.
 | 
						|
type Renderer interface {
 | 
						|
	Render(w io.Writer, source []byte, n ast.Node) error
 | 
						|
 | 
						|
	// AddOptions adds given option to this renderer.
 | 
						|
	AddOptions(...Option)
 | 
						|
}
 | 
						|
 | 
						|
type renderer struct {
 | 
						|
	config               *Config
 | 
						|
	options              map[OptionName]interface{}
 | 
						|
	nodeRendererFuncsTmp map[ast.NodeKind]NodeRendererFunc
 | 
						|
	maxKind              int
 | 
						|
	nodeRendererFuncs    []NodeRendererFunc
 | 
						|
	initSync             sync.Once
 | 
						|
}
 | 
						|
 | 
						|
// NewRenderer returns a new Renderer with given options.
 | 
						|
func NewRenderer(options ...Option) Renderer {
 | 
						|
	config := NewConfig()
 | 
						|
	for _, opt := range options {
 | 
						|
		opt.SetConfig(config)
 | 
						|
	}
 | 
						|
 | 
						|
	r := &renderer{
 | 
						|
		options:              map[OptionName]interface{}{},
 | 
						|
		config:               config,
 | 
						|
		nodeRendererFuncsTmp: map[ast.NodeKind]NodeRendererFunc{},
 | 
						|
	}
 | 
						|
 | 
						|
	return r
 | 
						|
}
 | 
						|
 | 
						|
func (r *renderer) AddOptions(opts ...Option) {
 | 
						|
	for _, opt := range opts {
 | 
						|
		opt.SetConfig(r.config)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (r *renderer) Register(kind ast.NodeKind, v NodeRendererFunc) {
 | 
						|
	r.nodeRendererFuncsTmp[kind] = v
 | 
						|
	if int(kind) > r.maxKind {
 | 
						|
		r.maxKind = int(kind)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Render renders the given AST node to the given writer with the given Renderer.
 | 
						|
func (r *renderer) Render(w io.Writer, source []byte, n ast.Node) error {
 | 
						|
	r.initSync.Do(func() {
 | 
						|
		r.options = r.config.Options
 | 
						|
		r.config.NodeRenderers.Sort()
 | 
						|
		l := len(r.config.NodeRenderers)
 | 
						|
		for i := l - 1; i >= 0; i-- {
 | 
						|
			v := r.config.NodeRenderers[i]
 | 
						|
			nr, _ := v.Value.(NodeRenderer)
 | 
						|
			if se, ok := v.Value.(SetOptioner); ok {
 | 
						|
				for oname, ovalue := range r.options {
 | 
						|
					se.SetOption(oname, ovalue)
 | 
						|
				}
 | 
						|
			}
 | 
						|
			nr.RegisterFuncs(r)
 | 
						|
		}
 | 
						|
		r.nodeRendererFuncs = make([]NodeRendererFunc, r.maxKind+1)
 | 
						|
		for kind, nr := range r.nodeRendererFuncsTmp {
 | 
						|
			r.nodeRendererFuncs[kind] = nr
 | 
						|
		}
 | 
						|
		r.config = nil
 | 
						|
		r.nodeRendererFuncsTmp = nil
 | 
						|
	})
 | 
						|
	writer, ok := w.(util.BufWriter)
 | 
						|
	if !ok {
 | 
						|
		writer = bufio.NewWriter(w)
 | 
						|
	}
 | 
						|
	err := ast.Walk(n, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
 | 
						|
		s := ast.WalkStatus(ast.WalkContinue)
 | 
						|
		var err error
 | 
						|
		f := r.nodeRendererFuncs[n.Kind()]
 | 
						|
		if f != nil {
 | 
						|
			s, err = f(writer, source, n, entering)
 | 
						|
		}
 | 
						|
		return s, err
 | 
						|
	})
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	return writer.Flush()
 | 
						|
}
 |