Add parser.ParseContext

This commit is contained in:
yuin 2019-04-28 19:54:33 +09:00
parent 77aabc8f8e
commit a8a4629dd9
3 changed files with 40 additions and 22 deletions

View file

@ -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

View file

@ -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 {

View file

@ -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) {