package ast // An attribute can be attached to block elements. They are specified as // {#id .classs key="value"} where quotes for values are mandatory, multiple // key/value pairs are separated by whitespace. type Attribute struct { ID []byte Classes [][]byte Attrs map[string][]byte } // 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 if trying to set non-empty children // because Leaf cannot have children func (l *Leaf) SetChildren(newChildren []Node) { if len(newChildren) != 0 { 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
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
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. AdditionalAttributes []string // Defines additional attributes to use during rendering. } // CrossReference is a reference node. type CrossReference struct { Container Destination []byte // Destination is where the reference points to Suffix []byte // Potential citation suffix, i.e. (#myid, text) } // 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 ColSpan int // How many columns to span } // 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) }