From f7fe9c2b4d106d5fefcaa42fda4935c6a645668c Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Thu, 2 Aug 2018 21:49:41 +0100 Subject: [PATCH 1/5] ref stuff Signed-off-by: Miek Gieben --- parser/parser.go | 4 ++++ parser/ref.go | 40 ++++++++++++++++++++++++++++++++++++++++ parser/ref_test.go | 1 + 3 files changed, 45 insertions(+) create mode 100644 parser/ref.go create mode 100644 parser/ref_test.go diff --git a/parser/parser.go b/parser/parser.go index cc58a0b..6097363 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -42,6 +42,7 @@ const ( MmarkAsides // Mmark asides paragraphs. See https://mmark.nl/syntax MmarkMatters // Mmark document divisions MmarkCaptions // Allow Mmark captions under code and quote blocks be parsed. + MmarkReferenceIndex // Mmark references and indeces CommonExtensions Extensions = NoIntraEmphasis | Tables | FencedCode | Autolink | Strikethrough | SpaceHeadings | HeadingIDs | @@ -146,6 +147,9 @@ func NewWithExtensions(extension Extensions) *Parser { p.inlineCallback['\\'] = escape p.inlineCallback['&'] = entity p.inlineCallback['!'] = maybeImage + if p.extensions & MmarkReferenceIndex { + p.inlineCallback['('] = maybeShortRefOrIndex + } p.inlineCallback['^'] = maybeInlineFootnote if p.extensions&Autolink != 0 { p.inlineCallback['h'] = maybeAutoLink diff --git a/parser/ref.go b/parser/ref.go new file mode 100644 index 0000000..ae94854 --- /dev/null +++ b/parser/ref.go @@ -0,0 +1,40 @@ +package parser + +import "github.com/gomarkdown/markdown/ast" + +// parse '(#r)', where r does not contain spaces and is an existing label. Or. +// (!item) (!item, subitem), for an index, (!!item) signals primary. +func maybeShortRefOrIndex(p *Parser, date []byte, offset int) (int, ast.Node) { + if len(data[offset:]) < 4 { + return 0 + } + // short ref first + data = data[offset:] + i := 1 + if data[i] != '#' { + return 0 + } + i++ + for i < len(data) && data[i] != ')' { + if isAlnum(data[i]) { + i++ + continue + } + if data[i] == '_' || data[i] == '-' || data[i] == ':' { + i++ + continue + } + i = 0 + break + } + // not found, or not valid + if i == 0 { + return 0, nil + } + + id := data[2:i] + lr, ok := p.getRef(string(id)) + if !ok { + return 0, nil + } +} diff --git a/parser/ref_test.go b/parser/ref_test.go new file mode 100644 index 0000000..0bfe2c2 --- /dev/null +++ b/parser/ref_test.go @@ -0,0 +1 @@ +package parser From bf6ad507936e6df92bd6ae0725f9ceda9506ffdc Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Thu, 2 Aug 2018 22:45:41 +0100 Subject: [PATCH 2/5] more Signed-off-by: Miek Gieben --- ast/node.go | 7 +++++++ html/renderer.go | 3 +++ parser/parser.go | 5 +++-- parser/ref.go | 37 ++++++++++++++++++++----------------- 4 files changed, 33 insertions(+), 19 deletions(-) diff --git a/ast/node.go b/ast/node.go index 78905b2..6353389 100644 --- a/ast/node.go +++ b/ast/node.go @@ -233,6 +233,13 @@ type Link struct { Footnote Node // If it's a footnote, this is a direct link to the footnote Node. Otherwise nil. } +// InternalLink is a link node that links to a local document anchor +type InternalLink struct { + Container + + Destination []byte // Destination is what goes into a href +} + // Image represents markdown image node type Image struct { Container diff --git a/html/renderer.go b/html/renderer.go index 9abbeed..3631416 100644 --- a/html/renderer.go +++ b/html/renderer.go @@ -879,6 +879,9 @@ func (r *Renderer) RenderNode(w io.Writer, node ast.Node, entering bool) ast.Wal r.outOneOfCr(w, entering, tag, "") case *ast.Link: r.link(w, node, entering) + case *ast.InternalLink: + link := &ast.Link{Destination: node.Destination} + r.link(w, link, entering) case *ast.Image: if r.opts.Flags&SkipImages != 0 { return ast.SkipChildren diff --git a/parser/parser.go b/parser/parser.go index 6097363..2573e24 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -42,7 +42,7 @@ const ( MmarkAsides // Mmark asides paragraphs. See https://mmark.nl/syntax MmarkMatters // Mmark document divisions MmarkCaptions // Allow Mmark captions under code and quote blocks be parsed. - MmarkReferenceIndex // Mmark references and indeces + MmarkReferenceIndex // Mmark cross references and indices CommonExtensions Extensions = NoIntraEmphasis | Tables | FencedCode | Autolink | Strikethrough | SpaceHeadings | HeadingIDs | @@ -147,7 +147,7 @@ func NewWithExtensions(extension Extensions) *Parser { p.inlineCallback['\\'] = escape p.inlineCallback['&'] = entity p.inlineCallback['!'] = maybeImage - if p.extensions & MmarkReferenceIndex { + if p.extensions&MmarkReferenceIndex != 0 { p.inlineCallback['('] = maybeShortRefOrIndex } p.inlineCallback['^'] = maybeInlineFootnote @@ -166,6 +166,7 @@ func NewWithExtensions(extension Extensions) *Parser { } func (p *Parser) getRef(refid string) (ref *reference, found bool) { + fmt.Printf("%+v\n", p.refs) if p.ReferenceOverride != nil { r, overridden := p.ReferenceOverride(refid) if overridden { diff --git a/parser/ref.go b/parser/ref.go index ae94854..def8d48 100644 --- a/parser/ref.go +++ b/parser/ref.go @@ -4,37 +4,40 @@ import "github.com/gomarkdown/markdown/ast" // parse '(#r)', where r does not contain spaces and is an existing label. Or. // (!item) (!item, subitem), for an index, (!!item) signals primary. -func maybeShortRefOrIndex(p *Parser, date []byte, offset int) (int, ast.Node) { +func maybeShortRefOrIndex(p *Parser, data []byte, offset int) (int, ast.Node) { if len(data[offset:]) < 4 { - return 0 + return 0, nil } // short ref first data = data[offset:] i := 1 if data[i] != '#' { - return 0 + return 0, nil } i++ for i < len(data) && data[i] != ')' { - if isAlnum(data[i]) { - i++ - continue + c := data[i] + switch { + case c == ')': + break + case !isAlnum(c): + if c == '_' || c == '-' || c == ':' { + i++ + continue + } + i = 0 + break } - if data[i] == '_' || data[i] == '-' || data[i] == ':' { - i++ - continue - } - i = 0 - break + i++ } - // not found, or not valid + // not found or not valid if i == 0 { return 0, nil } id := data[2:i] - lr, ok := p.getRef(string(id)) - if !ok { - return 0, nil - } + node := &ast.InternalLink{} + node.Destination = id + + return i, node } From fca7a9e0a6d83122bea6eb93aa49c62ad29b3961 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Fri, 3 Aug 2018 08:16:21 +0100 Subject: [PATCH 3/5] Mmark: add cross reference syntax Signed-off-by: Miek Gieben --- ast/node.go | 6 +++--- parser/parser.go | 7 +++---- parser/ref.go | 16 ++++++++++------ parser/ref_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 13 deletions(-) diff --git a/ast/node.go b/ast/node.go index 6353389..fff74b0 100644 --- a/ast/node.go +++ b/ast/node.go @@ -233,11 +233,11 @@ type Link struct { Footnote Node // If it's a footnote, this is a direct link to the footnote Node. Otherwise nil. } -// InternalLink is a link node that links to a local document anchor -type InternalLink struct { +// CrossReference is a reference node. +type CrossReference struct { Container - Destination []byte // Destination is what goes into a href + Destination []byte // Destination is where the reference points to } // Image represents markdown image node diff --git a/parser/parser.go b/parser/parser.go index 2573e24..0d2fd45 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -38,11 +38,11 @@ const ( MathJax // Parse MathJax 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 - MmarkMatters // Mmark document divisions MmarkCaptions // Allow Mmark captions under code and quote blocks be parsed. - MmarkReferenceIndex // Mmark cross references and indices + MmarkMatters // Mmark document divisions, see https://mmark.nl/syntax#document-matters + MmarkReferenceIndex // Mmark cross references and indices. See https://mmark.nl/syntax#cross-references + MmarkSpecialHeading // Allow Mmark special headings to be parsed. See mmark.nl/syntax CommonExtensions Extensions = NoIntraEmphasis | Tables | FencedCode | Autolink | Strikethrough | SpaceHeadings | HeadingIDs | @@ -166,7 +166,6 @@ func NewWithExtensions(extension Extensions) *Parser { } func (p *Parser) getRef(refid string) (ref *reference, found bool) { - fmt.Printf("%+v\n", p.refs) if p.ReferenceOverride != nil { r, overridden := p.ReferenceOverride(refid) if overridden { diff --git a/parser/ref.go b/parser/ref.go index def8d48..9c752f3 100644 --- a/parser/ref.go +++ b/parser/ref.go @@ -5,6 +5,9 @@ import "github.com/gomarkdown/markdown/ast" // parse '(#r)', where r does not contain spaces and is an existing label. Or. // (!item) (!item, subitem), for an index, (!!item) signals primary. func maybeShortRefOrIndex(p *Parser, data []byte, offset int) (int, ast.Node) { + if p.extensions&MmarkReferenceIndex == 0 { + return 0, nil + } if len(data[offset:]) < 4 { return 0, nil } @@ -15,28 +18,29 @@ func maybeShortRefOrIndex(p *Parser, data []byte, offset int) (int, ast.Node) { return 0, nil } i++ - for i < len(data) && data[i] != ')' { +Loop: + for i < len(data) { c := data[i] switch { case c == ')': - break + break Loop case !isAlnum(c): if c == '_' || c == '-' || c == ':' { i++ continue } i = 0 - break + break Loop } i++ } - // not found or not valid - if i == 0 { + // end not found or no valid syntax + if i == 0 || data[i-1] != ')' { return 0, nil } id := data[2:i] - node := &ast.InternalLink{} + node := &ast.CrossReference{} node.Destination = id return i, node diff --git a/parser/ref_test.go b/parser/ref_test.go index 0bfe2c2..980476a 100644 --- a/parser/ref_test.go +++ b/parser/ref_test.go @@ -1 +1,39 @@ package parser + +import ( + "testing" + + "github.com/gomarkdown/markdown/ast" +) + +func TestCrossReference(t *testing.T) { + p := New() + p.extensions |= MmarkReferenceIndex + + tests := []struct { + data []byte + r *ast.CrossReference + fail bool + }{ + // ok + { + data: []byte("(#yes)"), + r: &ast.CrossReference{Destination: []byte("yes")}, + }, + // ok + { + data: []byte("(#y:es)"), + r: &ast.CrossReference{Destination: []byte("y:es")}, + }, + // fails + {data: []byte("(#y es)"), r: nil, fail: true}, + {data: []byte("(#yes"), r: nil, fail: true}, + } + + for i, test := range tests { + _, n := maybeShortRefOrIndex(p, test.data, 0) + if test.fail && n != nil { + t.Errorf("test %d, should have failed to parse %s", i, test.data) + } + } +} From 4128e40d11bd4dda1fa39a1fdc017001d70ac765 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Fri, 3 Aug 2018 08:20:36 +0100 Subject: [PATCH 4/5] correct type Signed-off-by: Miek Gieben --- html/renderer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/html/renderer.go b/html/renderer.go index 3631416..9968278 100644 --- a/html/renderer.go +++ b/html/renderer.go @@ -879,7 +879,7 @@ func (r *Renderer) RenderNode(w io.Writer, node ast.Node, entering bool) ast.Wal r.outOneOfCr(w, entering, tag, "") case *ast.Link: r.link(w, node, entering) - case *ast.InternalLink: + case *ast.CrossReference: link := &ast.Link{Destination: node.Destination} r.link(w, link, entering) case *ast.Image: From a4ae7a10b0ff02c214b2132ce1153a9b28e61f79 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Fri, 3 Aug 2018 08:21:32 +0100 Subject: [PATCH 5/5] not needed Signed-off-by: Miek Gieben --- parser/ref.go | 3 --- parser/ref_test.go | 1 - 2 files changed, 4 deletions(-) diff --git a/parser/ref.go b/parser/ref.go index 9c752f3..68e2c91 100644 --- a/parser/ref.go +++ b/parser/ref.go @@ -5,9 +5,6 @@ import "github.com/gomarkdown/markdown/ast" // parse '(#r)', where r does not contain spaces and is an existing label. Or. // (!item) (!item, subitem), for an index, (!!item) signals primary. func maybeShortRefOrIndex(p *Parser, data []byte, offset int) (int, ast.Node) { - if p.extensions&MmarkReferenceIndex == 0 { - return 0, nil - } if len(data[offset:]) < 4 { return 0, nil } diff --git a/parser/ref_test.go b/parser/ref_test.go index 980476a..0177fb3 100644 --- a/parser/ref_test.go +++ b/parser/ref_test.go @@ -8,7 +8,6 @@ import ( func TestCrossReference(t *testing.T) { p := New() - p.extensions |= MmarkReferenceIndex tests := []struct { data []byte