560 lines
13 KiB
Go
560 lines
13 KiB
Go
|
package ast
|
||
|
|
||
|
// ListType contains bitwise or'ed flags for list and list item objects.
|
||
|
type ListType int
|
||
|
|
||
|
// These are the possible flag values for the ListItem renderer.
|
||
|
// Multiple flag values may be ORed together.
|
||
|
// These are mostly of interest if you are writing a new output format.
|
||
|
const (
|
||
|
ListTypeOrdered ListType = 1 << iota
|
||
|
ListTypeDefinition
|
||
|
ListTypeTerm
|
||
|
|
||
|
ListItemContainsBlock
|
||
|
ListItemBeginningOfList // TODO: figure out if this is of any use now
|
||
|
ListItemEndOfList
|
||
|
)
|
||
|
|
||
|
// CellAlignFlags holds a type of alignment in a table cell.
|
||
|
type CellAlignFlags int
|
||
|
|
||
|
// These are the possible flag values for the table cell renderer.
|
||
|
// Only a single one of these values will be used; they are not ORed together.
|
||
|
// These are mostly of interest if you are writing a new output format.
|
||
|
const (
|
||
|
TableAlignmentLeft CellAlignFlags = 1 << iota
|
||
|
TableAlignmentRight
|
||
|
TableAlignmentCenter = (TableAlignmentLeft | TableAlignmentRight)
|
||
|
)
|
||
|
|
||
|
func (a CellAlignFlags) String() string {
|
||
|
switch a {
|
||
|
case TableAlignmentLeft:
|
||
|
return "left"
|
||
|
case TableAlignmentRight:
|
||
|
return "right"
|
||
|
case TableAlignmentCenter:
|
||
|
return "center"
|
||
|
default:
|
||
|
return ""
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// DocumentMatters holds the type of a {front,main,back}matter in the document
|
||
|
type DocumentMatters int
|
||
|
|
||
|
// These are all possible Document divisions.
|
||
|
const (
|
||
|
DocumentMatterNone DocumentMatters = iota
|
||
|
DocumentMatterFront
|
||
|
DocumentMatterMain
|
||
|
DocumentMatterBack
|
||
|
)
|
||
|
|
||
|
// CitationTypes holds the type of a citation, informative, normative or suppressed
|
||
|
type CitationTypes int
|
||
|
|
||
|
const (
|
||
|
CitationTypeNone CitationTypes = iota
|
||
|
CitationTypeSuppressed
|
||
|
CitationTypeInformative
|
||
|
CitationTypeNormative
|
||
|
)
|
||
|
|
||
|
// Node defines an ast node
|
||
|
type Node interface {
|
||
|
AsContainer() *Container
|
||
|
AsLeaf() *Leaf
|
||
|
GetParent() Node
|
||
|
SetParent(newParent Node)
|
||
|
GetChildren() []Node
|
||
|
SetChildren(newChildren []Node)
|
||
|
}
|
||
|
|
||
|
// Container is a type of node that can contain children
|
||
|
type Container struct {
|
||
|
Parent Node
|
||
|
Children []Node
|
||
|
|
||
|
Literal []byte // Text contents of the leaf nodes
|
||
|
Content []byte // Markdown content of the block nodes
|
||
|
|
||
|
*Attribute // Block level attribute
|
||
|
}
|
||
|
|
||
|
// AsContainer returns itself as *Container
|
||
|
func (c *Container) AsContainer() *Container {
|
||
|
return c
|
||
|
}
|
||
|
|
||
|
// AsLeaf returns nil
|
||
|
func (c *Container) AsLeaf() *Leaf {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// GetParent returns parent node
|
||
|
func (c *Container) GetParent() Node {
|
||
|
return c.Parent
|
||
|
}
|
||
|
|
||
|
// SetParent sets the parent node
|
||
|
func (c *Container) SetParent(newParent Node) {
|
||
|
c.Parent = newParent
|
||
|
}
|
||
|
|
||
|
// GetChildren returns children nodes
|
||
|
func (c *Container) GetChildren() []Node {
|
||
|
return c.Children
|
||
|
}
|
||
|
|
||
|
// SetChildren sets children node
|
||
|
func (c *Container) SetChildren(newChildren []Node) {
|
||
|
c.Children = newChildren
|
||
|
}
|
||
|
|
||
|
// Leaf is a type of node that cannot have children
|
||
|
type Leaf struct {
|
||
|
Parent Node
|
||
|
|
||
|
Literal []byte // Text contents of the leaf nodes
|
||
|
Content []byte // Markdown content of the block nodes
|
||
|
|
||
|
*Attribute // Block level attribute
|
||
|
}
|
||
|
|
||
|
// AsContainer returns nil
|
||
|
func (l *Leaf) AsContainer() *Container {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// AsLeaf returns itself as *Leaf
|
||
|
func (l *Leaf) AsLeaf() *Leaf {
|
||
|
return l
|
||
|
}
|
||
|
|
||
|
// GetParent returns parent node
|
||
|
func (l *Leaf) GetParent() Node {
|
||
|
return l.Parent
|
||
|
}
|
||
|
|
||
|
// SetParent sets the parent nodd
|
||
|
func (l *Leaf) SetParent(newParent Node) {
|
||
|
l.Parent = newParent
|
||
|
}
|
||
|
|
||
|
// GetChildren returns nil because Leaf cannot have children
|
||
|
func (l *Leaf) GetChildren() []Node {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// SetChildren will panic becuase Leaf cannot have children
|
||
|
func (l *Leaf) SetChildren(newChildren []Node) {
|
||
|
panic("leaf node cannot have children")
|
||
|
}
|
||
|
|
||
|
// Document represents markdown document node, a root of ast
|
||
|
type Document struct {
|
||
|
Container
|
||
|
}
|
||
|
|
||
|
// DocumentMatter represents markdown node that signals a document
|
||
|
// division: frontmatter, mainmatter or backmatter.
|
||
|
type DocumentMatter struct {
|
||
|
Container
|
||
|
|
||
|
Matter DocumentMatters
|
||
|
}
|
||
|
|
||
|
// BlockQuote represents markdown block quote node
|
||
|
type BlockQuote struct {
|
||
|
Container
|
||
|
}
|
||
|
|
||
|
// Aside represents an markdown aside node.
|
||
|
type Aside struct {
|
||
|
Container
|
||
|
}
|
||
|
|
||
|
// List represents markdown list node
|
||
|
type List struct {
|
||
|
Container
|
||
|
|
||
|
ListFlags ListType
|
||
|
Tight bool // Skip <p>s around list item data if true
|
||
|
BulletChar byte // '*', '+' or '-' in bullet lists
|
||
|
Delimiter byte // '.' or ')' after the number in ordered lists
|
||
|
Start int // for ordered lists this indicates the starting number if > 0
|
||
|
RefLink []byte // If not nil, turns this list item into a footnote item and triggers different rendering
|
||
|
IsFootnotesList bool // This is a list of footnotes
|
||
|
}
|
||
|
|
||
|
// ListItem represents markdown list item node
|
||
|
type ListItem struct {
|
||
|
Container
|
||
|
|
||
|
ListFlags ListType
|
||
|
Tight bool // Skip <p>s around list item data if true
|
||
|
BulletChar byte // '*', '+' or '-' in bullet lists
|
||
|
Delimiter byte // '.' or ')' after the number in ordered lists
|
||
|
RefLink []byte // If not nil, turns this list item into a footnote item and triggers different rendering
|
||
|
IsFootnotesList bool // This is a list of footnotes
|
||
|
}
|
||
|
|
||
|
// Paragraph represents markdown paragraph node
|
||
|
type Paragraph struct {
|
||
|
Container
|
||
|
}
|
||
|
|
||
|
// Math represents markdown MathAjax inline node
|
||
|
type Math struct {
|
||
|
Leaf
|
||
|
}
|
||
|
|
||
|
// MathBlock represents markdown MathAjax block node
|
||
|
type MathBlock struct {
|
||
|
Container
|
||
|
}
|
||
|
|
||
|
// Heading represents markdown heading node
|
||
|
type Heading struct {
|
||
|
Container
|
||
|
|
||
|
Level int // This holds the heading level number
|
||
|
HeadingID string // This might hold heading ID, if present
|
||
|
IsTitleblock bool // Specifies whether it's a title block
|
||
|
IsSpecial bool // We are a special heading (starts with .#)
|
||
|
}
|
||
|
|
||
|
// HorizontalRule represents markdown horizontal rule node
|
||
|
type HorizontalRule struct {
|
||
|
Leaf
|
||
|
}
|
||
|
|
||
|
// Emph represents markdown emphasis node
|
||
|
type Emph struct {
|
||
|
Container
|
||
|
}
|
||
|
|
||
|
// Strong represents markdown strong node
|
||
|
type Strong struct {
|
||
|
Container
|
||
|
}
|
||
|
|
||
|
// Del represents markdown del node
|
||
|
type Del struct {
|
||
|
Container
|
||
|
}
|
||
|
|
||
|
// Link represents markdown link node
|
||
|
type Link struct {
|
||
|
Container
|
||
|
|
||
|
Destination []byte // Destination is what goes into a href
|
||
|
Title []byte // Title is the tooltip thing that goes in a title attribute
|
||
|
NoteID int // NoteID contains a serial number of a footnote, zero if it's not a footnote
|
||
|
Footnote Node // If it's a footnote, this is a direct link to the footnote Node. Otherwise nil.
|
||
|
DeferredID []byte // If a deferred link this holds the original ID.
|
||
|
}
|
||
|
|
||
|
// CrossReference is a reference node.
|
||
|
type CrossReference struct {
|
||
|
Container
|
||
|
|
||
|
Destination []byte // Destination is where the reference points to
|
||
|
}
|
||
|
|
||
|
// Citation is a citation node.
|
||
|
type Citation struct {
|
||
|
Leaf
|
||
|
|
||
|
Destination [][]byte // Destination is where the citation points to. Multiple ones are allowed.
|
||
|
Type []CitationTypes // 1:1 mapping of destination and citation type
|
||
|
Suffix [][]byte // Potential citation suffix, i.e. [@!RFC1035, p. 144]
|
||
|
}
|
||
|
|
||
|
// Image represents markdown image node
|
||
|
type Image struct {
|
||
|
Container
|
||
|
|
||
|
Destination []byte // Destination is what goes into a href
|
||
|
Title []byte // Title is the tooltip thing that goes in a title attribute
|
||
|
}
|
||
|
|
||
|
// Text represents markdown text node
|
||
|
type Text struct {
|
||
|
Leaf
|
||
|
}
|
||
|
|
||
|
// HTMLBlock represents markdown html node
|
||
|
type HTMLBlock struct {
|
||
|
Leaf
|
||
|
}
|
||
|
|
||
|
// CodeBlock represents markdown code block node
|
||
|
type CodeBlock struct {
|
||
|
Leaf
|
||
|
|
||
|
IsFenced bool // Specifies whether it's a fenced code block or an indented one
|
||
|
Info []byte // This holds the info string
|
||
|
FenceChar byte
|
||
|
FenceLength int
|
||
|
FenceOffset int
|
||
|
}
|
||
|
|
||
|
// Softbreak represents markdown softbreak node
|
||
|
// Note: not used currently
|
||
|
type Softbreak struct {
|
||
|
Leaf
|
||
|
}
|
||
|
|
||
|
// Hardbreak represents markdown hard break node
|
||
|
type Hardbreak struct {
|
||
|
Leaf
|
||
|
}
|
||
|
|
||
|
// NonBlockingSpace represents markdown non-blocking space node
|
||
|
type NonBlockingSpace struct {
|
||
|
Leaf
|
||
|
}
|
||
|
|
||
|
// Code represents markdown code node
|
||
|
type Code struct {
|
||
|
Leaf
|
||
|
}
|
||
|
|
||
|
// HTMLSpan represents markdown html span node
|
||
|
type HTMLSpan struct {
|
||
|
Leaf
|
||
|
}
|
||
|
|
||
|
// Table represents markdown table node
|
||
|
type Table struct {
|
||
|
Container
|
||
|
}
|
||
|
|
||
|
// TableCell represents markdown table cell node
|
||
|
type TableCell struct {
|
||
|
Container
|
||
|
|
||
|
IsHeader bool // This tells if it's under the header row
|
||
|
Align CellAlignFlags // This holds the value for align attribute
|
||
|
}
|
||
|
|
||
|
// TableHeader represents markdown table head node
|
||
|
type TableHeader struct {
|
||
|
Container
|
||
|
}
|
||
|
|
||
|
// TableBody represents markdown table body node
|
||
|
type TableBody struct {
|
||
|
Container
|
||
|
}
|
||
|
|
||
|
// TableRow represents markdown table row node
|
||
|
type TableRow struct {
|
||
|
Container
|
||
|
}
|
||
|
|
||
|
// TableFooter represents markdown table foot node
|
||
|
type TableFooter struct {
|
||
|
Container
|
||
|
}
|
||
|
|
||
|
// Caption represents a figure, code or quote caption
|
||
|
type Caption struct {
|
||
|
Container
|
||
|
}
|
||
|
|
||
|
// CaptionFigure is a node (blockquote or codeblock) that has a caption
|
||
|
type CaptionFigure struct {
|
||
|
Container
|
||
|
|
||
|
HeadingID string // This might hold heading ID, if present
|
||
|
}
|
||
|
|
||
|
// Callout is a node that can exist both in text (where it is an actual node) and in a code block.
|
||
|
type Callout struct {
|
||
|
Leaf
|
||
|
|
||
|
ID []byte // number of this callout
|
||
|
}
|
||
|
|
||
|
// Index is a node that contains an Index item and an optional, subitem.
|
||
|
type Index struct {
|
||
|
Leaf
|
||
|
|
||
|
Primary bool
|
||
|
Item []byte
|
||
|
Subitem []byte
|
||
|
ID string // ID of the index
|
||
|
}
|
||
|
|
||
|
// Subscript is a subscript node
|
||
|
type Subscript struct {
|
||
|
Leaf
|
||
|
}
|
||
|
|
||
|
// Subscript is a superscript node
|
||
|
type Superscript struct {
|
||
|
Leaf
|
||
|
}
|
||
|
|
||
|
// Footnotes is a node that contains all footnotes
|
||
|
type Footnotes struct {
|
||
|
Container
|
||
|
}
|
||
|
|
||
|
func removeNodeFromArray(a []Node, node Node) []Node {
|
||
|
n := len(a)
|
||
|
for i := 0; i < n; i++ {
|
||
|
if a[i] == node {
|
||
|
return append(a[:i], a[i+1:]...)
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// AppendChild appends child to children of parent
|
||
|
// It panics if either node is nil.
|
||
|
func AppendChild(parent Node, child Node) {
|
||
|
RemoveFromTree(child)
|
||
|
child.SetParent(parent)
|
||
|
newChildren := append(parent.GetChildren(), child)
|
||
|
parent.SetChildren(newChildren)
|
||
|
}
|
||
|
|
||
|
// RemoveFromTree removes this node from tree
|
||
|
func RemoveFromTree(n Node) {
|
||
|
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
|
||
|
n.SetChildren(nil)
|
||
|
p := n.GetParent()
|
||
|
newChildren := removeNodeFromArray(p.GetChildren(), n)
|
||
|
if newChildren != nil {
|
||
|
p.SetChildren(newChildren)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// GetLastChild returns last child of node n
|
||
|
// It's implemented as stand-alone function to keep Node interface small
|
||
|
func GetLastChild(n Node) Node {
|
||
|
a := n.GetChildren()
|
||
|
if len(a) > 0 {
|
||
|
return a[len(a)-1]
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// GetFirstChild returns first child of node n
|
||
|
// It's implemented as stand-alone function to keep Node interface small
|
||
|
func GetFirstChild(n Node) Node {
|
||
|
a := n.GetChildren()
|
||
|
if len(a) > 0 {
|
||
|
return a[0]
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// GetNextNode returns next sibling of node n (node after n)
|
||
|
// We can't make it part of Container or Leaf because we loose Node identity
|
||
|
func GetNextNode(n Node) Node {
|
||
|
parent := n.GetParent()
|
||
|
if parent == nil {
|
||
|
return nil
|
||
|
}
|
||
|
a := parent.GetChildren()
|
||
|
len := len(a) - 1
|
||
|
for i := 0; i < len; i++ {
|
||
|
if a[i] == n {
|
||
|
return a[i+1]
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// GetPrevNode returns previous sibling of node n (node before n)
|
||
|
// We can't make it part of Container or Leaf because we loose Node identity
|
||
|
func GetPrevNode(n Node) Node {
|
||
|
parent := n.GetParent()
|
||
|
if parent == nil {
|
||
|
return nil
|
||
|
}
|
||
|
a := parent.GetChildren()
|
||
|
len := len(a)
|
||
|
for i := 1; i < len; i++ {
|
||
|
if a[i] == n {
|
||
|
return a[i-1]
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// 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.
|
||
|
type WalkStatus int
|
||
|
|
||
|
const (
|
||
|
// GoToNext is the default traversal of every node.
|
||
|
GoToNext WalkStatus = iota
|
||
|
// SkipChildren tells walker to skip all children of current node.
|
||
|
SkipChildren
|
||
|
// Terminate tells walker to terminate the traversal.
|
||
|
Terminate
|
||
|
)
|
||
|
|
||
|
// NodeVisitor is a callback to be called when traversing the syntax tree.
|
||
|
// Called twice for every node: once with entering=true when the branch is
|
||
|
// first visited, then with entering=false after all the children are done.
|
||
|
type NodeVisitor interface {
|
||
|
Visit(node Node, entering bool) WalkStatus
|
||
|
}
|
||
|
|
||
|
// NodeVisitorFunc casts a function to match NodeVisitor interface
|
||
|
type NodeVisitorFunc func(node Node, entering bool) WalkStatus
|
||
|
|
||
|
// Walk traverses tree recursively
|
||
|
func Walk(n Node, visitor NodeVisitor) WalkStatus {
|
||
|
isContainer := n.AsContainer() != nil
|
||
|
status := visitor.Visit(n, true) // entering
|
||
|
if status == Terminate {
|
||
|
// even if terminating, close container node
|
||
|
if isContainer {
|
||
|
visitor.Visit(n, false)
|
||
|
}
|
||
|
return status
|
||
|
}
|
||
|
if isContainer && status != SkipChildren {
|
||
|
children := n.GetChildren()
|
||
|
for _, n := range children {
|
||
|
status = Walk(n, visitor)
|
||
|
if status == Terminate {
|
||
|
return status
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if isContainer {
|
||
|
status = visitor.Visit(n, false) // exiting
|
||
|
if status == Terminate {
|
||
|
return status
|
||
|
}
|
||
|
}
|
||
|
return GoToNext
|
||
|
}
|
||
|
|
||
|
// Visit calls visitor function
|
||
|
func (f NodeVisitorFunc) Visit(node Node, entering bool) WalkStatus {
|
||
|
return f(node, entering)
|
||
|
}
|
||
|
|
||
|
// WalkFunc is like Walk but accepts just a callback function
|
||
|
func WalkFunc(n Node, f NodeVisitorFunc) {
|
||
|
visitor := NodeVisitorFunc(f)
|
||
|
Walk(n, visitor)
|
||
|
}
|