all repos — grayfriday @ dd01088b7a8cbc37bab872a8d5891202b0a863ec

blackfriday fork with a few changes

Remove last call to Truncate() from parser

Autolink detection used to be triggered by a colon and preceding
protocol name used to be rewound. Now instead of doing that, trigger
autolink processing on [hmfHMF] and see if it looks like a link.
Vytautas Ĺ altenis vytas@rtfb.lt
Tue, 27 Oct 2015 21:56:16 +0200
commit

dd01088b7a8cbc37bab872a8d5891202b0a863ec

parent

8e90e8b6455f1608d6a0948d0eb07409bcf874d4

3 files changed, 51 insertions(+), 14 deletions(-)

jump to
M inline.goinline.go

@@ -38,9 +38,23 @@ p.nesting++

i, end := 0, 0 for i < len(data) { - // copy inactive chars into the output - for end < len(data) && p.inlineCallback[data[end]] == nil { - end++ + // Copy inactive chars into the output, but first check for one quirk: + // 'h', 'm' and 'f' all might trigger a check for autolink processing + // and end this run of inactive characters. However, there's one nasty + // case where breaking this run would be bad: in smartypants fraction + // detection, we expect things like "1/2th" to be in a single run. So + // we check here if an 'h' is followed by 't' (from 'http') and if it's + // not, we short circuit the 'h' into the run of inactive characters. + for end < len(data) { + if p.inlineCallback[data[end]] != nil { + if end+1 < len(data) && data[end] == 'h' && data[end+1] != 't' { + end++ + } else { + break + } + } else { + end++ + } } p.r.NormalText(out, data[i:end])

@@ -678,12 +692,32 @@ entityRanges := htmlEntity.FindAllIndex(data[:linkEnd], -1)

return entityRanges != nil && entityRanges[len(entityRanges)-1][1] == linkEnd } -func autoLink(p *parser, out *bytes.Buffer, data []byte, offset int) int { - // quick check to rule out most false hits on ':' - if p.insideLink || len(data) < offset+3 || data[offset+1] != '/' || data[offset+2] != '/' { +func maybeAutoLink(p *parser, out *bytes.Buffer, data []byte, offset int) int { + // quick check to rule out most false hits + if p.insideLink || len(data) < offset+6 { // 6 is the len() of the shortest prefix below return 0 } + prefixes := []string{ + "http://", + "https://", + "ftp://", + "file://", + "mailto:", + } + for _, prefix := range prefixes { + endOfHead := offset + 8 // 8 is the len() of the longest prefix + if endOfHead > len(data) { + endOfHead = len(data) + } + head := bytes.ToLower(data[offset:endOfHead]) + if bytes.HasPrefix(head, []byte(prefix)) { + return autoLink(p, out, data, offset) + } + } + return 0 +} +func autoLink(p *parser, out *bytes.Buffer, data []byte, offset int) int { // Now a more expensive check to see if we're not inside an anchor element anchorStart := offset offsetFromAnchor := 0

@@ -694,7 +728,7 @@ }

anchorStr := anchorRe.Find(data[anchorStart:]) if anchorStr != nil { - out.Write(anchorStr[offsetFromAnchor:]) + out.Write(anchorStr[offsetFromAnchor:]) // XXX: write in parser? return len(anchorStr) - offsetFromAnchor }

@@ -788,11 +822,6 @@ linkEnd--

} } - // we were triggered on the ':', so we need to rewind the output a bit - if out.Len() >= rewind { - out.Truncate(len(out.Bytes()) - rewind) - } - var uLink bytes.Buffer unescapeText(&uLink, data[:linkEnd])

@@ -800,7 +829,7 @@ if uLink.Len() > 0 {

p.r.AutoLink(out, uLink.Bytes(), LinkTypeNormal) } - return linkEnd - rewind + return linkEnd } func isEndOfLink(char byte) bool {
M inline_test.goinline_test.go

@@ -808,6 +808,9 @@ "<p><a href=\"http://foo.com/viewtopic.php?param=&quot;18&quot;zz\">http://foo.com/viewtopic.php?param=&quot;18&quot;zz</a></p>\n",

"http://foo.com/viewtopic.php?param=&quot;18&quot;", "<p><a href=\"http://foo.com/viewtopic.php?param=&quot;18&quot;\">http://foo.com/viewtopic.php?param=&quot;18&quot;</a></p>\n", + + "<a href=\"https://fancy.com\">https://fancy.com</a>\n", + "<p><a href=\"https://fancy.com\">https://fancy.com</a></p>\n", } doLinkTestsInline(t, tests) }
M markdown.gomarkdown.go

@@ -379,7 +379,12 @@ p.inlineCallback['^'] = maybeInlineFootnote

p.inlineCallback[' '] = maybeLineBreak if extensions&Autolink != 0 { - p.inlineCallback[':'] = autoLink + 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 extensions&Footnotes != 0 {