diff --git a/ast/ast.go b/ast/ast.go index c5edfe5..4f1d885 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -98,6 +98,9 @@ type Node interface { // RemoveChildren removes all children from this node. RemoveChildren(self Node) + // SortChildren sorts childrens by comparator. + SortChildren(comparator func(n1, n2 Node) int) + // ReplaceChild replace a node v1 with a node insertee. // If v1 is not children of this node, ReplaceChild append a insetee to the // tail of the children. @@ -243,6 +246,39 @@ func (n *BaseNode) RemoveChildren(self Node) { n.childCount = 0 } +// SortChildren implements Node.SortChildren +func (n *BaseNode) SortChildren(comparator func(n1, n2 Node) int) { + var sorted Node + current := n.firstChild + for current != nil { + next := current.NextSibling() + if sorted == nil || comparator(sorted, current) >= 0 { + current.SetNextSibling(sorted) + if sorted != nil { + sorted.SetPreviousSibling(current) + } + sorted = current + sorted.SetPreviousSibling(nil) + } else { + c := sorted + for c.NextSibling() != nil && comparator(c.NextSibling(), current) < 0 { + c = c.NextSibling() + } + current.SetNextSibling(c.NextSibling()) + current.SetPreviousSibling(c) + if c.NextSibling() != nil { + c.NextSibling().SetPreviousSibling(current) + } + c.SetNextSibling(current) + } + current = next + } + n.firstChild = sorted + for c := n.firstChild; c != nil; c = c.NextSibling() { + n.lastChild = c + } +} + // FirstChild implements Node.FirstChild . func (n *BaseNode) FirstChild() Node { return n.firstChild diff --git a/extension/_test/footnote.txt b/extension/_test/footnote.txt index 4a04a74..34cb5c0 100644 --- a/extension/_test/footnote.txt +++ b/extension/_test/footnote.txt @@ -22,11 +22,28 @@ That's some text with a footnote.[^1] //- - - - - - - - -// [^000]:0 [^]: //- - - - - - - - -// +//= = = = = = = = = = = = = = = = = = = = = = = =// + +4 +//- - - - - - - - -// +This[^3] is[^1] text with footnotes[^2]. + +[^1]: Footnote one +[^2]: Footnote two +[^3]: Footnote three +//- - - - - - - - -// +

This1 is2 text with footnotes3.


  1. -

    0 [^]: ↩︎

    +

    Footnote three ↩︎

    +
  2. +
  3. +

    Footnote one ↩︎

    +
  4. +
  5. +

    Footnote two ↩︎

diff --git a/extension/ast/footnote.go b/extension/ast/footnote.go index 291e6b3..835f847 100644 --- a/extension/ast/footnote.go +++ b/extension/ast/footnote.go @@ -73,7 +73,10 @@ type Footnote struct { // Dump implements Node.Dump. func (n *Footnote) Dump(source []byte, level int) { - gast.DumpHelper(n, source, level, nil, nil) + m := map[string]string{} + m["Index"] = fmt.Sprintf("%v", n.Index) + m["Ref"] = fmt.Sprintf("%s", n.Ref) + gast.DumpHelper(n, source, level, m, nil) } // KindFootnote is a NodeKind of the Footnote node. @@ -87,7 +90,8 @@ func (n *Footnote) Kind() gast.NodeKind { // NewFootnote returns a new Footnote node. func NewFootnote(ref []byte) *Footnote { return &Footnote{ - Ref: ref, + Ref: ref, + Index: -1, } } @@ -95,11 +99,14 @@ func NewFootnote(ref []byte) *Footnote { // (PHP Markdown Extra) text. type FootnoteList struct { gast.BaseBlock + Count int } // Dump implements Node.Dump. func (n *FootnoteList) Dump(source []byte, level int) { - gast.DumpHelper(n, source, level, nil, nil) + m := map[string]string{} + m["Count"] = fmt.Sprintf("%v", n.Count) + gast.DumpHelper(n, source, level, m, nil) } // KindFootnoteList is a NodeKind of the FootnoteList node. @@ -112,5 +119,7 @@ func (n *FootnoteList) Kind() gast.NodeKind { // NewFootnoteList returns a new FootnoteList node. func NewFootnoteList() *FootnoteList { - return &FootnoteList{} + return &FootnoteList{ + Count: 0, + } } diff --git a/extension/footnote.go b/extension/footnote.go index 2fbcbb5..31efddc 100644 --- a/extension/footnote.go +++ b/extension/footnote.go @@ -91,9 +91,6 @@ func (b *footnoteBlockParser) Close(node gast.Node, reader text.Reader, pc parse node.Parent().InsertBefore(node.Parent(), node, list) } node.Parent().RemoveChild(node.Parent(), node) - n := node.(*ast.Footnote) - index := list.ChildCount() + 1 - n.Index = index list.AppendChild(list, node) } @@ -150,6 +147,10 @@ func (s *footnoteParser) Parse(parent gast.Node, block text.Reader, pc parser.Co for def := list.FirstChild(); def != nil; def = def.NextSibling() { d := def.(*ast.Footnote) if bytes.Equal(d.Ref, value) { + if d.Index < 0 { + list.Count += 1 + d.Index = list.Count + } index = d.Index break } @@ -180,14 +181,29 @@ func (a *footnoteASTTransformer) Transform(node *gast.Document, reader text.Read return } pc.Set(footnoteListKey, nil) - - for footnote := list.FirstChild(); footnote != nil; footnote = footnote.NextSibling() { + for footnote := list.FirstChild(); footnote != nil; { var container gast.Node = footnote + next := footnote.NextSibling() if fc := container.LastChild(); fc != nil && gast.IsParagraph(fc) { container = fc } index := footnote.(*ast.Footnote).Index - container.AppendChild(container, ast.NewFootnoteBackLink(index)) + if index < 0 { + list.RemoveChild(list, footnote) + } else { + container.AppendChild(container, ast.NewFootnoteBackLink(index)) + } + footnote = next + } + list.SortChildren(func(n1, n2 gast.Node) int { + if n1.(*ast.Footnote).Index < n2.(*ast.Footnote).Index { + return -1 + } + return 1 + }) + if list.Count <= 0 { + list.Parent().RemoveChild(list.Parent(), list) + return } node.AppendChild(node, list) diff --git a/parser/attribute.go b/parser/attribute.go index f417d2b..ea8c064 100644 --- a/parser/attribute.go +++ b/parser/attribute.go @@ -99,6 +99,9 @@ func parseAttribute(reader text.Reader) (Attribute, bool) { return Attribute{Name: name, Value: line[0:i]}, true } line, _ := reader.PeekLine() + if len(line) == 0 { + return Attribute{}, false + } c = line[0] if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == ':') {