diff --git a/internal/mdrenderer/node_renderer.go b/internal/mdrenderer/node_renderer.go index b9cf8dc..a20d1ae 100644 --- a/internal/mdrenderer/node_renderer.go +++ b/internal/mdrenderer/node_renderer.go @@ -11,8 +11,8 @@ import ( east "github.com/yuin/goldmark/extension/ast" "github.com/yuin/goldmark/renderer" - "github.com/blackstork-io/goldmark-markdown/internal/noderenderer" "github.com/blackstork-io/goldmark-markdown/internal/options" + "github.com/blackstork-io/goldmark-markdown/noderenderer" ) // TODO: add the ability to register new node renderers @@ -43,8 +43,9 @@ func renderHelper(n ast.Node, fn noderenderer.RenderFunc) (err error) { } // NewRenderer returns a new renderer. Use [goldmark.WithRenderer] to add it to a goldmark instance. -func NewRenderer() *Renderer { +func NewRenderer(opts ...options.Option) *Renderer { return &Renderer{ + opts: opts, config: options.DefaultConfig(), previousLineBlank: true, // document (conceptually) starts with a blank line wasAtNL: true, @@ -62,6 +63,7 @@ type bufs []buf // Renderer renders markdown nodes to a writer. type Renderer struct { config options.Config + opts []options.Option initOnce sync.Once nodeRendererFuncs []noderenderer.RenderFunc @@ -86,11 +88,9 @@ var _ goldmark.Extender = &Renderer{} // Extend implements goldmark.Extender. func (r *Renderer) Extend(md goldmark.Markdown) { - r.AddOptions( - renderer.WithOption(options.OptParser, options.ParserOpt{ - Parser: md.Parser(), - }), - ) + options.ParserOpt{ + Parser: md.Parser(), + }.Apply(&r.config) md.SetRenderer(r) } @@ -129,34 +129,8 @@ func (r *Renderer) getRenderer(kind ast.NodeKind) noderenderer.RenderFunc { // // You can call this method directly or use [goldmark.WithRendererOptions]. func (r *Renderer) AddOptions(opts ...renderer.Option) { - r.initOnce.Do(r.init) - - config := renderer.NewConfig() - for _, opt := range opts { - opt.SetConfig(config) - } - if opt, found := config.Options[options.OptParser]; found { - delete(config.Options, options.OptParser) - opt.(options.Option).Apply(&r.config) - } - for name, val := range config.Options { - if opt, ok := val.(options.Option); ok { - opt.Apply(&r.config) - } else { - r.config.Errs = append(r.config.Errs, fmt.Errorf("%w: %s", options.ErrUnsupportedOption, name)) - } - } - - config.NodeRenderers.Sort() - l := len(config.NodeRenderers) - for i := l - 1; i >= 0; i-- { - v := config.NodeRenderers[i] - nr, ok := v.Value.(noderenderer.NodeRenderer) - if !ok { - continue - } - r.setRenderer(nr.Kind, nr.Fn) - } + // ignore standard options + return } // Render renders the given AST node to the given writer. @@ -220,6 +194,13 @@ func (r *Renderer) init() { r.setRenderer(east.KindTableCell, r.renderTableCell) r.setRenderer(east.KindStrikethrough, r.renderStrikethrough) r.setRenderer(east.KindTaskCheckBox, r.renderTaskCheckBox) + + for _, opt := range r.opts { + opt.Apply(&r.config) + } + for _, kind := range r.config.IgnoredNodes { + r.setRenderer(kind, noderenderer.NoopRenderer) + } } // appends new line if the previous line is not empty @@ -246,7 +227,7 @@ func (r *Renderer) endLine() { } func (r *Renderer) flush() error { - if len(r.positionStack) != 0 || len(r.emphStack) != 0 || len(r.bufs) == 0 || r.table != nil { + if len(r.positionStack) != 0 || len(r.emphStack) != 0 || r.table != nil || len(r.bufs) == 0 { return nil } diff --git a/internal/options/ignore.go b/internal/options/ignore.go new file mode 100644 index 0000000..bdcdad6 --- /dev/null +++ b/internal/options/ignore.go @@ -0,0 +1,19 @@ +package options + +import ( + "github.com/yuin/goldmark/ast" + "github.com/yuin/goldmark/renderer" +) + +const OptIgnore renderer.OptionName = "IgnoreNodeKinds" + +type IgnoreOpt struct { + Kinds []ast.NodeKind +} + +// Apply implements Option. +func (t IgnoreOpt) Apply(c *Config) { + c.IgnoredNodes = append(c.IgnoredNodes, t.Kinds...) +} + +var _ Option = IgnoreOpt{} diff --git a/internal/options/options.go b/internal/options/options.go index 4c86335..f15501e 100644 --- a/internal/options/options.go +++ b/internal/options/options.go @@ -28,6 +28,7 @@ type Config struct { LinkTitleQuote []byte CheckedCheckbox []byte UncheckedCheckbox []byte + IgnoredNodes []ast.NodeKind } func DefaultConfig() Config { diff --git a/markdown_test.go b/markdown_test.go index 70fdfd9..341c4b4 100644 --- a/markdown_test.go +++ b/markdown_test.go @@ -30,7 +30,7 @@ func roundtrip(t testing.TB, source, expectedHTMLOverride []byte) { mdToMd := goldmark.New( goldmark.WithParser(mdToHtml.Parser()), - goldmark.WithRenderer(NewRenderer()), + goldmark.WithExtensions(NewRenderer()), ) expectedHtmlBuf := &bytes.Buffer{} diff --git a/internal/noderenderer/node_renderer.go b/noderenderer/node_renderer.go similarity index 83% rename from internal/noderenderer/node_renderer.go rename to noderenderer/node_renderer.go index 1346188..73b6ae9 100644 --- a/internal/noderenderer/node_renderer.go +++ b/noderenderer/node_renderer.go @@ -6,6 +6,10 @@ import "github.com/yuin/goldmark/ast" type RenderFunc func(n ast.Node, entering bool) error +func NoopRenderer(n ast.Node, entering bool) error { + return nil +} + type NodeRenderer struct { Kind ast.NodeKind Fn RenderFunc diff --git a/options.go b/options.go index 006c135..419bd42 100644 --- a/options.go +++ b/options.go @@ -1,10 +1,8 @@ package markdown import ( - "github.com/yuin/goldmark/parser" - "github.com/yuin/goldmark/renderer" - "github.com/blackstork-io/goldmark-markdown/internal/options" + "github.com/yuin/goldmark/ast" ) var ( @@ -13,18 +11,15 @@ var ( ) // WithThematicBreaks sets the thematic break tags to use, in the order of preference -func WithThematicBreaks(breaks ...string) renderer.Option { - return renderer.WithOption( - options.OptThematicBreaks, - options.ThematicBreaksOpt(breaks), - ) +func WithThematicBreaks(breaks ...string) options.Option { + return options.ThematicBreaksOpt(breaks) } -// WithParser sets the markdown parser used to verify other options's validity/applicability -func WithParser(p parser.Parser) renderer.Option { - return renderer.WithOption(options.OptParser, options.ParserOpt{ - Parser: p, - }) +// WithIgnoredNodes sets up the renderer to ignore a node, proceeding to render its children +func WithIgnoredNodes(kind ...ast.NodeKind) options.Option { + return options.IgnoreOpt{ + Kinds: kind, + } } // TODO: Create more options diff --git a/renderer.go b/renderer.go index 9690c7b..3ebafce 100644 --- a/renderer.go +++ b/renderer.go @@ -2,9 +2,10 @@ package markdown import ( "github.com/blackstork-io/goldmark-markdown/internal/mdrenderer" + "github.com/blackstork-io/goldmark-markdown/internal/options" ) -// NewRenderer returns a new renderer. Use [goldmark.WithRenderer] to add it to a goldmark instance. -func NewRenderer() *mdrenderer.Renderer { - return mdrenderer.NewRenderer() +// NewRenderer returns a new renderer. Use [goldmark.WithExtensions] to add it to a goldmark instance. +func NewRenderer(opts ...options.Option) *mdrenderer.Renderer { + return mdrenderer.NewRenderer(opts...) }