vendor/github.com/russross/blackfriday/node.go
changeset 246 0998f404dd31
parent 245 910f00ab2799
child 247 1ca743b3eb80
equal deleted inserted replaced
245:910f00ab2799 246:0998f404dd31
     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 func (n *Node) isContainer() bool {
       
   203 	switch n.Type {
       
   204 	case Document:
       
   205 		fallthrough
       
   206 	case BlockQuote:
       
   207 		fallthrough
       
   208 	case List:
       
   209 		fallthrough
       
   210 	case Item:
       
   211 		fallthrough
       
   212 	case Paragraph:
       
   213 		fallthrough
       
   214 	case Heading:
       
   215 		fallthrough
       
   216 	case Emph:
       
   217 		fallthrough
       
   218 	case Strong:
       
   219 		fallthrough
       
   220 	case Del:
       
   221 		fallthrough
       
   222 	case Link:
       
   223 		fallthrough
       
   224 	case Image:
       
   225 		fallthrough
       
   226 	case Table:
       
   227 		fallthrough
       
   228 	case TableHead:
       
   229 		fallthrough
       
   230 	case TableBody:
       
   231 		fallthrough
       
   232 	case TableRow:
       
   233 		fallthrough
       
   234 	case TableCell:
       
   235 		return true
       
   236 	default:
       
   237 		return false
       
   238 	}
       
   239 }
       
   240 
       
   241 func (n *Node) canContain(t NodeType) bool {
       
   242 	if n.Type == List {
       
   243 		return t == Item
       
   244 	}
       
   245 	if n.Type == Document || n.Type == BlockQuote || n.Type == Item {
       
   246 		return t != Item
       
   247 	}
       
   248 	if n.Type == Table {
       
   249 		return t == TableHead || t == TableBody
       
   250 	}
       
   251 	if n.Type == TableHead || n.Type == TableBody {
       
   252 		return t == TableRow
       
   253 	}
       
   254 	if n.Type == TableRow {
       
   255 		return t == TableCell
       
   256 	}
       
   257 	return false
       
   258 }
       
   259 
       
   260 // WalkStatus allows NodeVisitor to have some control over the tree traversal.
       
   261 // It is returned from NodeVisitor and different values allow Node.Walk to
       
   262 // decide which node to go to next.
       
   263 type WalkStatus int
       
   264 
       
   265 const (
       
   266 	// GoToNext is the default traversal of every node.
       
   267 	GoToNext WalkStatus = iota
       
   268 	// SkipChildren tells walker to skip all children of current node.
       
   269 	SkipChildren
       
   270 	// Terminate tells walker to terminate the traversal.
       
   271 	Terminate
       
   272 )
       
   273 
       
   274 // NodeVisitor is a callback to be called when traversing the syntax tree.
       
   275 // Called twice for every node: once with entering=true when the branch is
       
   276 // first visited, then with entering=false after all the children are done.
       
   277 type NodeVisitor func(node *Node, entering bool) WalkStatus
       
   278 
       
   279 // Walk is a convenience method that instantiates a walker and starts a
       
   280 // traversal of subtree rooted at n.
       
   281 func (n *Node) Walk(visitor NodeVisitor) {
       
   282 	w := newNodeWalker(n)
       
   283 	for w.current != nil {
       
   284 		status := visitor(w.current, w.entering)
       
   285 		switch status {
       
   286 		case GoToNext:
       
   287 			w.next()
       
   288 		case SkipChildren:
       
   289 			w.entering = false
       
   290 			w.next()
       
   291 		case Terminate:
       
   292 			return
       
   293 		}
       
   294 	}
       
   295 }
       
   296 
       
   297 type nodeWalker struct {
       
   298 	current  *Node
       
   299 	root     *Node
       
   300 	entering bool
       
   301 }
       
   302 
       
   303 func newNodeWalker(root *Node) *nodeWalker {
       
   304 	return &nodeWalker{
       
   305 		current:  root,
       
   306 		root:     root,
       
   307 		entering: true,
       
   308 	}
       
   309 }
       
   310 
       
   311 func (nw *nodeWalker) next() {
       
   312 	if (!nw.current.isContainer() || !nw.entering) && nw.current == nw.root {
       
   313 		nw.current = nil
       
   314 		return
       
   315 	}
       
   316 	if nw.entering && nw.current.isContainer() {
       
   317 		if nw.current.FirstChild != nil {
       
   318 			nw.current = nw.current.FirstChild
       
   319 			nw.entering = true
       
   320 		} else {
       
   321 			nw.entering = false
       
   322 		}
       
   323 	} else if nw.current.Next == nil {
       
   324 		nw.current = nw.current.Parent
       
   325 		nw.entering = false
       
   326 	} else {
       
   327 		nw.current = nw.current.Next
       
   328 		nw.entering = true
       
   329 	}
       
   330 }
       
   331 
       
   332 func dump(ast *Node) {
       
   333 	fmt.Println(dumpString(ast))
       
   334 }
       
   335 
       
   336 func dumpR(ast *Node, depth int) string {
       
   337 	if ast == nil {
       
   338 		return ""
       
   339 	}
       
   340 	indent := bytes.Repeat([]byte("\t"), depth)
       
   341 	content := ast.Literal
       
   342 	if content == nil {
       
   343 		content = ast.content
       
   344 	}
       
   345 	result := fmt.Sprintf("%s%s(%q)\n", indent, ast.Type, content)
       
   346 	for n := ast.FirstChild; n != nil; n = n.Next {
       
   347 		result += dumpR(n, depth+1)
       
   348 	}
       
   349 	return result
       
   350 }
       
   351 
       
   352 func dumpString(ast *Node) string {
       
   353 	return dumpR(ast, 0)
       
   354 }