Compare commits

..

No commits in common. "master" and "v1.5.1" have entirely different histories.

13 changed files with 165 additions and 292 deletions

View File

@ -1,14 +1,26 @@
sudo: false
language: go
go:
- "1.9.x"
- "1.10.x"
- "1.11.x"
- 1.5.4
- 1.6.2
- tip
matrix:
fast_finish: true
include:
- go: 1.2.2
script:
- go get -t -v ./...
- go test -v -race ./...
- go: 1.3.3
script:
- go get -t -v ./...
- go test -v -race ./...
- go: 1.4.3
script:
- go get -t -v ./...
- go test -v -race ./...
allow_failures:
- go: tip
fast_finish: true
install:
- # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step).
script:

View File

@ -1,28 +1,29 @@
Blackfriday is distributed under the Simplified BSD License:
Copyright © 2011 Russ Ross
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided with
the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
> Copyright © 2011 Russ Ross
> All rights reserved.
>
> Redistribution and use in source and binary forms, with or without
> modification, are permitted provided that the following conditions
> are met:
>
> 1. Redistributions of source code must retain the above copyright
> notice, this list of conditions and the following disclaimer.
>
> 2. Redistributions in binary form must reproduce the above
> copyright notice, this list of conditions and the following
> disclaimer in the documentation and/or other materials provided with
> the distribution.
>
> THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
> FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
> BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
> LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
> CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
> LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
> ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
> POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,6 +1,6 @@
Blackfriday
[![Build Status][BuildV2SVG]][BuildV2URL]
[![PkgGoDev][PkgGoDevV2SVG]][PkgGoDevV2URL]
[![Build Status][BuildSVG]][BuildURL]
[![Godoc][GodocV2SVG]][GodocV2URL]
===========
Blackfriday is a [Markdown][1] processor implemented in [Go][2]. It
@ -18,21 +18,12 @@ It started as a translation from C of [Sundown][3].
Installation
------------
Blackfriday is compatible with modern Go releases in module mode.
With Go installed:
Blackfriday is compatible with any modern Go release. With Go and git installed:
go get github.com/russross/blackfriday
go get -u gopkg.in/russross/blackfriday.v2
will resolve and add the package to the current development module,
then build and install it. Alternatively, you can achieve the same
if you import it in a package:
import "github.com/russross/blackfriday"
and `go get` without parameters.
Old versions of Go and legacy GOPATH mode might work,
but no effort is made to keep them working.
will download, compile, and install the package into your `$GOPATH` directory
hierarchy.
Versions
@ -41,9 +32,13 @@ Versions
Currently maintained and recommended version of Blackfriday is `v2`. It's being
developed on its own branch: https://github.com/russross/blackfriday/tree/v2 and the
documentation is available at
https://pkg.go.dev/github.com/russross/blackfriday/v2.
https://godoc.org/gopkg.in/russross/blackfriday.v2.
It is `go get`-able in module mode at `github.com/russross/blackfriday/v2`.
It is `go get`-able via via [gopkg.in][6] at `gopkg.in/russross/blackfriday.v2`,
but we highly recommend using package management tool like [dep][7] or
[Glide][8] and make use of semantic versioning. With package management you
should import `github.com/russross/blackfriday` and specify that you're using
version 2.0.0.
Version 2 offers a number of improvements over v1:
@ -65,7 +60,22 @@ Potential drawbacks:
If you are still interested in the legacy `v1`, you can import it from
`github.com/russross/blackfriday`. Documentation for the legacy v1 can be found
here: https://pkg.go.dev/github.com/russross/blackfriday.
here: https://godoc.org/github.com/russross/blackfriday
### Known issue with `dep`
There is a known problem with using Blackfriday v1 _transitively_ and `dep`.
Currently `dep` prioritizes semver versions over anything else, and picks the
latest one, plus it does not apply a `[[constraint]]` specifier to transitively
pulled in packages. So if you're using something that uses Blackfriday v1, but
that something does not use `dep` yet, you will get Blackfriday v2 pulled in and
your first dependency will fail to build.
There are couple of fixes for it, documented here:
https://github.com/golang/dep/blob/master/docs/FAQ.md#how-do-i-constrain-a-transitive-dependencys-version
Meanwhile, `dep` team is working on a more general solution to the constraints
on transitive dependencies problem: https://github.com/golang/dep/issues/1124.
Usage
@ -76,16 +86,12 @@ Usage
For basic usage, it is as simple as getting your input into a byte
slice and calling:
```go
output := blackfriday.MarkdownBasic(input)
```
output := blackfriday.MarkdownBasic(input)
This renders it with no extensions enabled. To get a more useful
feature set, use this instead:
```go
output := blackfriday.MarkdownCommon(input)
```
output := blackfriday.MarkdownCommon(input)
### v2
@ -115,7 +121,7 @@ Here's an example of simple usage of Blackfriday together with Bluemonday:
```go
import (
"github.com/microcosm-cc/bluemonday"
"github.com/russross/blackfriday"
"gopkg.in/russross/blackfriday.v2"
)
// ...
@ -148,7 +154,7 @@ markdown file using a standalone program. You can also browse the
source directly on github if you are just looking for some example
code:
* <https://github.com/russross/blackfriday-tool>
* <http://github.com/russross/blackfriday-tool>
Note that if you have not already done so, installing
`blackfriday-tool` will be sufficient to download and install
@ -165,12 +171,12 @@ anchors for headings when `EXTENSION_AUTO_HEADER_IDS` is enabled. The
algorithm has a specification, so that other packages can create
compatible anchor names and links to those anchors.
The specification is located at https://pkg.go.dev/github.com/russross/blackfriday#hdr-Sanitized_Anchor_Names.
The specification is located at https://godoc.org/github.com/russross/blackfriday#hdr-Sanitized_Anchor_Names.
[`SanitizedAnchorName`](https://pkg.go.dev/github.com/russross/blackfriday#SanitizedAnchorName) exposes this functionality, and can be used to
[`SanitizedAnchorName`](https://godoc.org/github.com/russross/blackfriday#SanitizedAnchorName) exposes this functionality, and can be used to
create compatible links to the anchor names generated by blackfriday.
This algorithm is also implemented in a small standalone package at
[`github.com/shurcooL/sanitized_anchor_name`](https://pkg.go.dev/github.com/shurcooL/sanitized_anchor_name). It can be useful for clients
[`github.com/shurcooL/sanitized_anchor_name`](https://godoc.org/github.com/shurcooL/sanitized_anchor_name). It can be useful for clients
that want a small package and don't need full functionality of blackfriday.
@ -240,7 +246,7 @@ implements the following extensions:
and supply a language (to make syntax highlighting simple). Just
mark it like this:
```go
``` go
func getTrue() bool {
return true
}
@ -252,7 +258,7 @@ implements the following extensions:
To preserve classes of fenced code blocks while using the bluemonday
HTML sanitizer, use the following policy:
```go
``` go
p := bluemonday.UGCPolicy()
p.AllowAttrs("class").Matching(regexp.MustCompile("^language-[a-zA-Z0-9]+$")).OnElements("code")
html := p.SanitizeBytes(unsafe)
@ -263,7 +269,7 @@ implements the following extensions:
Cat
: Fluffy animal everyone likes
Internet
: Vector of transmission for pictures of cats
@ -274,7 +280,7 @@ implements the following extensions:
end of the document. A footnote looks like this:
This is a footnote.[^1]
[^1]: the footnote text.
* **Autolinking**. Blackfriday can find URLs that have not been
@ -311,7 +317,7 @@ Other renderers
Blackfriday is structured to allow alternative rendering engines. Here
are a few of note:
* [github_flavored_markdown](https://pkg.go.dev/github.com/shurcooL/github_flavored_markdown):
* [github_flavored_markdown](https://godoc.org/github.com/shurcooL/github_flavored_markdown):
provides a GitHub Flavored Markdown renderer with fenced code block
highlighting, clickable heading anchor links.
@ -322,19 +328,9 @@ are a few of note:
* [markdownfmt](https://github.com/shurcooL/markdownfmt): like gofmt,
but for markdown.
* [LaTeX output](https://gitlab.com/ambrevar/blackfriday-latex):
* [LaTeX output](https://bitbucket.org/ambrevar/blackfriday-latex):
renders output as LaTeX.
* [bfchroma](https://github.com/Depado/bfchroma/): provides convenience
integration with the [Chroma](https://github.com/alecthomas/chroma) code
highlighting library. bfchroma is only compatible with v2 of Blackfriday and
provides a drop-in renderer ready to use with Blackfriday, as well as
options and means for further customization.
* [Blackfriday-Confluence](https://github.com/kentaro-m/blackfriday-confluence): provides a [Confluence Wiki Markup](https://confluence.atlassian.com/doc/confluence-wiki-markup-251003035.html) renderer.
* [Blackfriday-Slack](https://github.com/karriereat/blackfriday-slack): converts markdown to slack message style
TODO
----
@ -355,10 +351,13 @@ License
[1]: https://daringfireball.net/projects/markdown/ "Markdown"
[2]: https://golang.org/ "Go Language"
[3]: https://github.com/vmg/sundown "Sundown"
[4]: https://pkg.go.dev/github.com/russross/blackfriday/v2#Parse "Parse func"
[4]: https://godoc.org/gopkg.in/russross/blackfriday.v2#Parse "Parse func"
[5]: https://github.com/microcosm-cc/bluemonday "Bluemonday"
[6]: https://labix.org/gopkg.in "gopkg.in"
[7]: https://github.com/golang/dep/ "dep"
[8]: https://github.com/Masterminds/glide "Glide"
[BuildV2SVG]: https://travis-ci.org/russross/blackfriday.svg?branch=v2
[BuildV2URL]: https://travis-ci.org/russross/blackfriday
[PkgGoDevV2SVG]: https://pkg.go.dev/badge/github.com/russross/blackfriday/v2
[PkgGoDevV2URL]: https://pkg.go.dev/github.com/russross/blackfriday/v2
[BuildSVG]: https://travis-ci.org/russross/blackfriday.svg?branch=master
[BuildURL]: https://travis-ci.org/russross/blackfriday
[GodocV2SVG]: https://godoc.org/gopkg.in/russross/blackfriday.v2?status.svg
[GodocV2URL]: https://godoc.org/gopkg.in/russross/blackfriday.v2

View File

@ -15,7 +15,6 @@ package blackfriday
import (
"bytes"
"strings"
"unicode"
)
@ -93,7 +92,7 @@ func (p *parser) block(out *bytes.Buffer, data []byte) {
// fenced code block:
//
// ``` go info string here
// ``` go
// func fact(n int) int {
// if n <= 1 {
// return n
@ -563,7 +562,7 @@ func (*parser) isHRule(data []byte) bool {
// and returns the end index if so, or 0 otherwise. It also returns the marker found.
// If syntax is not nil, it gets set to the syntax specified in the fence line.
// A final newline is mandatory to recognize the fence line, unless newlineOptional is true.
func isFenceLine(data []byte, info *string, oldmarker string, newlineOptional bool) (end int, marker string) {
func isFenceLine(data []byte, syntax *string, oldmarker string, newlineOptional bool) (end int, marker string) {
i, size := 0, 0
// skip up to three spaces
@ -599,9 +598,9 @@ func isFenceLine(data []byte, info *string, oldmarker string, newlineOptional bo
}
// TODO(shurcooL): It's probably a good idea to simplify the 2 code paths here
// into one, always get the info string, and discard it if the caller doesn't care.
if info != nil {
infoLength := 0
// into one, always get the syntax, and discard it if the caller doesn't care.
if syntax != nil {
syn := 0
i = skipChar(data, i, ' ')
if i >= len(data) {
@ -611,14 +610,14 @@ func isFenceLine(data []byte, info *string, oldmarker string, newlineOptional bo
return 0, ""
}
infoStart := i
syntaxStart := i
if data[i] == '{' {
i++
infoStart++
syntaxStart++
for i < len(data) && data[i] != '}' && data[i] != '\n' {
infoLength++
syn++
i++
}
@ -628,46 +627,43 @@ func isFenceLine(data []byte, info *string, oldmarker string, newlineOptional bo
// strip all whitespace at the beginning and the end
// of the {} block
for infoLength > 0 && isspace(data[infoStart]) {
infoStart++
infoLength--
for syn > 0 && isspace(data[syntaxStart]) {
syntaxStart++
syn--
}
for infoLength > 0 && isspace(data[infoStart+infoLength-1]) {
infoLength--
for syn > 0 && isspace(data[syntaxStart+syn-1]) {
syn--
}
i++
} else {
for i < len(data) && !isverticalspace(data[i]) {
infoLength++
for i < len(data) && !isspace(data[i]) {
syn++
i++
}
}
*info = strings.TrimSpace(string(data[infoStart : infoStart+infoLength]))
*syntax = string(data[syntaxStart : syntaxStart+syn])
}
i = skipChar(data, i, ' ')
if i >= len(data) {
if newlineOptional {
if i >= len(data) || data[i] != '\n' {
if newlineOptional && i == len(data) {
return i, marker
}
return 0, ""
}
if data[i] == '\n' {
i++ // Take newline into account
}
return i, marker
return i + 1, marker // Take newline into account.
}
// fencedCodeBlock returns the end index if data contains a fenced code block at the beginning,
// or 0 otherwise. It writes to out if doRender is true, otherwise it has no side effects.
// If doRender is true, a final newline is mandatory to recognize the fenced code block.
func (p *parser) fencedCodeBlock(out *bytes.Buffer, data []byte, doRender bool) int {
var infoString string
beg, marker := isFenceLine(data, &infoString, "", false)
var syntax string
beg, marker := isFenceLine(data, &syntax, "", false)
if beg == 0 || beg >= len(data) {
return 0
}
@ -701,7 +697,7 @@ func (p *parser) fencedCodeBlock(out *bytes.Buffer, data []byte, doRender bool)
}
if doRender {
p.r.BlockCode(out, work.Bytes(), infoString)
p.r.BlockCode(out, work.Bytes(), syntax)
}
return beg
@ -1136,15 +1132,6 @@ func (p *parser) listItem(out *bytes.Buffer, data []byte, flags *int) int {
i++
}
// process the following lines
containsBlankLine := false
sublist := 0
codeBlockMarker := ""
if p.flags&EXTENSION_FENCED_CODE != 0 && i > line {
// determine if codeblock starts on the first line
_, codeBlockMarker = isFenceLine(data[line:i], nil, "", false)
}
// get working buffer
var raw bytes.Buffer
@ -1152,6 +1139,10 @@ func (p *parser) listItem(out *bytes.Buffer, data []byte, flags *int) int {
raw.Write(data[line:i])
line = i
// process the following lines
containsBlankLine := false
sublist := 0
gatherlines:
for line < len(data) {
i++
@ -1160,6 +1151,7 @@ gatherlines:
for data[i-1] != '\n' {
i++
}
// if it is an empty line, guess that it is part of this item
// and move on to the next line
if p.isEmpty(data[line:i]) > 0 {
@ -1177,28 +1169,6 @@ gatherlines:
chunk := data[line+indent : i]
if p.flags&EXTENSION_FENCED_CODE != 0 {
// determine if in or out of codeblock
// if in codeblock, ignore normal list processing
_, marker := isFenceLine(chunk, nil, codeBlockMarker, false)
if marker != "" {
if codeBlockMarker == "" {
// start of codeblock
codeBlockMarker = marker
} else {
// end of codeblock.
*flags |= LIST_ITEM_CONTAINS_BLOCK
codeBlockMarker = ""
}
}
// we are in a codeblock, write line, and continue
if codeBlockMarker != "" || marker != "" {
raw.Write(data[line+indent : i])
line = i
continue gatherlines
}
}
// evaluate how this line fits in
switch {
// is this a nested list item?

View File

@ -697,8 +697,8 @@ func TestUnorderedList(t *testing.T) {
"* List\n extra indent, same paragraph\n",
"<ul>\n<li>List\n extra indent, same paragraph</li>\n</ul>\n",
"* List\n\n code block\n\n* List continues",
"<ul>\n<li><p>List</p>\n\n<pre><code>code block\n</code></pre></li>\n\n<li><p>List continues</p></li>\n</ul>\n",
"* List\n\n code block\n",
"<ul>\n<li><p>List</p>\n\n<pre><code>code block\n</code></pre></li>\n</ul>\n",
"* List\n\n code block with spaces\n",
"<ul>\n<li><p>List</p>\n\n<pre><code> code block with spaces\n</code></pre></li>\n</ul>\n",
@ -1053,9 +1053,6 @@ func TestFencedCodeBlock(t *testing.T) {
"``` go\nfunc foo() bool {\n\treturn true;\n}\n```\n",
"<pre><code class=\"language-go\">func foo() bool {\n\treturn true;\n}\n</code></pre>\n",
"``` go foo bar\nfunc foo() bool {\n\treturn true;\n}\n```\n",
"<pre><code class=\"language-go\">func foo() bool {\n\treturn true;\n}\n</code></pre>\n",
"``` c\n/* special & char < > \" escaping */\n```\n",
"<pre><code class=\"language-c\">/* special &amp; char &lt; &gt; &quot; escaping */\n</code></pre>\n",
@ -1096,7 +1093,7 @@ func TestFencedCodeBlock(t *testing.T) {
"<p>``` lisp\nno ending</p>\n",
"~~~ lisp\nend with language\n~~~ lisp\n",
"<pre><code class=\"language-lisp\">end with language\n</code></pre>\n\n<p>lisp</p>\n",
"<p>~~~ lisp\nend with language\n~~~ lisp</p>\n",
"```\nmismatched begin and end\n~~~\n",
"<p>```\nmismatched begin and end\n~~~</p>\n",
@ -1139,21 +1136,6 @@ func TestFencedCodeBlock(t *testing.T) {
"```\n[]:()\n[]:)\n[]:(\n[]:x\n[]:testing\n[:testing\n\n[]:\nlinebreak\n[]()\n\n[]:\n[]()\n```",
"<pre><code>[]:()\n[]:)\n[]:(\n[]:x\n[]:testing\n[:testing\n\n[]:\nlinebreak\n[]()\n\n[]:\n[]()\n</code></pre>\n",
"- test\n\n```\n codeblock\n ```\ntest\n",
"<ul>\n<li><p>test</p>\n\n<pre><code>codeblock\n</code></pre></li>\n</ul>\n\n<p>test</p>\n",
"- ```\n codeblock\n ```\n\n- test\n",
"<ul>\n<li><pre><code>codeblock\n</code></pre></li>\n\n<li><p>test</p></li>\n</ul>\n",
"- test\n- ```\n codeblock\n ```\n",
"<ul>\n<li>test</li>\n\n<li><pre><code>codeblock\n</code></pre></li>\n</ul>\n",
"- test\n```\ncodeblock\n```\n\n- test\n",
"<ul>\n<li><p>test</p>\n\n<pre><code>codeblock\n</code></pre></li>\n\n<li><p>test</p></li>\n</ul>\n",
"- test\n```go\nfunc foo() bool {\n\treturn true;\n}\n```\n\n- test\n",
"<ul>\n<li><p>test</p>\n\n<pre><code class=\"language-go\">func foo() bool {\n\treturn true;\n}\n</code></pre></li>\n\n<li><p>test</p></li>\n</ul>\n",
}
doTestsBlock(t, tests, EXTENSION_FENCED_CODE)
}
@ -1529,9 +1511,6 @@ func TestFencedCodeBlock_EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK(t *testing.T) {
"``` go\nfunc foo() bool {\n\treturn true;\n}\n```\n",
"<pre><code class=\"language-go\">func foo() bool {\n\treturn true;\n}\n</code></pre>\n",
"``` go foo bar\nfunc foo() bool {\n\treturn true;\n}\n```\n",
"<pre><code class=\"language-go\">func foo() bool {\n\treturn true;\n}\n</code></pre>\n",
"``` c\n/* special & char < > \" escaping */\n```\n",
"<pre><code class=\"language-c\">/* special &amp; char &lt; &gt; &quot; escaping */\n</code></pre>\n",
@ -1572,7 +1551,7 @@ func TestFencedCodeBlock_EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK(t *testing.T) {
"<p>``` lisp\nno ending</p>\n",
"~~~ lisp\nend with language\n~~~ lisp\n",
"<pre><code class=\"language-lisp\">end with language\n</code></pre>\n\n<p>lisp</p>\n",
"<p>~~~ lisp\nend with language\n~~~ lisp</p>\n",
"```\nmismatched begin and end\n~~~\n",
"<p>```\nmismatched begin and end\n~~~</p>\n",
@ -1598,44 +1577,6 @@ func TestFencedCodeBlock_EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK(t *testing.T) {
doTestsBlock(t, tests, EXTENSION_FENCED_CODE|EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK)
}
func TestListWithFencedCodeBlock(t *testing.T) {
var tests = []string{
"1. one\n\n ```\n code\n ```\n\n2. two\n",
"<ol>\n<li><p>one</p>\n\n<pre><code>code\n</code></pre></li>\n\n<li><p>two</p></li>\n</ol>\n",
// https://github.com/russross/blackfriday/issues/239
"1. one\n\n ```\n - code\n ```\n\n2. two\n",
"<ol>\n<li><p>one</p>\n\n<pre><code>- code\n</code></pre></li>\n\n<li><p>two</p></li>\n</ol>\n",
}
doTestsBlock(t, tests, EXTENSION_FENCED_CODE)
}
func TestListWithMalformedFencedCodeBlock(t *testing.T) {
// Ensure that in the case of an unclosed fenced code block in a list,
// no source gets ommitted (even if it is malformed).
// See russross/blackfriday#372 for context.
var tests = []string{
"1. one\n\n ```\n code\n\n2. two\n",
"<ol>\n<li>one\n\n```\ncode\n\n2. two</li>\n</ol>\n",
"1. one\n\n ```\n - code\n\n2. two\n",
"<ol>\n<li>one\n\n```\n- code\n\n2. two</li>\n</ol>\n",
}
doTestsBlock(t, tests, EXTENSION_FENCED_CODE)
}
func TestListWithFencedCodeBlockNoExtensions(t *testing.T) {
// If there is a fenced code block in a list, and FencedCode is not set,
// lists should be processed normally.
var tests = []string{
"1. one\n\n ```\n code\n ```\n\n2. two\n",
"<ol>\n<li><p>one</p>\n\n<p><code>\ncode\n</code></p></li>\n\n<li><p>two</p></li>\n</ol>\n",
"1. one\n\n ```\n - code\n ```\n\n2. two\n",
"<ol>\n<li><p>one</p>\n\n<p>```</p>\n\n<ul>\n<li>code\n```</li>\n</ul></li>\n\n<li><p>two</p></li>\n</ol>\n",
}
doTestsBlock(t, tests, 0)
}
func TestTitleBlock_EXTENSION_TITLEBLOCK(t *testing.T) {
var tests = []string{
"% Some title\n" +
@ -1705,11 +1646,11 @@ func TestCDATA(t *testing.T) {
func TestIsFenceLine(t *testing.T) {
tests := []struct {
data []byte
infoRequested bool
syntaxRequested bool
newlineOptional bool
wantEnd int
wantMarker string
wantInfo string
wantSyntax string
}{
{
data: []byte("```"),
@ -1721,10 +1662,10 @@ func TestIsFenceLine(t *testing.T) {
wantMarker: "```",
},
{
data: []byte("```\nstuff here\n"),
infoRequested: true,
wantEnd: 4,
wantMarker: "```",
data: []byte("```\nstuff here\n"),
syntaxRequested: true,
wantEnd: 4,
wantMarker: "```",
},
{
data: []byte("stuff here\n```\n"),
@ -1738,52 +1679,36 @@ func TestIsFenceLine(t *testing.T) {
},
{
data: []byte("```"),
infoRequested: true,
syntaxRequested: true,
newlineOptional: true,
wantEnd: 3,
wantMarker: "```",
},
{
data: []byte("``` go"),
infoRequested: true,
syntaxRequested: true,
newlineOptional: true,
wantEnd: 6,
wantMarker: "```",
wantInfo: "go",
},
{
data: []byte("``` go foo bar"),
infoRequested: true,
newlineOptional: true,
wantEnd: 14,
wantMarker: "```",
wantInfo: "go foo bar",
},
{
data: []byte("``` go foo bar "),
infoRequested: true,
newlineOptional: true,
wantEnd: 16,
wantMarker: "```",
wantInfo: "go foo bar",
wantSyntax: "go",
},
}
for _, test := range tests {
var info *string
if test.infoRequested {
info = new(string)
var syntax *string
if test.syntaxRequested {
syntax = new(string)
}
end, marker := isFenceLine(test.data, info, "```", test.newlineOptional)
end, marker := isFenceLine(test.data, syntax, "```", test.newlineOptional)
if got, want := end, test.wantEnd; got != want {
t.Errorf("got end %v, want %v", got, want)
}
if got, want := marker, test.wantMarker; got != want {
t.Errorf("got marker %q, want %q", got, want)
}
if test.infoRequested {
if got, want := *info, test.wantInfo; got != want {
t.Errorf("got info %q, want %q", got, want)
if test.syntaxRequested {
if got, want := *syntax, test.wantSyntax; got != want {
t.Errorf("got syntax %q, want %q", got, want)
}
}
}

3
go.mod
View File

@ -1,3 +0,0 @@
module github.com/russross/blackfriday
go 1.13

35
html.go
View File

@ -32,7 +32,6 @@ const (
HTML_SAFELINK // only link to trusted protocols
HTML_NOFOLLOW_LINKS // only link with rel="nofollow"
HTML_NOREFERRER_LINKS // only link with rel="noreferrer"
HTML_NOOPENER_LINKS // only link with rel="noopener"
HTML_HREF_TARGET_BLANK // add a blank target
HTML_TOC // generate a table of contents
HTML_OMIT_CONTENTS // skip the main contents (for a standalone table of contents)
@ -256,21 +255,33 @@ func (options *Html) HRule(out *bytes.Buffer) {
out.WriteByte('\n')
}
func (options *Html) BlockCode(out *bytes.Buffer, text []byte, info string) {
func (options *Html) BlockCode(out *bytes.Buffer, text []byte, lang string) {
doubleSpace(out)
endOfLang := strings.IndexAny(info, "\t ")
if endOfLang < 0 {
endOfLang = len(info)
// parse out the language names/classes
count := 0
for _, elt := range strings.Fields(lang) {
if elt[0] == '.' {
elt = elt[1:]
}
if len(elt) == 0 {
continue
}
if count == 0 {
out.WriteString("<pre><code class=\"language-")
} else {
out.WriteByte(' ')
}
attrEscape(out, []byte(elt))
count++
}
lang := info[:endOfLang]
if len(lang) == 0 || lang == "." {
if count == 0 {
out.WriteString("<pre><code>")
} else {
out.WriteString("<pre><code class=\"language-")
attrEscape(out, []byte(lang))
out.WriteString("\">")
}
attrEscape(out, text)
out.WriteString("</code></pre>\n")
}
@ -446,9 +457,6 @@ func (options *Html) AutoLink(out *bytes.Buffer, link []byte, kind int) {
if options.flags&HTML_NOREFERRER_LINKS != 0 && !isRelativeLink(link) {
relAttrs = append(relAttrs, "noreferrer")
}
if options.flags&HTML_NOOPENER_LINKS != 0 && !isRelativeLink(link) {
relAttrs = append(relAttrs, "noopener")
}
if len(relAttrs) > 0 {
out.WriteString(fmt.Sprintf("\" rel=\"%s", strings.Join(relAttrs, " ")))
}
@ -563,9 +571,6 @@ func (options *Html) Link(out *bytes.Buffer, link []byte, title []byte, content
if options.flags&HTML_NOREFERRER_LINKS != 0 && !isRelativeLink(link) {
relAttrs = append(relAttrs, "noreferrer")
}
if options.flags&HTML_NOOPENER_LINKS != 0 && !isRelativeLink(link) {
relAttrs = append(relAttrs, "noopener")
}
if len(relAttrs) > 0 {
out.WriteString(fmt.Sprintf("\" rel=\"%s", strings.Join(relAttrs, " ")))
}

View File

@ -252,7 +252,7 @@ func link(p *parser, out *bytes.Buffer, data []byte, offset int) int {
case data[i] == '\n':
textHasNl = true
case isBackslashEscaped(data, i):
case data[i-1] == '\\':
continue
case data[i] == '[':

View File

@ -624,25 +624,6 @@ func TestRelAttrLink(t *testing.T) {
}
doTestsInlineParam(t, nofollownoreferrerTests, Options{}, HTML_SAFELINK|HTML_NOFOLLOW_LINKS|HTML_NOREFERRER_LINKS,
HtmlRendererParameters{})
var noopenerTests = []string{
"[foo](http://bar.com/foo/)\n",
"<p><a href=\"http://bar.com/foo/\" rel=\"noopener\">foo</a></p>\n",
"[foo](/bar/)\n",
"<p><a href=\"/bar/\">foo</a></p>\n",
}
doTestsInlineParam(t, noopenerTests, Options{}, HTML_SAFELINK|HTML_NOOPENER_LINKS, HtmlRendererParameters{})
var nofollownoreferrernoopenerTests = []string{
"[foo](http://bar.com/foo/)\n",
"<p><a href=\"http://bar.com/foo/\" rel=\"nofollow noreferrer noopener\">foo</a></p>\n",
"[foo](/bar/)\n",
"<p><a href=\"/bar/\">foo</a></p>\n",
}
doTestsInlineParam(t, nofollownoreferrernoopenerTests, Options{},
HTML_SAFELINK|HTML_NOOPENER_LINKS|HTML_NOFOLLOW_LINKS|HTML_NOREFERRER_LINKS, HtmlRendererParameters{})
}
func TestHrefTargetBlank(t *testing.T) {

View File

@ -17,7 +17,6 @@ package blackfriday
import (
"bytes"
"strings"
)
// Latex is a type that implements the Renderer interface for LaTeX output.
@ -40,17 +39,16 @@ func (options *Latex) GetFlags() int {
}
// render code chunks using verbatim, or listings if we have a language
func (options *Latex) BlockCode(out *bytes.Buffer, text []byte, info string) {
if info == "" {
func (options *Latex) BlockCode(out *bytes.Buffer, text []byte, lang string) {
if lang == "" {
out.WriteString("\n\\begin{verbatim}\n")
} else {
lang := strings.Fields(info)[0]
out.WriteString("\n\\begin{lstlisting}[language=")
out.WriteString(lang)
out.WriteString("]\n")
}
out.Write(text)
if info == "" {
if lang == "" {
out.WriteString("\n\\end{verbatim}\n")
} else {
out.WriteString("\n\\end{lstlisting}\n")

View File

@ -132,7 +132,6 @@ var blockTags = map[string]struct{}{
"article": {},
"aside": {},
"canvas": {},
"details": {},
"figcaption": {},
"figure": {},
"footer": {},
@ -143,7 +142,6 @@ var blockTags = map[string]struct{}{
"output": {},
"progress": {},
"section": {},
"summary": {},
"video": {},
}
@ -161,7 +159,7 @@ var blockTags = map[string]struct{}{
// Currently Html and Latex implementations are provided
type Renderer interface {
// block-level callbacks
BlockCode(out *bytes.Buffer, text []byte, infoString string)
BlockCode(out *bytes.Buffer, text []byte, lang string)
BlockQuote(out *bytes.Buffer, text []byte)
BlockHtml(out *bytes.Buffer, text []byte)
Header(out *bytes.Buffer, text func() bool, level int, id string)
@ -806,17 +804,7 @@ func ispunct(c byte) bool {
// Test if a character is a whitespace character.
func isspace(c byte) bool {
return ishorizontalspace(c) || isverticalspace(c)
}
// Test if a character is a horizontal whitespace character.
func ishorizontalspace(c byte) bool {
return c == ' ' || c == '\t'
}
// Test if a character is a vertical whitespace character.
func isverticalspace(c byte) bool {
return c == '\n' || c == '\r' || c == '\f' || c == '\v'
return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v'
}
// Test if a character is letter.

View File

@ -8,6 +8,4 @@
<p><a href="/url/" title="title has spaces afterward">URL and title</a>.</p>
<p><a href="/url/">URL with backslashes\</a>.</p>
<p>[Empty]().</p>

View File

@ -8,6 +8,5 @@ Just a [URL](/url/).
[URL and title](/url/ "title has spaces afterward" ).
[URL with backslashes\\](/url/).
[Empty]().