diff --git a/ast/node.go b/ast/node.go index f3fda90..5c5f032 100644 --- a/ast/node.go +++ b/ast/node.go @@ -37,9 +37,12 @@ const ( type Node interface { AsTreeNode() *TreeNode GetParent() Node + SetParent(newParent Node) GetChildren() []Node + SetChildren(newChildren []Node) FirstChild() Node LastChild() Node + SetContent(c []byte) } // TreeNode is a common part of all nodes, used to represent tree and contain @@ -64,11 +67,75 @@ func (n *TreeNode) GetParent() Node { return n.Parent } +// SetParent sets the parent +func (n *TreeNode) SetParent(newParent Node) { + n.Parent = newParent +} + // GetChildren returns children func (n *TreeNode) GetChildren() []Node { return n.Children } +// SetChildren sets children +func (n *TreeNode) SetChildren(newChildren []Node) { + n.Children = newChildren +} + +// SetContent sets content of the node +func (n *TreeNode) SetContent(c []byte) { + n.Content = c +} + +// LeafNode is a common part of all nodes, used to represent tree and contain +// data that all nodes have in common +type LeafNode struct { + Parent Node + + Literal []byte // Text contents of the leaf nodes + Content []byte // Markdown content of the block nodes +} + +// AsTreeNode returns itself as *TreeNode +func (n *LeafNode) AsTreeNode() *TreeNode { + return nil +} + +// GetParent returns parent +func (n *LeafNode) GetParent() Node { + return n.Parent +} + +// SetParent sets the parent +func (n *LeafNode) SetParent(newParent Node) { + n.Parent = newParent +} + +// GetChildren returns children +func (n *LeafNode) GetChildren() []Node { + return nil +} + +// SetChildren sets children +func (n *LeafNode) SetChildren(newChildren []Node) { + // do nothing, LeafNode has no children +} + +// FirstChild returns children +func (n *LeafNode) FirstChild() Node { + return nil +} + +// LastChild returns children +func (n *LeafNode) LastChild() Node { + return nil +} + +// SetContent sets content of the node +func (n *LeafNode) SetContent(c []byte) { + n.Content = c +} + // PanicIfTreeNode will panic if node is *TreeNode func PanicIfTreeNode(node Node) { if _, ok := node.(*TreeNode); ok { @@ -135,7 +202,7 @@ type Heading struct { // HorizontalRule represents data for horizontal rule node type HorizontalRule struct { - TreeNode + LeafNode } // Emph represents data for emp node @@ -173,17 +240,17 @@ type Image struct { // Text represents data for text node type Text struct { - TreeNode + LeafNode } // HTMLBlock represents data for html node type HTMLBlock struct { - TreeNode + LeafNode } // CodeBlock contains fields relevant to a CodeBlock node type. type CodeBlock struct { - TreeNode + LeafNode IsFenced bool // Specifies whether it's a fenced code block or an indented one Info []byte // This holds the info string @@ -195,22 +262,22 @@ type CodeBlock struct { // Softbreak represents data for softbreak node // Note: not used currently type Softbreak struct { - TreeNode + LeafNode } // Hardbreak represents data for hard break node type Hardbreak struct { - TreeNode + LeafNode } // Code represents data for code node type Code struct { - TreeNode + LeafNode } // HTMLSpan represents data for html span node type HTMLSpan struct { - TreeNode + LeafNode } // Table represents data for table node @@ -265,27 +332,29 @@ func removeNodeFromArray(a []Node, node Node) []Node { // RemoveFromTree removes this node from tree func RemoveFromTree(n Node) { - nt := n.AsTreeNode() - if nt.Parent == nil { + if n.GetParent() == nil { return } // important: don't clear n.Children if n has no parent // we're called from AppendChild and that might happen on a node // that accumulated Children but hasn't been inserted into the tree - p := nt.Parent.AsTreeNode() - p.Children = removeNodeFromArray(p.Children, n) - nt.Parent = nil - nt.Children = nil + p := n.GetParent() + newChildren := removeNodeFromArray(p.GetChildren(), n) + p.SetChildren(newChildren) + n.SetChildren(nil) } // AppendChild adds a node 'child' as a child of 'n'. // It panics if either node is nil. func AppendChild(n Node, child Node) { - childTN := child.AsTreeNode() RemoveFromTree(child) - childTN.Parent = n - nTN := n.AsTreeNode() - nTN.Children = append(nTN.Children, child) + child.SetParent(n) + newChildren := append(n.GetChildren(), child) + n.SetChildren(newChildren) +} + +func isContainer(n Node) bool { + return n.AsTreeNode() != nil } // LastChild returns last child of this node @@ -336,16 +405,6 @@ func PrevNode(n Node) Node { return nil } -func isContainer(n Node) bool { - // list of non-containers is smaller so we check against that for speed - switch n.(type) { - case *HorizontalRule, *Text, *HTMLBlock, *CodeBlock, *Softbreak, *Hardbreak, *Code, *HTMLSpan: - return false - default: - return true - } -} - // WalkStatus allows NodeVisitor to have some control over the tree traversal. // It is returned from NodeVisitor and different values allow Node.Walk to // decide which node to go to next. diff --git a/parser/block.go b/parser/block.go index 43921c4..29b4bb4 100644 --- a/parser/block.go +++ b/parser/block.go @@ -253,7 +253,7 @@ func (p *Parser) block(data []byte) { func (p *Parser) addBlock(n ast.Node, content []byte) ast.Node { p.closeUnmatchedBlocks() container := p.addChild(n, 0) - container.AsTreeNode().Content = content + container.SetContent(content) return container } @@ -455,14 +455,15 @@ func (p *Parser) html(data []byte, doRender bool) int { if doRender { // trim newlines end := backChar(data, i, '\n') - finalizeHTMLBlock(p.addBlock(&ast.HTMLBlock{}, data[:end])) + htmlBlock := &ast.HTMLBlock{} + p.addBlock(htmlBlock, data[:end]) + finalizeHTMLBlock(htmlBlock) } return i } -func finalizeHTMLBlock(blockNode ast.Node) { - block := blockNode.AsTreeNode() +func finalizeHTMLBlock(block *ast.HTMLBlock) { block.Literal = block.Content block.Content = nil } @@ -476,8 +477,9 @@ func (p *Parser) htmlComment(data []byte, doRender bool) int { if doRender { // trim trailing newlines end := backChar(data, size, '\n') - block := p.addBlock(&ast.HTMLBlock{}, data[:end]) - finalizeHTMLBlock(block) + htmlBLock := &ast.HTMLBlock{} + p.addBlock(htmlBLock, data[:end]) + finalizeHTMLBlock(htmlBLock) } return size } @@ -507,7 +509,9 @@ func (p *Parser) htmlHr(data []byte, doRender bool) int { if doRender { // trim newlines end := backChar(data, size, '\n') - finalizeHTMLBlock(p.addBlock(&ast.HTMLBlock{}, data[:end])) + htmlBlock := &ast.HTMLBlock{} + p.addBlock(htmlBlock, data[:end]) + finalizeHTMLBlock(htmlBlock) } return size } @@ -742,11 +746,11 @@ func (p *Parser) fencedCodeBlock(data []byte, doRender bool) int { } if doRender { - d := &ast.CodeBlock{ + codeBlock := &ast.CodeBlock{ IsFenced: true, } - block := p.addBlock(d, work.Bytes()) // TODO: get rid of temp buffer - finalizeCodeBlock(block, d) + p.addBlock(codeBlock, work.Bytes()) // TODO: get rid of temp buffer + finalizeCodeBlock(codeBlock) } return beg @@ -766,18 +770,18 @@ func unescapeString(str []byte) []byte { return str } -func finalizeCodeBlock(blockNode ast.Node, code *ast.CodeBlock) { - block := blockNode.AsTreeNode() +func finalizeCodeBlock(code *ast.CodeBlock) { + c := code.Content if code.IsFenced { - newlinePos := bytes.IndexByte(block.Content, '\n') - firstLine := block.Content[:newlinePos] - rest := block.Content[newlinePos+1:] + newlinePos := bytes.IndexByte(c, '\n') + firstLine := c[:newlinePos] + rest := c[newlinePos+1:] code.Info = unescapeString(bytes.Trim(firstLine, "\n")) - block.Literal = rest + code.Literal = rest } else { - block.Literal = block.Content + code.Literal = c } - block.Content = nil + code.Content = nil } func (p *Parser) table(data []byte) int { @@ -1087,11 +1091,11 @@ func (p *Parser) code(data []byte) int { work.WriteByte('\n') - d := &ast.CodeBlock{ + codeBlock := &ast.CodeBlock{ IsFenced: false, } - block := p.addBlock(d, work.Bytes()) // TODO: get rid of temp buffer - finalizeCodeBlock(block, d) + p.addBlock(codeBlock, work.Bytes()) // TODO: get rid of temp buffer + finalizeCodeBlock(codeBlock) return i } diff --git a/parser/inline.go b/parser/inline.go index e32ddde..dbd4c9d 100644 --- a/parser/inline.go +++ b/parser/inline.go @@ -1181,7 +1181,7 @@ func helperTripleEmphasis(p *Parser, data []byte, offset int, c byte) (int, ast. } func newTextNode(d []byte) *ast.Text { - return &ast.Text{ast.TreeNode{Literal: d}} + return &ast.Text{ast.LeafNode{Literal: d}} } func normalizeURI(s []byte) []byte {