...

Source file src/github.com/russross/blackfriday/v2/node.go

Documentation: github.com/russross/blackfriday/v2

     1  package blackfriday
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  )
     7  
     8  // NodeType specifies a type of a single node of a syntax tree. Usually one
     9  // node (and its type) corresponds to a single markdown feature, e.g. emphasis
    10  // or code block.
    11  type NodeType int
    12  
    13  // Constants for identifying different types of nodes. See NodeType.
    14  const (
    15  	Document NodeType = iota
    16  	BlockQuote
    17  	List
    18  	Item
    19  	Paragraph
    20  	Heading
    21  	HorizontalRule
    22  	Emph
    23  	Strong
    24  	Del
    25  	Link
    26  	Image
    27  	Text
    28  	HTMLBlock
    29  	CodeBlock
    30  	Softbreak
    31  	Hardbreak
    32  	Code
    33  	HTMLSpan
    34  	Table
    35  	TableCell
    36  	TableHead
    37  	TableBody
    38  	TableRow
    39  )
    40  
    41  var nodeTypeNames = []string{
    42  	Document:       "Document",
    43  	BlockQuote:     "BlockQuote",
    44  	List:           "List",
    45  	Item:           "Item",
    46  	Paragraph:      "Paragraph",
    47  	Heading:        "Heading",
    48  	HorizontalRule: "HorizontalRule",
    49  	Emph:           "Emph",
    50  	Strong:         "Strong",
    51  	Del:            "Del",
    52  	Link:           "Link",
    53  	Image:          "Image",
    54  	Text:           "Text",
    55  	HTMLBlock:      "HTMLBlock",
    56  	CodeBlock:      "CodeBlock",
    57  	Softbreak:      "Softbreak",
    58  	Hardbreak:      "Hardbreak",
    59  	Code:           "Code",
    60  	HTMLSpan:       "HTMLSpan",
    61  	Table:          "Table",
    62  	TableCell:      "TableCell",
    63  	TableHead:      "TableHead",
    64  	TableBody:      "TableBody",
    65  	TableRow:       "TableRow",
    66  }
    67  
    68  func (t NodeType) String() string {
    69  	return nodeTypeNames[t]
    70  }
    71  
    72  // ListData contains fields relevant to a List and Item node type.
    73  type ListData struct {
    74  	ListFlags       ListType
    75  	Tight           bool   // Skip <p>s around list item data if true
    76  	BulletChar      byte   // '*', '+' or '-' in bullet lists
    77  	Delimiter       byte   // '.' or ')' after the number in ordered lists
    78  	RefLink         []byte // If not nil, turns this list item into a footnote item and triggers different rendering
    79  	IsFootnotesList bool   // This is a list of footnotes
    80  }
    81  
    82  // LinkData contains fields relevant to a Link node type.
    83  type LinkData struct {
    84  	Destination []byte // Destination is what goes into a href
    85  	Title       []byte // Title is the tooltip thing that goes in a title attribute
    86  	NoteID      int    // NoteID contains a serial number of a footnote, zero if it's not a footnote
    87  	Footnote    *Node  // If it's a footnote, this is a direct link to the footnote Node. Otherwise nil.
    88  }
    89  
    90  // CodeBlockData contains fields relevant to a CodeBlock node type.
    91  type CodeBlockData struct {
    92  	IsFenced    bool   // Specifies whether it's a fenced code block or an indented one
    93  	Info        []byte // This holds the info string
    94  	FenceChar   byte
    95  	FenceLength int
    96  	FenceOffset int
    97  }
    98  
    99  // TableCellData contains fields relevant to a TableCell node type.
   100  type TableCellData struct {
   101  	IsHeader bool           // This tells if it's under the header row
   102  	Align    CellAlignFlags // This holds the value for align attribute
   103  }
   104  
   105  // HeadingData contains fields relevant to a Heading node type.
   106  type HeadingData struct {
   107  	Level        int    // This holds the heading level number
   108  	HeadingID    string // This might hold heading ID, if present
   109  	IsTitleblock bool   // Specifies whether it's a title block
   110  }
   111  
   112  // Node is a single element in the abstract syntax tree of the parsed document.
   113  // It holds connections to the structurally neighboring nodes and, for certain
   114  // types of nodes, additional information that might be needed when rendering.
   115  type Node struct {
   116  	Type       NodeType // Determines the type of the node
   117  	Parent     *Node    // Points to the parent
   118  	FirstChild *Node    // Points to the first child, if any
   119  	LastChild  *Node    // Points to the last child, if any
   120  	Prev       *Node    // Previous sibling; nil if it's the first child
   121  	Next       *Node    // Next sibling; nil if it's the last child
   122  
   123  	Literal []byte // Text contents of the leaf nodes
   124  
   125  	HeadingData   // Populated if Type is Heading
   126  	ListData      // Populated if Type is List
   127  	CodeBlockData // Populated if Type is CodeBlock
   128  	LinkData      // Populated if Type is Link
   129  	TableCellData // Populated if Type is TableCell
   130  
   131  	content []byte // Markdown content of the block nodes
   132  	open    bool   // Specifies an open block node that has not been finished to process yet
   133  }
   134  
   135  // NewNode allocates a node of a specified type.
   136  func NewNode(typ NodeType) *Node {
   137  	return &Node{
   138  		Type: typ,
   139  		open: true,
   140  	}
   141  }
   142  
   143  func (n *Node) String() string {
   144  	ellipsis := ""
   145  	snippet := n.Literal
   146  	if len(snippet) > 16 {
   147  		snippet = snippet[:16]
   148  		ellipsis = "..."
   149  	}
   150  	return fmt.Sprintf("%s: '%s%s'", n.Type, snippet, ellipsis)
   151  }
   152  
   153  // Unlink removes node 'n' from the tree.
   154  // It panics if the node is nil.
   155  func (n *Node) Unlink() {
   156  	if n.Prev != nil {
   157  		n.Prev.Next = n.Next
   158  	} else if n.Parent != nil {
   159  		n.Parent.FirstChild = n.Next
   160  	}
   161  	if n.Next != nil {
   162  		n.Next.Prev = n.Prev
   163  	} else if n.Parent != nil {
   164  		n.Parent.LastChild = n.Prev
   165  	}
   166  	n.Parent = nil
   167  	n.Next = nil
   168  	n.Prev = nil
   169  }
   170  
   171  // AppendChild adds a node 'child' as a child of 'n'.
   172  // It panics if either node is nil.
   173  func (n *Node) AppendChild(child *Node) {
   174  	child.Unlink()
   175  	child.Parent = n
   176  	if n.LastChild != nil {
   177  		n.LastChild.Next = child
   178  		child.Prev = n.LastChild
   179  		n.LastChild = child
   180  	} else {
   181  		n.FirstChild = child
   182  		n.LastChild = child
   183  	}
   184  }
   185  
   186  // InsertBefore inserts 'sibling' immediately before 'n'.
   187  // It panics if either node is nil.
   188  func (n *Node) InsertBefore(sibling *Node) {
   189  	sibling.Unlink()
   190  	sibling.Prev = n.Prev
   191  	if sibling.Prev != nil {
   192  		sibling.Prev.Next = sibling
   193  	}
   194  	sibling.Next = n
   195  	n.Prev = sibling
   196  	sibling.Parent = n.Parent
   197  	if sibling.Prev == nil {
   198  		sibling.Parent.FirstChild = sibling
   199  	}
   200  }
   201  
   202  // IsContainer returns true if 'n' can contain children.
   203  func (n *Node) IsContainer() bool {
   204  	switch n.Type {
   205  	case Document:
   206  		fallthrough
   207  	case BlockQuote:
   208  		fallthrough
   209  	case List:
   210  		fallthrough
   211  	case Item:
   212  		fallthrough
   213  	case Paragraph:
   214  		fallthrough
   215  	case Heading:
   216  		fallthrough
   217  	case Emph:
   218  		fallthrough
   219  	case Strong:
   220  		fallthrough
   221  	case Del:
   222  		fallthrough
   223  	case Link:
   224  		fallthrough
   225  	case Image:
   226  		fallthrough
   227  	case Table:
   228  		fallthrough
   229  	case TableHead:
   230  		fallthrough
   231  	case TableBody:
   232  		fallthrough
   233  	case TableRow:
   234  		fallthrough
   235  	case TableCell:
   236  		return true
   237  	default:
   238  		return false
   239  	}
   240  }
   241  
   242  // IsLeaf returns true if 'n' is a leaf node.
   243  func (n *Node) IsLeaf() bool {
   244  	return !n.IsContainer()
   245  }
   246  
   247  func (n *Node) canContain(t NodeType) bool {
   248  	if n.Type == List {
   249  		return t == Item
   250  	}
   251  	if n.Type == Document || n.Type == BlockQuote || n.Type == Item {
   252  		return t != Item
   253  	}
   254  	if n.Type == Table {
   255  		return t == TableHead || t == TableBody
   256  	}
   257  	if n.Type == TableHead || n.Type == TableBody {
   258  		return t == TableRow
   259  	}
   260  	if n.Type == TableRow {
   261  		return t == TableCell
   262  	}
   263  	return false
   264  }
   265  
   266  // WalkStatus allows NodeVisitor to have some control over the tree traversal.
   267  // It is returned from NodeVisitor and different values allow Node.Walk to
   268  // decide which node to go to next.
   269  type WalkStatus int
   270  
   271  const (
   272  	// GoToNext is the default traversal of every node.
   273  	GoToNext WalkStatus = iota
   274  	// SkipChildren tells walker to skip all children of current node.
   275  	SkipChildren
   276  	// Terminate tells walker to terminate the traversal.
   277  	Terminate
   278  )
   279  
   280  // NodeVisitor is a callback to be called when traversing the syntax tree.
   281  // Called twice for every node: once with entering=true when the branch is
   282  // first visited, then with entering=false after all the children are done.
   283  type NodeVisitor func(node *Node, entering bool) WalkStatus
   284  
   285  // Walk is a convenience method that instantiates a walker and starts a
   286  // traversal of subtree rooted at n.
   287  func (n *Node) Walk(visitor NodeVisitor) {
   288  	w := newNodeWalker(n)
   289  	for w.current != nil {
   290  		status := visitor(w.current, w.entering)
   291  		switch status {
   292  		case GoToNext:
   293  			w.next()
   294  		case SkipChildren:
   295  			w.entering = false
   296  			w.next()
   297  		case Terminate:
   298  			return
   299  		}
   300  	}
   301  }
   302  
   303  type nodeWalker struct {
   304  	current  *Node
   305  	root     *Node
   306  	entering bool
   307  }
   308  
   309  func newNodeWalker(root *Node) *nodeWalker {
   310  	return &nodeWalker{
   311  		current:  root,
   312  		root:     root,
   313  		entering: true,
   314  	}
   315  }
   316  
   317  func (nw *nodeWalker) next() {
   318  	if (!nw.current.IsContainer() || !nw.entering) && nw.current == nw.root {
   319  		nw.current = nil
   320  		return
   321  	}
   322  	if nw.entering && nw.current.IsContainer() {
   323  		if nw.current.FirstChild != nil {
   324  			nw.current = nw.current.FirstChild
   325  			nw.entering = true
   326  		} else {
   327  			nw.entering = false
   328  		}
   329  	} else if nw.current.Next == nil {
   330  		nw.current = nw.current.Parent
   331  		nw.entering = false
   332  	} else {
   333  		nw.current = nw.current.Next
   334  		nw.entering = true
   335  	}
   336  }
   337  
   338  func dump(ast *Node) {
   339  	fmt.Println(dumpString(ast))
   340  }
   341  
   342  func dumpR(ast *Node, depth int) string {
   343  	if ast == nil {
   344  		return ""
   345  	}
   346  	indent := bytes.Repeat([]byte("\t"), depth)
   347  	content := ast.Literal
   348  	if content == nil {
   349  		content = ast.content
   350  	}
   351  	result := fmt.Sprintf("%s%s(%q)\n", indent, ast.Type, content)
   352  	for n := ast.FirstChild; n != nil; n = n.Next {
   353  		result += dumpR(n, depth+1)
   354  	}
   355  	return result
   356  }
   357  
   358  func dumpString(ast *Node) string {
   359  	return dumpR(ast, 0)
   360  }
   361  

View as plain text