...

Source file src/github.com/yuin/goldmark/renderer/renderer.go

Documentation: github.com/yuin/goldmark/renderer

     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  }
   175  

View as plain text