mirror of
https://github.com/yuin/goldmark
synced 2025-03-04 23:04:52 +00:00
Add parser.ParseContext
This commit is contained in:
parent
77aabc8f8e
commit
a8a4629dd9
3 changed files with 40 additions and 22 deletions
|
|
@ -108,7 +108,7 @@ Summary:
|
||||||
Security
|
Security
|
||||||
--------------------
|
--------------------
|
||||||
By default, goldmark does not render raw HTMLs and potentially dangerous urls.
|
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).
|
use HTML sanitizer such as [bluemonday](https://github.com/microcosm-cc/bluemonday).
|
||||||
|
|
||||||
Benchmark
|
Benchmark
|
||||||
|
|
|
||||||
12
markdown.go
12
markdown.go
|
|
@ -27,8 +27,8 @@ var defaultMarkdown = New()
|
||||||
|
|
||||||
// Convert interprets a UTF-8 bytes source in Markdown and
|
// Convert interprets a UTF-8 bytes source in Markdown and
|
||||||
// write rendered contents to a writer w.
|
// write rendered contents to a writer w.
|
||||||
func Convert(source []byte, w io.Writer) error {
|
func Convert(source []byte, w io.Writer, opts ...parser.ParseOption) error {
|
||||||
return defaultMarkdown.Convert(source, w)
|
return defaultMarkdown.Convert(source, w, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Markdown interface offers functions to convert Markdown text to
|
// 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 {
|
type Markdown interface {
|
||||||
// Convert interprets a UTF-8 bytes source in Markdown and write rendered
|
// Convert interprets a UTF-8 bytes source in Markdown and write rendered
|
||||||
// contents to a writer w.
|
// 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 returns a Parser that will be used for conversion.
|
||||||
Parser() parser.Parser
|
Parser() parser.Parser
|
||||||
|
|
@ -115,10 +115,10 @@ func New(options ...Option) Markdown {
|
||||||
return md
|
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)
|
reader := text.NewReader(source)
|
||||||
doc, _ := m.parser.Parse(reader)
|
doc := m.parser.Parse(reader, opts...)
|
||||||
return m.renderer.Render(writer, reader.Source(), doc)
|
return m.renderer.Render(writer, source, doc)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *markdown) Parser() parser.Parser {
|
func (m *markdown) Parser() parser.Parser {
|
||||||
|
|
|
||||||
|
|
@ -62,11 +62,12 @@ func (c *ContextKey) New() ContextKey {
|
||||||
return ContextKey(atomic.AddInt32((*int32)(c), 1))
|
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.
|
// NewContextKey return a new ContextKey value.
|
||||||
func NewContextKey() ContextKey {
|
func NewContextKey() ContextKey {
|
||||||
return contextKey.New()
|
return ContextKeyMax.New()
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Context interface holds a information that are necessary to parse
|
// A Context interface holds a information that are necessary to parse
|
||||||
|
|
@ -131,13 +132,6 @@ type Context interface {
|
||||||
SetLastOpenedBlock(Block)
|
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 {
|
type parseContext struct {
|
||||||
store []interface{}
|
store []interface{}
|
||||||
source []byte
|
source []byte
|
||||||
|
|
@ -149,9 +143,10 @@ type parseContext struct {
|
||||||
lastOpenedBlock Block
|
lastOpenedBlock Block
|
||||||
}
|
}
|
||||||
|
|
||||||
func newContext(source []byte) Context {
|
// NewContext returns a new Context.
|
||||||
|
func NewContext(source []byte) Context {
|
||||||
return &parseContext{
|
return &parseContext{
|
||||||
store: make([]interface{}, contextKey+1),
|
store: make([]interface{}, ContextKeyMax+1),
|
||||||
source: source,
|
source: source,
|
||||||
refs: map[string]Reference{},
|
refs: map[string]Reference{},
|
||||||
blockOffset: 0,
|
blockOffset: 0,
|
||||||
|
|
@ -339,7 +334,7 @@ type OptionName string
|
||||||
// A Parser interface parses Markdown text into AST nodes.
|
// A Parser interface parses Markdown text into AST nodes.
|
||||||
type Parser interface {
|
type Parser interface {
|
||||||
// Parse parses given Markdown text into AST nodes.
|
// 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 adds given option to thie parser.
|
||||||
AddOption(Option)
|
AddOption(Option)
|
||||||
|
|
@ -652,7 +647,23 @@ func (p *parser) addASTTransformer(v util.PrioritizedValue, options map[OptionNa
|
||||||
p.astTransformers = append(p.astTransformers, at)
|
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.initSync.Do(func() {
|
||||||
p.config.BlockParsers.Sort()
|
p.config.BlockParsers.Sort()
|
||||||
for _, v := range p.config.BlockParsers {
|
for _, v := range p.config.BlockParsers {
|
||||||
|
|
@ -672,8 +683,15 @@ func (p *parser) Parse(reader text.Reader) (ast.Node, Result) {
|
||||||
}
|
}
|
||||||
p.config = nil
|
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()
|
root := ast.NewDocument()
|
||||||
pc := newContext(reader.Source())
|
|
||||||
p.parseBlocks(root, reader, pc)
|
p.parseBlocks(root, reader, pc)
|
||||||
blockReader := text.NewBlockReader(reader.Source(), nil)
|
blockReader := text.NewBlockReader(reader.Source(), nil)
|
||||||
p.walkBlock(root, func(node ast.Node) {
|
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)
|
at.Transform(root, pc)
|
||||||
}
|
}
|
||||||
//root.Dump(reader.Source(), 0)
|
//root.Dump(reader.Source(), 0)
|
||||||
return root, pc
|
return root
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) transformParagraph(node *ast.Paragraph, pc Context) {
|
func (p *parser) transformParagraph(node *ast.Paragraph, pc Context) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue