WIP: Mmark: implement asides

This implements asides, blockquote like paragraph prexifed with `A>`
instead of just a '>'

Adds the extension MmarkAsides

Signed-off-by: Miek Gieben <miek@miek.nl>
This commit is contained in:
Miek Gieben 2018-07-30 17:19:39 +01:00
parent 059798c247
commit f1700b7c88
6 changed files with 101 additions and 5 deletions

View File

@ -129,6 +129,11 @@ type BlockQuote struct {
Container
}
// Aside represents an markdown aside node.
type Aside struct {
Container
}
// List represents markdown list node
type List struct {
Container

View File

@ -536,7 +536,7 @@ func (r *Renderer) paragraphEnter(w io.Writer, para *ast.Paragraph) {
prev := ast.GetPrevNode(para)
if prev != nil {
switch prev.(type) {
case *ast.HTMLBlock, *ast.List, *ast.Paragraph, *ast.Heading, *ast.CodeBlock, *ast.BlockQuote, *ast.HorizontalRule:
case *ast.HTMLBlock, *ast.List, *ast.Paragraph, *ast.Heading, *ast.CodeBlock, *ast.BlockQuote, *ast.Aside, *ast.HorizontalRule:
r.cr(w)
}
}
@ -546,6 +546,10 @@ func (r *Renderer) paragraphEnter(w io.Writer, para *ast.Paragraph) {
if isParentBlockQuote {
r.cr(w)
}
_, isParentAside := para.Parent.(*ast.Aside)
if isParentAside {
r.cr(w)
}
}
tag := tagWithAttributes("<p", blockAttrs(para))
@ -689,7 +693,7 @@ func (r *Renderer) listExit(w io.Writer, list *ast.List) {
if ast.GetNextNode(list) != nil {
r.cr(w)
}
case *ast.Document, *ast.BlockQuote:
case *ast.Document, *ast.BlockQuote, *ast.Aside:
r.cr(w)
}
@ -831,6 +835,9 @@ func (r *Renderer) RenderNode(w io.Writer, node ast.Node, entering bool) ast.Wal
case *ast.BlockQuote:
tag := tagWithAttributes("<blockquote", blockAttrs(node))
r.outOneOfCr(w, entering, tag, "</blockquote>")
case *ast.Aside:
tag := tagWithAttributes("<aside", blockAttrs(node))
r.outOneOfCr(w, entering, tag, "</aside>")
case *ast.Link:
r.link(w, node, entering)
case *ast.Image:

View File

@ -21,14 +21,15 @@ func TestMmark(t *testing.T) {
if len(testdata)%2 != 0 {
t.Fatalf("odd test tuples: %d", len(testdata))
}
ext := parser.CommonExtensions | parser.Attributes | parser.OrderedListStart | parser.MmarkSpecialHeading
for i := 0; i < len(testdata); i += 2 {
ext := parser.CommonExtensions | parser.Attributes | parser.OrderedListStart | parser.MmarkSpecialHeading
parser := parser.NewWithExtensions(ext)
p := parser.NewWithExtensions(ext)
input := testdata[i]
want := testdata[i+1]
got := ToHTML([]byte(input), parser, nil)
got := ToHTML([]byte(input), p, nil)
if bytes.Compare(got, want) != 0 {
t.Errorf("want %s, got %s, for input %q", want, got, input)

71
parser/aside.go Normal file
View File

@ -0,0 +1,71 @@
package parser
import (
"bytes"
"github.com/gomarkdown/markdown/ast"
)
// returns aisde prefix length
func (p *Parser) asidePrefix(data []byte) int {
i := 0
n := len(data)
for i < 3 && i < n && data[i] == ' ' {
i++
}
if i+1 < n && data[i] == 'A' && data[i+1] == '>' {
if i+2 < n && data[i+2] == ' ' {
return i + 3
}
return i + 2
}
return 0
}
// aside ends with at least one blank line
// followed by something without a aside prefix
func (p *Parser) terminateAside(data []byte, beg, end int) bool {
if p.isEmpty(data[beg:]) <= 0 {
return false
}
if end >= len(data) {
return true
}
return p.asidePrefix(data[end:]) == 0 && p.isEmpty(data[end:]) == 0
}
// parse a blockquote fragment
func (p *Parser) aside(data []byte) int {
block := p.addBlock(&ast.Aside{})
var raw bytes.Buffer
beg, end := 0, 0
for beg < len(data) {
end = beg
// Step over whole lines, collecting them. While doing that, check for
// fenced code and if one's found, incorporate it altogether,
// irregardless of any contents inside it
for end < len(data) && data[end] != '\n' {
if p.extensions&FencedCode != 0 {
if i := p.fencedCodeBlock(data[end:], false); i > 0 {
// -1 to compensate for the extra end++ after the loop:
end += i - 1
break
}
}
end++
}
end = skipCharN(data, end, '\n', 1)
if pre := p.asidePrefix(data[beg:]); pre > 0 {
// skip the prefix
beg += pre
} else if p.terminateAside(data, beg, end) {
break
}
// this line is part of the aside
raw.Write(data[beg:end])
beg = end
}
p.block(raw.Bytes())
p.finalize(block)
return end
}

View File

@ -228,6 +228,17 @@ func (p *Parser) block(data []byte) {
continue
}
// aside:
//
// A> The proof is too large to fit
// A> in the margin.
if p.extensions|MmarkAsides != 0 {
if p.asidePrefix(data) > 0 {
data = data[p.aside(data):]
continue
}
}
// table:
//
// Name | Age | Phone

View File

@ -39,6 +39,7 @@ const (
OrderedListStart // Keep track of the first number used when starting an ordered list.
Attributes // Block Attributes
MmarkSpecialHeading // Allow Mmark special headings to be parsed. See mmark.nl/syntax.
MmarkAsides // Mmark asides paragraphs. See https://mmark.nl/syntax.
CommonExtensions Extensions = NoIntraEmphasis | Tables | FencedCode |
Autolink | Strikethrough | SpaceHeadings | HeadingIDs |