Merge branch 'master' of https://github.com/jtolds/blackfriday into jtolds-master

Conflicts:
	inline_test.go
pull/170/head
Vytautas Šaltenis 2015-05-06 15:41:12 +03:00
commit eeb2c39339
3 changed files with 168 additions and 38 deletions

View File

@ -216,10 +216,10 @@ func link(p *parser, out *bytes.Buffer, data []byte, offset int) int {
data = data[offset:]
var (
i = 1
noteId int
title, link []byte
textHasNl = false
i = 1
noteId int
title, link, alt_content []byte
textHasNl = false
)
if t == linkDeferredFootnote {
@ -355,6 +355,7 @@ func link(p *parser, out *bytes.Buffer, data []byte, offset int) int {
// reference style link
case i < len(data)-1 && data[i] == '[' && data[i+1] != '^':
var id []byte
alt_content_considered := false
// look for the id
i++
@ -384,14 +385,14 @@ func link(p *parser, out *bytes.Buffer, data []byte, offset int) int {
id = b.Bytes()
} else {
id = data[1:txtE]
alt_content_considered = true
}
} else {
id = data[linkB:linkE]
}
// find the reference with matching id (ids are case-insensitive)
key := string(bytes.ToLower(id))
lr, ok := p.refs[key]
// find the reference with matching id
lr, ok := p.getRef(string(id))
if !ok {
return 0
@ -400,6 +401,9 @@ func link(p *parser, out *bytes.Buffer, data []byte, offset int) int {
// keep link and title from reference
link = lr.link
title = lr.title
if alt_content_considered {
alt_content = lr.text
}
i++
// shortcut reference style link or reference or inline footnote
@ -428,7 +432,6 @@ func link(p *parser, out *bytes.Buffer, data []byte, offset int) int {
}
}
key := string(bytes.ToLower(id))
if t == linkInlineFootnote {
// create a new reference
noteId = len(p.notes) + 1
@ -458,7 +461,7 @@ func link(p *parser, out *bytes.Buffer, data []byte, offset int) int {
title = ref.title
} else {
// find the reference with matching id
lr, ok := p.refs[key]
lr, ok := p.getRef(string(id))
if !ok {
return 0
}
@ -510,7 +513,11 @@ func link(p *parser, out *bytes.Buffer, data []byte, offset int) int {
// call the relevant rendering function
switch t {
case linkNormal:
p.r.Link(out, uLink, title, content.Bytes())
if len(alt_content) > 0 {
p.r.Link(out, uLink, title, alt_content)
} else {
p.r.Link(out, uLink, title, content.Bytes())
}
case linkImg:
outSize := out.Len()

View File

@ -20,19 +20,19 @@ import (
"strings"
)
func runMarkdownInline(input string, extensions, htmlFlags int, params HtmlRendererParameters) string {
extensions |= EXTENSION_AUTOLINK
extensions |= EXTENSION_STRIKETHROUGH
func runMarkdownInline(input string, opts Options, htmlFlags int, params HtmlRendererParameters) string {
opts.Extensions |= EXTENSION_AUTOLINK
opts.Extensions |= EXTENSION_STRIKETHROUGH
htmlFlags |= HTML_USE_XHTML
renderer := HtmlRendererWithParameters(htmlFlags, "", "", params)
return string(Markdown([]byte(input), renderer, extensions))
return string(MarkdownOptions([]byte(input), renderer, opts))
}
func doTestsInline(t *testing.T, tests []string) {
doTestsInlineParam(t, tests, 0, 0, HtmlRendererParameters{})
doTestsInlineParam(t, tests, Options{}, 0, HtmlRendererParameters{})
}
func doLinkTestsInline(t *testing.T, tests []string) {
@ -41,22 +41,22 @@ func doLinkTestsInline(t *testing.T, tests []string) {
prefix := "http://localhost"
params := HtmlRendererParameters{AbsolutePrefix: prefix}
transformTests := transformLinks(tests, prefix)
doTestsInlineParam(t, transformTests, 0, 0, params)
doTestsInlineParam(t, transformTests, 0, commonHtmlFlags, params)
doTestsInlineParam(t, transformTests, Options{}, 0, params)
doTestsInlineParam(t, transformTests, Options{}, commonHtmlFlags, params)
}
func doSafeTestsInline(t *testing.T, tests []string) {
doTestsInlineParam(t, tests, 0, HTML_SAFELINK, HtmlRendererParameters{})
doTestsInlineParam(t, tests, Options{}, HTML_SAFELINK, HtmlRendererParameters{})
// All the links in this test should not have the prefix appended, so
// just rerun it with different parameters and the same expectations.
prefix := "http://localhost"
params := HtmlRendererParameters{AbsolutePrefix: prefix}
transformTests := transformLinks(tests, prefix)
doTestsInlineParam(t, transformTests, 0, HTML_SAFELINK, params)
doTestsInlineParam(t, transformTests, Options{}, HTML_SAFELINK, params)
}
func doTestsInlineParam(t *testing.T, tests []string, extensions, htmlFlags int,
func doTestsInlineParam(t *testing.T, tests []string, opts Options, htmlFlags int,
params HtmlRendererParameters) {
// catch and report panics
var candidate string
@ -72,8 +72,7 @@ func doTestsInlineParam(t *testing.T, tests []string, extensions, htmlFlags int,
input := tests[i]
candidate = input
expected := tests[i+1]
actual := runMarkdownInline(candidate, extensions, htmlFlags, params)
actual := runMarkdownInline(candidate, opts, htmlFlags, params)
if actual != expected {
t.Errorf("\nInput [%#v]\nExpected[%#v]\nActual [%#v]",
candidate, expected, actual)
@ -84,7 +83,7 @@ func doTestsInlineParam(t *testing.T, tests []string, extensions, htmlFlags int,
for start := 0; start < len(input); start++ {
for end := start + 1; end <= len(input); end++ {
candidate = input[start:end]
_ = runMarkdownInline(candidate, extensions, htmlFlags, params)
_ = runMarkdownInline(candidate, opts, htmlFlags, params)
}
}
}
@ -158,6 +157,54 @@ func TestEmphasis(t *testing.T) {
doTestsInline(t, tests)
}
func TestReferenceOverride(t *testing.T) {
var tests = []string{
"test [ref1][]\n",
"<p>test <a href=\"http://www.ref1.com/\" title=\"Reference 1\">ref1</a></p>\n",
"test [my ref][ref1]\n",
"<p>test <a href=\"http://www.ref1.com/\" title=\"Reference 1\">my ref</a></p>\n",
"test [ref2][]\n\n[ref2]: http://www.leftalone.com/ (Ref left alone)\n",
"<p>test <a href=\"http://www.overridden.com/\" title=\"Reference Overridden\">ref2</a></p>\n",
"test [ref3][]\n\n[ref3]: http://www.leftalone.com/ (Ref left alone)\n",
"<p>test <a href=\"http://www.leftalone.com/\" title=\"Ref left alone\">ref3</a></p>\n",
"test [ref4][]\n\n[ref4]: http://zombo.com/ (You can do anything)\n",
"<p>test [ref4][]</p>\n",
"test [!(*http.ServeMux).ServeHTTP][] complicated ref\n",
"<p>test <a href=\"http://localhost:6060/pkg/net/http/#ServeMux.ServeHTTP\" title=\"ServeHTTP docs\">!(*http.ServeMux).ServeHTTP</a> complicated ref</p>\n",
}
doTestsInlineParam(t, tests, Options{
ReferenceOverride: func(reference string) (rv *Reference, overridden bool) {
switch reference {
case "ref1":
// just an overriden reference exists without definition
return &Reference{
Link: "http://www.ref1.com/",
Title: "Reference 1"}, true
case "ref2":
// overridden exists and reference defined
return &Reference{
Link: "http://www.overridden.com/",
Title: "Reference Overridden"}, true
case "ref3":
// not overridden and reference defined
return nil, false
case "ref4":
// overridden missing and defined
return nil, true
case "!(*http.ServeMux).ServeHTTP":
return &Reference{
Link: "http://localhost:6060/pkg/net/http/#ServeMux.ServeHTTP",
Title: "ServeHTTP docs"}, true
}
return nil, false
}}, 0, HtmlRendererParameters{})
}
func TestStrong(t *testing.T) {
var tests = []string{
"nothing inline\n",
@ -359,7 +406,9 @@ func TestLineBreak(t *testing.T) {
"this has an \nextra space\n",
"<p>this has an<br />\nextra space</p>\n",
}
doTestsInlineParam(t, tests, EXTENSION_BACKSLASH_LINE_BREAK, 0, HtmlRendererParameters{})
doTestsInlineParam(t, tests, Options{
Extensions: EXTENSION_BACKSLASH_LINE_BREAK},
0, HtmlRendererParameters{})
}
func TestInlineLink(t *testing.T) {
@ -499,7 +548,7 @@ func TestRelAttrLink(t *testing.T) {
"[foo](../bar)\n",
"<p><a href=\"../bar\">foo</a></p>\n",
}
doTestsInlineParam(t, nofollowTests, 0, HTML_SAFELINK|HTML_NOFOLLOW_LINKS,
doTestsInlineParam(t, nofollowTests, Options{}, HTML_SAFELINK|HTML_NOFOLLOW_LINKS,
HtmlRendererParameters{})
var noreferrerTests = []string{
@ -509,7 +558,7 @@ func TestRelAttrLink(t *testing.T) {
"[foo](/bar/)\n",
"<p><a href=\"/bar/\">foo</a></p>\n",
}
doTestsInlineParam(t, noreferrerTests, 0, HTML_SAFELINK|HTML_NOREFERRER_LINKS,
doTestsInlineParam(t, noreferrerTests, Options{}, HTML_SAFELINK|HTML_NOREFERRER_LINKS,
HtmlRendererParameters{})
var nofollownoreferrerTests = []string{
@ -519,7 +568,7 @@ func TestRelAttrLink(t *testing.T) {
"[foo](/bar/)\n",
"<p><a href=\"/bar/\">foo</a></p>\n",
}
doTestsInlineParam(t, nofollownoreferrerTests, 0, HTML_SAFELINK|HTML_NOFOLLOW_LINKS|HTML_NOREFERRER_LINKS,
doTestsInlineParam(t, nofollownoreferrerTests, Options{}, HTML_SAFELINK|HTML_NOFOLLOW_LINKS|HTML_NOREFERRER_LINKS,
HtmlRendererParameters{})
}
@ -547,7 +596,7 @@ func TestHrefTargetBlank(t *testing.T) {
"[foo](http://example.com)\n",
"<p><a href=\"http://example.com\" target=\"_blank\">foo</a></p>\n",
}
doTestsInlineParam(t, tests, 0, HTML_SAFELINK|HTML_HREF_TARGET_BLANK, HtmlRendererParameters{})
doTestsInlineParam(t, tests, Options{}, HTML_SAFELINK|HTML_HREF_TARGET_BLANK, HtmlRendererParameters{})
}
func TestSafeInlineLink(t *testing.T) {
@ -887,7 +936,7 @@ what happens here
}
func TestFootnotes(t *testing.T) {
doTestsInlineParam(t, footnoteTests, EXTENSION_FOOTNOTES, 0, HtmlRendererParameters{})
doTestsInlineParam(t, footnoteTests, Options{Extensions: EXTENSION_FOOTNOTES}, 0, HtmlRendererParameters{})
}
func TestFootnotesWithParameters(t *testing.T) {
@ -912,7 +961,7 @@ func TestFootnotesWithParameters(t *testing.T) {
FootnoteReturnLinkContents: returnText,
}
doTestsInlineParam(t, tests, EXTENSION_FOOTNOTES, HTML_FOOTNOTE_RETURN_LINKS, params)
doTestsInlineParam(t, tests, Options{Extensions: EXTENSION_FOOTNOTES}, HTML_FOOTNOTE_RETURN_LINKS, params)
}
func TestSmartDoubleQuotes(t *testing.T) {
@ -924,7 +973,7 @@ func TestSmartDoubleQuotes(t *testing.T) {
"two pair of \"some\" quoted \"text\".\n",
"<p>two pair of &ldquo;some&rdquo; quoted &ldquo;text&rdquo;.</p>\n"}
doTestsInlineParam(t, tests, 0, HTML_USE_SMARTYPANTS, HtmlRendererParameters{})
doTestsInlineParam(t, tests, Options{}, HTML_USE_SMARTYPANTS, HtmlRendererParameters{})
}
func TestSmartAngledDoubleQuotes(t *testing.T) {
@ -936,7 +985,7 @@ func TestSmartAngledDoubleQuotes(t *testing.T) {
"two pair of \"some\" quoted \"text\".\n",
"<p>two pair of &laquo;some&raquo; quoted &laquo;text&raquo;.</p>\n"}
doTestsInlineParam(t, tests, 0, HTML_USE_SMARTYPANTS|HTML_SMARTYPANTS_ANGLED_QUOTES, HtmlRendererParameters{})
doTestsInlineParam(t, tests, Options{}, HTML_USE_SMARTYPANTS|HTML_SMARTYPANTS_ANGLED_QUOTES, HtmlRendererParameters{})
}
func TestSmartFractions(t *testing.T) {
@ -946,7 +995,7 @@ func TestSmartFractions(t *testing.T) {
"1/2/2015, 1/4/2015, 3/4/2015; 2015/1/2, 2015/1/4, 2015/3/4.\n",
"<p>1/2/2015, 1/4/2015, 3/4/2015; 2015/1/2, 2015/1/4, 2015/3/4.</p>\n"}
doTestsInlineParam(t, tests, 0, HTML_USE_SMARTYPANTS, HtmlRendererParameters{})
doTestsInlineParam(t, tests, Options{}, HTML_USE_SMARTYPANTS, HtmlRendererParameters{})
tests = []string{
"1/2, 2/3, 81/100 and 1000000/1048576.\n",
@ -954,5 +1003,5 @@ func TestSmartFractions(t *testing.T) {
"1/2/2015, 1/4/2015, 3/4/2015; 2015/1/2, 2015/1/4, 2015/3/4.\n",
"<p>1/2/2015, 1/4/2015, 3/4/2015; 2015/1/2, 2015/1/4, 2015/3/4.</p>\n"}
doTestsInlineParam(t, tests, 0, HTML_USE_SMARTYPANTS|HTML_SMARTYPANTS_FRACTIONS, HtmlRendererParameters{})
doTestsInlineParam(t, tests, Options{}, HTML_USE_SMARTYPANTS|HTML_SMARTYPANTS_FRACTIONS, HtmlRendererParameters{})
}

View File

@ -20,6 +20,7 @@ package blackfriday
import (
"bytes"
"strings"
"unicode/utf8"
)
@ -198,6 +199,7 @@ type inlineParser func(p *parser, out *bytes.Buffer, data []byte, offset int) in
// This is constructed by the Markdown function.
type parser struct {
r Renderer
refOverride ReferenceOverrideFunc
refs map[string]*reference
inlineCallback [256]inlineParser
flags int
@ -211,12 +213,74 @@ type parser struct {
notes []*reference
}
func (p *parser) getRef(refid string) (ref *reference, found bool) {
if p.refOverride != nil {
r, overridden := p.refOverride(refid)
if overridden {
if r == nil {
return nil, false
}
return &reference{
link: []byte(r.Link),
title: []byte(r.Title),
noteId: 0,
hasBlock: false,
text: []byte(r.Text)}, true
}
}
// refs are case insensitive
ref, found = p.refs[strings.ToLower(refid)]
return ref, found
}
//
//
// Public interface
//
//
// Reference represents the details of a link.
// See the documentation in Options for more details on use-case.
type Reference struct {
// Link is usually the URL the reference points to.
Link string
// Title is the alternate text describing the link in more detail.
Title string
// Text is the optional text to override the ref with if the syntax used was
// [refid][]
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)
// Options represents configurable overrides and callbacks (in addition to the
// extension flag set) for configuring a Markdown parse.
type Options struct {
// Extensions is a flag set of bit-wise ORed extension bits. See the
// EXTENSION_* flags defined in this package.
Extensions int
// ReferenceOverride is an optional function callback that is called every
// time a reference is resolved.
//
// 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
}
// MarkdownBasic is a convenience function for simple rendering.
// It processes markdown input with no extensions enabled.
func MarkdownBasic(input []byte) []byte {
@ -225,9 +289,7 @@ func MarkdownBasic(input []byte) []byte {
renderer := HtmlRenderer(htmlFlags, "", "")
// set up the parser
extensions := 0
return Markdown(input, renderer, extensions)
return MarkdownOptions(input, renderer, Options{Extensions: 0})
}
// Call Markdown with most useful extensions enabled
@ -252,7 +314,8 @@ func MarkdownBasic(input []byte) []byte {
func MarkdownCommon(input []byte) []byte {
// set up the HTML renderer
renderer := HtmlRenderer(commonHtmlFlags, "", "")
return Markdown(input, renderer, commonExtensions)
return MarkdownOptions(input, renderer, Options{
Extensions: commonExtensions})
}
// Markdown is the main rendering function.
@ -263,15 +326,25 @@ func MarkdownCommon(input []byte) []byte {
// To use the supplied Html or LaTeX renderers, see HtmlRenderer and
// LatexRenderer, respectively.
func Markdown(input []byte, renderer Renderer, extensions int) []byte {
return MarkdownOptions(input, renderer, Options{
Extensions: extensions})
}
// MarkdownOptions is just like Markdown but takes additional options through
// the Options struct.
func MarkdownOptions(input []byte, renderer Renderer, opts Options) []byte {
// no point in parsing if we can't render
if renderer == nil {
return nil
}
extensions := opts.Extensions
// fill in the render structure
p := new(parser)
p.r = renderer
p.flags = extensions
p.refOverride = opts.ReferenceOverride
p.refs = make(map[string]*reference)
p.maxNesting = 16
p.insideLink = false
@ -438,6 +511,7 @@ type reference struct {
title []byte
noteId int // 0 if not a footnote ref
hasBlock bool
text []byte
}
// Check whether or not data starts with a reference link.