Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
485 lines
12 KiB
485 lines
12 KiB
// Package ast defines AST nodes that represent markdown elements. |
|
package ast |
|
|
|
import ( |
|
"bytes" |
|
"fmt" |
|
"strings" |
|
|
|
textm "github.com/yuin/goldmark/text" |
|
"github.com/yuin/goldmark/util" |
|
) |
|
|
|
// A NodeType indicates what type a node belongs to. |
|
type NodeType int |
|
|
|
const ( |
|
// TypeBlock indicates that a node is kind of block nodes. |
|
TypeBlock NodeType = iota + 1 |
|
// TypeInline indicates that a node is kind of inline nodes. |
|
TypeInline |
|
// TypeDocument indicates that a node is kind of document nodes. |
|
TypeDocument |
|
) |
|
|
|
// NodeKind indicates more specific type than NodeType. |
|
type NodeKind int |
|
|
|
func (k NodeKind) String() string { |
|
return kindNames[k] |
|
} |
|
|
|
var kindMax NodeKind |
|
var kindNames = []string{""} |
|
|
|
// NewNodeKind returns a new Kind value. |
|
func NewNodeKind(name string) NodeKind { |
|
kindMax++ |
|
kindNames = append(kindNames, name) |
|
return kindMax |
|
} |
|
|
|
// An Attribute is an attribute of the Node |
|
type Attribute struct { |
|
Name []byte |
|
Value interface{} |
|
} |
|
|
|
var attrNameIDS = []byte("#") |
|
var attrNameID = []byte("id") |
|
var attrNameClassS = []byte(".") |
|
var attrNameClass = []byte("class") |
|
|
|
// A Node interface defines basic AST node functionalities. |
|
type Node interface { |
|
// Type returns a type of this node. |
|
Type() NodeType |
|
|
|
// Kind returns a kind of this node. |
|
Kind() NodeKind |
|
|
|
// NextSibling returns a next sibling node of this node. |
|
NextSibling() Node |
|
|
|
// PreviousSibling returns a previous sibling node of this node. |
|
PreviousSibling() Node |
|
|
|
// Parent returns a parent node of this node. |
|
Parent() Node |
|
|
|
// SetParent sets a parent node to this node. |
|
SetParent(Node) |
|
|
|
// SetPreviousSibling sets a previous sibling node to this node. |
|
SetPreviousSibling(Node) |
|
|
|
// SetNextSibling sets a next sibling node to this node. |
|
SetNextSibling(Node) |
|
|
|
// HasChildren returns true if this node has any children, otherwise false. |
|
HasChildren() bool |
|
|
|
// ChildCount returns a total number of children. |
|
ChildCount() int |
|
|
|
// FirstChild returns a first child of this node. |
|
FirstChild() Node |
|
|
|
// LastChild returns a last child of this node. |
|
LastChild() Node |
|
|
|
// AppendChild append a node child to the tail of the children. |
|
AppendChild(self, child Node) |
|
|
|
// RemoveChild removes a node child from this node. |
|
// If a node child is not children of this node, RemoveChild nothing to do. |
|
RemoveChild(self, child Node) |
|
|
|
// 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. |
|
ReplaceChild(self, v1, insertee Node) |
|
|
|
// InsertBefore inserts a node insertee before a node v1. |
|
// If v1 is not children of this node, InsertBefore append a insetee to the |
|
// tail of the children. |
|
InsertBefore(self, v1, insertee Node) |
|
|
|
// InsertAfterinserts a node insertee after a node v1. |
|
// If v1 is not children of this node, InsertBefore append a insetee to the |
|
// tail of the children. |
|
InsertAfter(self, v1, insertee Node) |
|
|
|
// Dump dumps an AST tree structure to stdout. |
|
// This function completely aimed for debugging. |
|
// level is a indent level. Implementer should indent informations with |
|
// 2 * level spaces. |
|
Dump(source []byte, level int) |
|
|
|
// Text returns text values of this node. |
|
Text(source []byte) []byte |
|
|
|
// HasBlankPreviousLines returns true if the row before this node is blank, |
|
// otherwise false. |
|
// This method is valid only for block nodes. |
|
HasBlankPreviousLines() bool |
|
|
|
// SetBlankPreviousLines sets whether the row before this node is blank. |
|
// This method is valid only for block nodes. |
|
SetBlankPreviousLines(v bool) |
|
|
|
// Lines returns text segments that hold positions in a source. |
|
// This method is valid only for block nodes. |
|
Lines() *textm.Segments |
|
|
|
// SetLines sets text segments that hold positions in a source. |
|
// This method is valid only for block nodes. |
|
SetLines(*textm.Segments) |
|
|
|
// IsRaw returns true if contents should be rendered as 'raw' contents. |
|
IsRaw() bool |
|
|
|
// SetAttribute sets the given value to the attributes. |
|
SetAttribute(name []byte, value interface{}) |
|
|
|
// SetAttributeString sets the given value to the attributes. |
|
SetAttributeString(name string, value interface{}) |
|
|
|
// Attribute returns a (attribute value, true) if an attribute |
|
// associated with the given name is found, otherwise |
|
// (nil, false) |
|
Attribute(name []byte) (interface{}, bool) |
|
|
|
// AttributeString returns a (attribute value, true) if an attribute |
|
// associated with the given name is found, otherwise |
|
// (nil, false) |
|
AttributeString(name string) (interface{}, bool) |
|
|
|
// Attributes returns a list of attributes. |
|
// This may be a nil if there are no attributes. |
|
Attributes() []Attribute |
|
|
|
// RemoveAttributes removes all attributes from this node. |
|
RemoveAttributes() |
|
} |
|
|
|
// A BaseNode struct implements the Node interface. |
|
type BaseNode struct { |
|
firstChild Node |
|
lastChild Node |
|
parent Node |
|
next Node |
|
prev Node |
|
childCount int |
|
attributes []Attribute |
|
} |
|
|
|
func ensureIsolated(v Node) { |
|
if p := v.Parent(); p != nil { |
|
p.RemoveChild(p, v) |
|
} |
|
} |
|
|
|
// HasChildren implements Node.HasChildren . |
|
func (n *BaseNode) HasChildren() bool { |
|
return n.firstChild != nil |
|
} |
|
|
|
// SetPreviousSibling implements Node.SetPreviousSibling . |
|
func (n *BaseNode) SetPreviousSibling(v Node) { |
|
n.prev = v |
|
} |
|
|
|
// SetNextSibling implements Node.SetNextSibling . |
|
func (n *BaseNode) SetNextSibling(v Node) { |
|
n.next = v |
|
} |
|
|
|
// PreviousSibling implements Node.PreviousSibling . |
|
func (n *BaseNode) PreviousSibling() Node { |
|
return n.prev |
|
} |
|
|
|
// NextSibling implements Node.NextSibling . |
|
func (n *BaseNode) NextSibling() Node { |
|
return n.next |
|
} |
|
|
|
// RemoveChild implements Node.RemoveChild . |
|
func (n *BaseNode) RemoveChild(self, v Node) { |
|
if v.Parent() != self { |
|
return |
|
} |
|
n.childCount-- |
|
prev := v.PreviousSibling() |
|
next := v.NextSibling() |
|
if prev != nil { |
|
prev.SetNextSibling(next) |
|
} else { |
|
n.firstChild = next |
|
} |
|
if next != nil { |
|
next.SetPreviousSibling(prev) |
|
} else { |
|
n.lastChild = prev |
|
} |
|
v.SetParent(nil) |
|
v.SetPreviousSibling(nil) |
|
v.SetNextSibling(nil) |
|
} |
|
|
|
// RemoveChildren implements Node.RemoveChildren . |
|
func (n *BaseNode) RemoveChildren(self Node) { |
|
for c := n.firstChild; c != nil; c = c.NextSibling() { |
|
c.SetParent(nil) |
|
c.SetPreviousSibling(nil) |
|
c.SetNextSibling(nil) |
|
} |
|
n.firstChild = nil |
|
n.lastChild = nil |
|
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 |
|
} |
|
|
|
// LastChild implements Node.LastChild . |
|
func (n *BaseNode) LastChild() Node { |
|
return n.lastChild |
|
} |
|
|
|
// ChildCount implements Node.ChildCount . |
|
func (n *BaseNode) ChildCount() int { |
|
return n.childCount |
|
} |
|
|
|
// Parent implements Node.Parent . |
|
func (n *BaseNode) Parent() Node { |
|
return n.parent |
|
} |
|
|
|
// SetParent implements Node.SetParent . |
|
func (n *BaseNode) SetParent(v Node) { |
|
n.parent = v |
|
} |
|
|
|
// AppendChild implements Node.AppendChild . |
|
func (n *BaseNode) AppendChild(self, v Node) { |
|
ensureIsolated(v) |
|
if n.firstChild == nil { |
|
n.firstChild = v |
|
v.SetNextSibling(nil) |
|
v.SetPreviousSibling(nil) |
|
} else { |
|
last := n.lastChild |
|
last.SetNextSibling(v) |
|
v.SetPreviousSibling(last) |
|
} |
|
v.SetParent(self) |
|
n.lastChild = v |
|
n.childCount++ |
|
} |
|
|
|
// ReplaceChild implements Node.ReplaceChild . |
|
func (n *BaseNode) ReplaceChild(self, v1, insertee Node) { |
|
n.InsertBefore(self, v1, insertee) |
|
n.RemoveChild(self, v1) |
|
} |
|
|
|
// InsertAfter implements Node.InsertAfter . |
|
func (n *BaseNode) InsertAfter(self, v1, insertee Node) { |
|
n.InsertBefore(self, v1.NextSibling(), insertee) |
|
} |
|
|
|
// InsertBefore implements Node.InsertBefore . |
|
func (n *BaseNode) InsertBefore(self, v1, insertee Node) { |
|
n.childCount++ |
|
if v1 == nil { |
|
n.AppendChild(self, insertee) |
|
return |
|
} |
|
ensureIsolated(insertee) |
|
if v1.Parent() == self { |
|
c := v1 |
|
prev := c.PreviousSibling() |
|
if prev != nil { |
|
prev.SetNextSibling(insertee) |
|
insertee.SetPreviousSibling(prev) |
|
} else { |
|
n.firstChild = insertee |
|
insertee.SetPreviousSibling(nil) |
|
} |
|
insertee.SetNextSibling(c) |
|
c.SetPreviousSibling(insertee) |
|
insertee.SetParent(self) |
|
} |
|
} |
|
|
|
// Text implements Node.Text . |
|
func (n *BaseNode) Text(source []byte) []byte { |
|
var buf bytes.Buffer |
|
for c := n.firstChild; c != nil; c = c.NextSibling() { |
|
buf.Write(c.Text(source)) |
|
} |
|
return buf.Bytes() |
|
} |
|
|
|
// SetAttribute implements Node.SetAttribute. |
|
func (n *BaseNode) SetAttribute(name []byte, value interface{}) { |
|
if n.attributes == nil { |
|
n.attributes = make([]Attribute, 0, 10) |
|
} else { |
|
for i, a := range n.attributes { |
|
if bytes.Equal(a.Name, name) { |
|
n.attributes[i].Name = name |
|
n.attributes[i].Value = value |
|
return |
|
} |
|
} |
|
} |
|
n.attributes = append(n.attributes, Attribute{name, value}) |
|
} |
|
|
|
// SetAttributeString implements Node.SetAttributeString |
|
func (n *BaseNode) SetAttributeString(name string, value interface{}) { |
|
n.SetAttribute(util.StringToReadOnlyBytes(name), value) |
|
} |
|
|
|
// Attribute implements Node.Attribute. |
|
func (n *BaseNode) Attribute(name []byte) (interface{}, bool) { |
|
if n.attributes == nil { |
|
return nil, false |
|
} |
|
for i, a := range n.attributes { |
|
if bytes.Equal(a.Name, name) { |
|
return n.attributes[i].Value, true |
|
} |
|
} |
|
return nil, false |
|
} |
|
|
|
// AttributeString implements Node.AttributeString. |
|
func (n *BaseNode) AttributeString(s string) (interface{}, bool) { |
|
return n.Attribute(util.StringToReadOnlyBytes(s)) |
|
} |
|
|
|
// Attributes implements Node.Attributes |
|
func (n *BaseNode) Attributes() []Attribute { |
|
return n.attributes |
|
} |
|
|
|
// RemoveAttributes implements Node.RemoveAttributes |
|
func (n *BaseNode) RemoveAttributes() { |
|
n.attributes = nil |
|
} |
|
|
|
// DumpHelper is a helper function to implement Node.Dump. |
|
// kv is pairs of an attribute name and an attribute value. |
|
// cb is a function called after wrote a name and attributes. |
|
func DumpHelper(v Node, source []byte, level int, kv map[string]string, cb func(int)) { |
|
name := v.Kind().String() |
|
indent := strings.Repeat(" ", level) |
|
fmt.Printf("%s%s {\n", indent, name) |
|
indent2 := strings.Repeat(" ", level+1) |
|
if v.Type() == TypeBlock { |
|
fmt.Printf("%sRawText: \"", indent2) |
|
for i := 0; i < v.Lines().Len(); i++ { |
|
line := v.Lines().At(i) |
|
fmt.Printf("%s", line.Value(source)) |
|
} |
|
fmt.Printf("\"\n") |
|
fmt.Printf("%sHasBlankPreviousLines: %v\n", indent2, v.HasBlankPreviousLines()) |
|
} |
|
for name, value := range kv { |
|
fmt.Printf("%s%s: %s\n", indent2, name, value) |
|
} |
|
if cb != nil { |
|
cb(level + 1) |
|
} |
|
for c := v.FirstChild(); c != nil; c = c.NextSibling() { |
|
c.Dump(source, level+1) |
|
} |
|
fmt.Printf("%s}\n", indent) |
|
} |
|
|
|
// WalkStatus represents a current status of the Walk function. |
|
type WalkStatus int |
|
|
|
const ( |
|
// WalkStop indicates no more walking needed. |
|
WalkStop WalkStatus = iota + 1 |
|
|
|
// WalkSkipChildren indicates that Walk wont walk on children of current |
|
// node. |
|
WalkSkipChildren |
|
|
|
// WalkContinue indicates that Walk can continue to walk. |
|
WalkContinue |
|
) |
|
|
|
// Walker is a function that will be called when Walk find a |
|
// new node. |
|
// entering is set true before walks children, false after walked children. |
|
// If Walker returns error, Walk function immediately stop walking. |
|
type Walker func(n Node, entering bool) (WalkStatus, error) |
|
|
|
// Walk walks a AST tree by the depth first search algorithm. |
|
func Walk(n Node, walker Walker) error { |
|
status, err := walker(n, true) |
|
if err != nil || status == WalkStop { |
|
return err |
|
} |
|
if status != WalkSkipChildren { |
|
for c := n.FirstChild(); c != nil; c = c.NextSibling() { |
|
if err = Walk(c, walker); err != nil { |
|
return err |
|
} |
|
} |
|
} |
|
status, err = walker(n, false) |
|
if err != nil || status == WalkStop { |
|
return err |
|
} |
|
return nil |
|
}
|
|
|