From 892d084af3088b5e336366c47e2a7e6aef36e40b Mon Sep 17 00:00:00 2001 From: Krzysztof Kowalczyk Date: Fri, 26 Jan 2018 00:11:58 -0800 Subject: [PATCH] rework the api a bit --- cmd/crashtest/main.go | 6 +- helpers_test.go | 7 +- html_renderer.go | 3 + markdown.go | 237 +++++++++++++++++------------------------- s/run-bench.sh | 4 + 5 files changed, 110 insertions(+), 147 deletions(-) create mode 100755 s/run-bench.sh diff --git a/cmd/crashtest/main.go b/cmd/crashtest/main.go index 5ee9507..55ddb73 100644 --- a/cmd/crashtest/main.go +++ b/cmd/crashtest/main.go @@ -54,11 +54,7 @@ var ( ) func runMarkdown(d []byte) string { - htmlParams := markdown.HTMLRendererParameters{ - Flags: markdown.HTMLFlagsNone, - } - renderer := markdown.NewHTMLRenderer(htmlParams) - html := markdown.Run(d, markdown.WithRenderer(renderer), markdown.WithExtensions(markdown.CommonExtensions)) + html := markdown.ToHTML(d, nil, nil) return string(html) } diff --git a/helpers_test.go b/helpers_test.go index c1f8759..76725c6 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -45,10 +45,11 @@ func execRecoverableTestSuite(t *testing.T, tests []string, params TestParams, s func runMarkdown(input string, params TestParams) string { params.HTMLRendererParameters.Flags = params.HTMLFlags + parser := NewParserWithExtensions(params.extensions) + parser.ReferenceOverride = params.referenceOverride renderer := NewHTMLRenderer(params.HTMLRendererParameters) - d := ParseAndRender([]byte(input), renderer, - WithExtensions(params.extensions), - WithRefOverride(params.referenceOverride)) + + d := ToHTML([]byte(input), parser, renderer) return string(d) } diff --git a/html_renderer.go b/html_renderer.go index c646826..40e6aaa 100644 --- a/html_renderer.go +++ b/html_renderer.go @@ -46,6 +46,9 @@ const ( SmartypantsAngledQuotes // Enable angled double quotes (with Smartypants) for double quotes rendering SmartypantsQuotesNBSP // Enable « French guillemets » (with Smartypants) TOC // Generate a table of contents + + CommonHTMLFlags HTMLFlags = UseXHTML | Smartypants | + SmartypantsFractions | SmartypantsDashes | SmartypantsLatexDashes ) var ( diff --git a/markdown.go b/markdown.go index e87a296..905dfdd 100644 --- a/markdown.go +++ b/markdown.go @@ -15,18 +15,14 @@ import ( "unicode/utf8" ) -// -// Markdown parsing and processing -// - // Version string of the package. Appears in the rendered document when // CompletePage flag is on. const Version = "2.0" -// Extensions is a bitfield of enabled extensions. +// Extensions is a bitmask of enabled Parser extensions. type Extensions int -// Bitflags representing markdown parsing extensions. +// Bit flags representing markdown parsing extensions. // Use | (or) to specify multiple extensions. const ( NoExtensions Extensions = 0 @@ -47,9 +43,6 @@ const ( BackslashLineBreak // Translate trailing backslashes into line breaks DefinitionLists // Render definition lists - CommonHTMLFlags HTMLFlags = UseXHTML | Smartypants | - SmartypantsFractions | SmartypantsDashes | SmartypantsLatexDashes - CommonExtensions Extensions = NoIntraEmphasis | Tables | FencedCode | Autolink | Strikethrough | SpaceHeadings | HeadingIDs | BackslashLineBreak | DefinitionLists @@ -167,19 +160,42 @@ type Renderer interface { // for each character that triggers a response when parsing inline data. type inlineParser func(p *Parser, data []byte, offset int) (int, *Node) +// ReferenceOverrideFunc is expected to be called with a reference string and +// return either a valid Reference type that the reference string maps to or +// nil. If overridden is false, the default reference logic will be executed. +// See the documentation in Options for more details on use-case. +type ReferenceOverrideFunc func(reference string) (ref *Reference, overridden bool) + // Parser is a type that holds extensions and the runtime state used by // Parse, and the renderer. You can not use it directly, construct it with New. type Parser struct { + + // ReferenceOverride is an optional function callback that is called every + // time a reference is resolved. It can be set before starting parsing. + // + // In Markdown, the link reference syntax can be made to resolve a link to + // a reference instead of an inline URL, in one of the following ways: + // + // * [link text][refid] + // * [refid][] + // + // Usually, the refid is defined at the bottom of the Markdown document. If + // this override function is provided, the refid is passed to the override + // function first, before consulting the defined refids at the bottom. If + // the override function indicates an override did not occur, the refids at + // the bottom will be used to fill in the link details. + ReferenceOverride ReferenceOverrideFunc + // after parsing, this is root of parsed ast Doc *Node - referenceOverride ReferenceOverrideFunc - refs map[string]*reference - inlineCallback [256]inlineParser - extensions Extensions - nesting int - maxNesting int - insideLink bool + extensions Extensions + + refs map[string]*reference + inlineCallback [256]inlineParser + nesting int + maxNesting int + insideLink bool // Footnotes need to be ordered as well as available to quickly check for // presence. If a ref is also a footnote, it's stored both in refs and here @@ -192,9 +208,58 @@ type Parser struct { allClosed bool } +// NewParser creates a markdown parser with CommonExtensions. +func NewParser() *Parser { + return NewParserWithExtensions(CommonExtensions) +} + +// NewParserWithExtensions creates a markdown parser with given extensions. +// Before +// for Run() to customize parser's behavior and the renderer. +func NewParserWithExtensions(extension Extensions) *Parser { + p := Parser{ + refs: make(map[string]*reference), + maxNesting: 16, + insideLink: false, + Doc: NewNode(&DocumentData{}), + extensions: extension, + allClosed: true, + } + p.tip = p.Doc + p.oldTip = p.Doc + p.lastMatchedContainer = p.Doc + + p.inlineCallback[' '] = maybeLineBreak + p.inlineCallback['*'] = emphasis + p.inlineCallback['_'] = emphasis + if p.extensions&Strikethrough != 0 { + p.inlineCallback['~'] = emphasis + } + p.inlineCallback['`'] = codeSpan + p.inlineCallback['\n'] = lineBreak + p.inlineCallback['['] = link + p.inlineCallback['<'] = leftAngle + p.inlineCallback['\\'] = escape + p.inlineCallback['&'] = entity + p.inlineCallback['!'] = maybeImage + p.inlineCallback['^'] = maybeInlineFootnote + if p.extensions&Autolink != 0 { + p.inlineCallback['h'] = maybeAutoLink + p.inlineCallback['m'] = maybeAutoLink + p.inlineCallback['f'] = maybeAutoLink + p.inlineCallback['H'] = maybeAutoLink + p.inlineCallback['M'] = maybeAutoLink + p.inlineCallback['F'] = maybeAutoLink + } + if p.extensions&Footnotes != 0 { + p.notes = make([]*reference, 0) + } + return &p +} + func (p *Parser) getRef(refid string) (ref *reference, found bool) { - if p.referenceOverride != nil { - r, overridden := p.referenceOverride(refid) + if p.ReferenceOverride != nil { + r, overridden := p.ReferenceOverride(refid) if overridden { if r == nil { return nil, false @@ -254,131 +319,25 @@ type Reference struct { Text string } -// ReferenceOverrideFunc is expected to be called with a reference string and -// return either a valid Reference type that the reference string maps to or -// nil. If overridden is false, the default reference logic will be executed. -// See the documentation in Options for more details on use-case. -type ReferenceOverrideFunc func(reference string) (ref *Reference, overridden bool) - -// NewParser creates a markdown parser. You can use the same With* functions as -// for Run() to customize parser's behavior and the renderer. -func NewParser(opts ...Option) *Parser { - var p Parser - for _, opt := range opts { - opt(&p) - } - p.refs = make(map[string]*reference) - p.maxNesting = 16 - p.insideLink = false - docNode := NewNode(&DocumentData{}) - p.Doc = docNode - p.tip = docNode - p.oldTip = docNode - p.lastMatchedContainer = docNode - p.allClosed = true - - p.inlineCallback[' '] = maybeLineBreak - p.inlineCallback['*'] = emphasis - p.inlineCallback['_'] = emphasis - if p.extensions&Strikethrough != 0 { - p.inlineCallback['~'] = emphasis - } - p.inlineCallback['`'] = codeSpan - p.inlineCallback['\n'] = lineBreak - p.inlineCallback['['] = link - p.inlineCallback['<'] = leftAngle - p.inlineCallback['\\'] = escape - p.inlineCallback['&'] = entity - p.inlineCallback['!'] = maybeImage - p.inlineCallback['^'] = maybeInlineFootnote - if p.extensions&Autolink != 0 { - p.inlineCallback['h'] = maybeAutoLink - p.inlineCallback['m'] = maybeAutoLink - p.inlineCallback['f'] = maybeAutoLink - p.inlineCallback['H'] = maybeAutoLink - p.inlineCallback['M'] = maybeAutoLink - p.inlineCallback['F'] = maybeAutoLink - } - if p.extensions&Footnotes != 0 { - p.notes = make([]*reference, 0) - } - return &p -} - -// Option customizes the Markdown processor's default behavior. -type Option func(*Parser) - -// WithExtensions allows you to enable extensions -func WithExtensions(e Extensions) Option { - return func(p *Parser) { - p.extensions = e - } -} - -// WithNoExtensions turns off all extensions -func WithNoExtensions() Option { - return func(p *Parser) { - p.extensions = NoExtensions - } -} - -// WithRefOverride sets an optional function callback that is called every -// time a reference is resolved. +// ToHTML converts a markdown text in input and converts it to HTML. // -// In Markdown, the link reference syntax can be made to resolve a link to -// a reference instead of an inline URL, in one of the following ways: +// You can optionally pass a parser and renderer, which allows to customize +// a parser, a render or provide a renderer other than HTMLRenderer. // -// * [link text][refid] -// * [refid][] -// -// Usually, the refid is defined at the bottom of the Markdown document. If -// this override function is provided, the refid is passed to the override -// function first, before consulting the defined refids at the bottom. If -// the override function indicates an override did not occur, the refids at -// the bottom will be used to fill in the link details. -func WithRefOverride(o ReferenceOverrideFunc) Option { - return func(p *Parser) { - p.referenceOverride = o +// If you pass nil for both, we convert with CommonExtensions for +// the parser and HTMLRenderer with CommonHTMLFlags. +func ToHTML(input []byte, parser *Parser, renderer Renderer) []byte { + if parser == nil { + parser = NewParserWithExtensions(CommonExtensions) + } + if renderer == nil { + params := HTMLRendererParameters{ + Flags: CommonHTMLFlags, + } + renderer = NewHTMLRenderer(params) } -} - -// ToHTML is the main entry point to Blackfriday. It parses and renders a -// block of markdown-encoded text. -// -// The simplest invocation of Run takes one argument, input: -// output := Run(input) -// This will parse the input with CommonExtensions enabled and render it with -// the default HTMLRenderer (with CommonHTMLFlags). -// -// Variadic arguments opts can customize the default behavior. Since Markdown -// type does not contain exported fields, you can not use it directly. Instead, -// use the With* functions. For example, this will call the most basic -// functionality, with no extensions: -// output := Run(input, WithNoExtensions()) -// -// You can use any number of With* arguments, even contradicting ones. They -// will be applied in order of appearance and the latter will override the -// former: -// output := Run(input, WithNoExtensions(), WithExtensions(exts), -// -func ToHTML(input []byte, opts ...Option) []byte { - r := NewHTMLRenderer(HTMLRendererParameters{ - Flags: CommonHTMLFlags, - }) - optList := []Option{WithExtensions(CommonExtensions)} - optList = append(optList, opts...) - parser := NewParser(optList...) parser.Parse(input) - return parser.Render(r) -} - -// ParseAndRender parsers input and renders it with a renderer -func ParseAndRender(input []byte, r Renderer, opts ...Option) []byte { - optList := []Option{WithExtensions(CommonExtensions)} - optList = append(optList, opts...) - parser := NewParser(optList...) - parser.Parse(input) - return parser.Render(r) + return parser.Render(renderer) } // Render renders a parsed data in parser with a given renderer diff --git a/s/run-bench.sh b/s/run-bench.sh new file mode 100755 index 0000000..134d059 --- /dev/null +++ b/s/run-bench.sh @@ -0,0 +1,4 @@ +#!/bin/bash +set -u -e -o pipefail -o verbose + +go test -bench=. -test.benchmem