diff --git a/README.md b/README.md index 600b0f7..6e66cb7 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ Summary: Security -------------------- By default, goldmark does not render raw HTMLs and potentially dangerous urls. -If you need to gain more control about untrusted contents, it is recommended to +If you need to gain more control over untrusted contents, it is recommended to use HTML sanitizer such as [bluemonday](https://github.com/microcosm-cc/bluemonday). Benchmark diff --git a/markdown.go b/markdown.go index 117e90f..20f1388 100644 --- a/markdown.go +++ b/markdown.go @@ -27,8 +27,8 @@ var defaultMarkdown = New() // Convert interprets a UTF-8 bytes source in Markdown and // write rendered contents to a writer w. -func Convert(source []byte, w io.Writer) error { - return defaultMarkdown.Convert(source, w) +func Convert(source []byte, w io.Writer, opts ...parser.ParseOption) error { + return defaultMarkdown.Convert(source, w, opts...) } // A Markdown interface offers functions to convert Markdown text to @@ -36,7 +36,7 @@ func Convert(source []byte, w io.Writer) error { type Markdown interface { // Convert interprets a UTF-8 bytes source in Markdown and write rendered // contents to a writer w. - Convert(source []byte, writer io.Writer) error + Convert(source []byte, writer io.Writer, opts ...parser.ParseOption) error // Parser returns a Parser that will be used for conversion. Parser() parser.Parser @@ -115,10 +115,10 @@ func New(options ...Option) Markdown { return md } -func (m *markdown) Convert(source []byte, writer io.Writer) error { +func (m *markdown) Convert(source []byte, writer io.Writer, opts ...parser.ParseOption) error { reader := text.NewReader(source) - doc, _ := m.parser.Parse(reader) - return m.renderer.Render(writer, reader.Source(), doc) + doc := m.parser.Parse(reader, opts...) + return m.renderer.Render(writer, source, doc) } func (m *markdown) Parser() parser.Parser { diff --git a/parser/parser.go b/parser/parser.go index 51ff945..5d33ab3 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -62,11 +62,12 @@ func (c *ContextKey) New() ContextKey { return ContextKey(atomic.AddInt32((*int32)(c), 1)) } -var contextKey ContextKey +// ContextKeyMax is a maximum value of the ContextKey. +var ContextKeyMax ContextKey // NewContextKey return a new ContextKey value. func NewContextKey() ContextKey { - return contextKey.New() + return ContextKeyMax.New() } // A Context interface holds a information that are necessary to parse @@ -131,13 +132,6 @@ type Context interface { SetLastOpenedBlock(Block) } -// A Result interface holds a result of parsing Markdown text. -type Result interface { - // Reference returns (a reference, true) if a reference associated with - // given label exists, otherwise (nil, false). - Reference(label string) (Reference, bool) -} - type parseContext struct { store []interface{} source []byte @@ -149,9 +143,10 @@ type parseContext struct { lastOpenedBlock Block } -func newContext(source []byte) Context { +// NewContext returns a new Context. +func NewContext(source []byte) Context { return &parseContext{ - store: make([]interface{}, contextKey+1), + store: make([]interface{}, ContextKeyMax+1), source: source, refs: map[string]Reference{}, blockOffset: 0, @@ -339,7 +334,7 @@ type OptionName string // A Parser interface parses Markdown text into AST nodes. type Parser interface { // Parse parses given Markdown text into AST nodes. - Parse(reader text.Reader) (ast.Node, Result) + Parse(reader text.Reader, opts ...ParseOption) ast.Node // AddOption adds given option to thie parser. AddOption(Option) @@ -652,7 +647,23 @@ func (p *parser) addASTTransformer(v util.PrioritizedValue, options map[OptionNa p.astTransformers = append(p.astTransformers, at) } -func (p *parser) Parse(reader text.Reader) (ast.Node, Result) { +// A ParseConfig struct is a data structure that holds configuration of the Parser.Parse. +type ParseConfig struct { + Context Context +} + +// A ParseOption is a functional option type for the Parser.Parse. +type ParseOption func(c *ParseConfig) + +// WithContext is a functional option that allow you to override +// a default context. +func WithContext(context Context) ParseOption { + return func(c *ParseConfig) { + c.Context = context + } +} + +func (p *parser) Parse(reader text.Reader, opts ...ParseOption) ast.Node { p.initSync.Do(func() { p.config.BlockParsers.Sort() for _, v := range p.config.BlockParsers { @@ -672,8 +683,15 @@ func (p *parser) Parse(reader text.Reader) (ast.Node, Result) { } p.config = nil }) + c := &ParseConfig{} + for _, opt := range opts { + opt(c) + } + if c.Context == nil { + c.Context = NewContext(reader.Source()) + } + pc := c.Context root := ast.NewDocument() - pc := newContext(reader.Source()) p.parseBlocks(root, reader, pc) blockReader := text.NewBlockReader(reader.Source(), nil) p.walkBlock(root, func(node ast.Node) { @@ -683,7 +701,7 @@ func (p *parser) Parse(reader text.Reader) (ast.Node, Result) { at.Transform(root, pc) } //root.Dump(reader.Source(), 0) - return root, pc + return root } func (p *parser) transformParagraph(node *ast.Paragraph, pc Context) {