all repos — grayfriday @ 4ba991937b01bee0956c7bfc890ea0b0bf7f64b4

blackfriday fork with a few changes

node.go (view raw)

  1package blackfriday
  2
  3import (
  4	"bytes"
  5	"fmt"
  6)
  7
  8type NodeType int
  9
 10const (
 11	Document NodeType = iota
 12	BlockQuote
 13	List
 14	Item
 15	Paragraph
 16	Header
 17	HorizontalRule
 18	Emph
 19	Strong
 20	Del
 21	Link
 22	Image
 23	Text
 24	HtmlBlock
 25	CodeBlock
 26	Softbreak
 27	Hardbreak
 28	Code
 29	HtmlSpan
 30	Table
 31	TableCell
 32	TableHead
 33	TableBody
 34	TableRow
 35)
 36
 37var nodeTypeNames = []string{
 38	Document:       "Document",
 39	BlockQuote:     "BlockQuote",
 40	List:           "List",
 41	Item:           "Item",
 42	Paragraph:      "Paragraph",
 43	Header:         "Header",
 44	HorizontalRule: "HorizontalRule",
 45	Emph:           "Emph",
 46	Strong:         "Strong",
 47	Del:            "Del",
 48	Link:           "Link",
 49	Image:          "Image",
 50	Text:           "Text",
 51	HtmlBlock:      "HtmlBlock",
 52	CodeBlock:      "CodeBlock",
 53	Softbreak:      "Softbreak",
 54	Hardbreak:      "Hardbreak",
 55	Code:           "Code",
 56	HtmlSpan:       "HtmlSpan",
 57	Table:          "Table",
 58	TableCell:      "TableCell",
 59	TableHead:      "TableHead",
 60	TableBody:      "TableBody",
 61	TableRow:       "TableRow",
 62}
 63
 64func (t NodeType) String() string {
 65	return nodeTypeNames[t]
 66}
 67
 68type ListData struct {
 69	ListFlags  ListType
 70	Tight      bool   // Skip <p>s around list item data if true
 71	BulletChar byte   // '*', '+' or '-' in bullet lists
 72	Delimiter  byte   // '.' or ')' after the number in ordered lists
 73	RefLink    []byte // If not nil, turns this list item into a footnote item and triggers different rendering
 74}
 75
 76type LinkData struct {
 77	Destination []byte
 78	Title       []byte
 79	NoteID      int
 80}
 81
 82type CodeBlockData struct {
 83	IsFenced    bool   // Specifies whether it's a fenced code block or an indented one
 84	Info        []byte // This holds the info string
 85	FenceChar   byte
 86	FenceLength uint32
 87	FenceOffset uint32
 88}
 89
 90type Node struct {
 91	Type       NodeType
 92	Parent     *Node
 93	FirstChild *Node
 94	LastChild  *Node
 95	Prev       *Node // prev sibling
 96	Next       *Node // next sibling
 97
 98	content []byte
 99	open    bool
100
101	Level   uint32 // If Type == Header, this holds the heading level number
102	Literal []byte
103
104	ListData             // If Type == List, this holds list info
105	CodeBlockData        // If Type == CodeBlock, this holds its properties
106	LinkData             // If Type == Link, this holds link info
107	HeaderID      string // If Type == Header, this might hold header ID, if present
108	IsTitleblock  bool
109	IsHeader      bool           // If Type == TableCell, this tells if it's under the header row
110	Align         CellAlignFlags // If Type == TableCell, this holds the value for align attribute
111}
112
113func NewNode(typ NodeType) *Node {
114	return &Node{
115		Type: typ,
116		open: true,
117	}
118}
119
120func (n *Node) unlink() {
121	if n.Prev != nil {
122		n.Prev.Next = n.Next
123	} else if n.Parent != nil {
124		n.Parent.FirstChild = n.Next
125	}
126	if n.Next != nil {
127		n.Next.Prev = n.Prev
128	} else if n.Parent != nil {
129		n.Parent.LastChild = n.Prev
130	}
131	n.Parent = nil
132	n.Next = nil
133	n.Prev = nil
134}
135
136func (n *Node) appendChild(child *Node) {
137	child.unlink()
138	child.Parent = n
139	if n.LastChild != nil {
140		n.LastChild.Next = child
141		child.Prev = n.LastChild
142		n.LastChild = child
143	} else {
144		n.FirstChild = child
145		n.LastChild = child
146	}
147}
148
149func (n *Node) isContainer() bool {
150	switch n.Type {
151	case Document:
152		fallthrough
153	case BlockQuote:
154		fallthrough
155	case List:
156		fallthrough
157	case Item:
158		fallthrough
159	case Paragraph:
160		fallthrough
161	case Header:
162		fallthrough
163	case Emph:
164		fallthrough
165	case Strong:
166		fallthrough
167	case Del:
168		fallthrough
169	case Link:
170		fallthrough
171	case Image:
172		fallthrough
173	case Table:
174		fallthrough
175	case TableHead:
176		fallthrough
177	case TableBody:
178		fallthrough
179	case TableRow:
180		fallthrough
181	case TableCell:
182		return true
183	default:
184		return false
185	}
186	return false
187}
188
189func (n *Node) canContain(t NodeType) bool {
190	if n.Type == List {
191		return t == Item
192	}
193	if n.Type == Document || n.Type == BlockQuote || n.Type == Item {
194		return t != Item
195	}
196	if n.Type == Table {
197		return t == TableHead || t == TableBody
198	}
199	if n.Type == TableHead || n.Type == TableBody {
200		return t == TableRow
201	}
202	if n.Type == TableRow {
203		return t == TableCell
204	}
205	return false
206}
207
208type NodeWalker struct {
209	current  *Node
210	root     *Node
211	entering bool
212}
213
214func NewNodeWalker(root *Node) *NodeWalker {
215	return &NodeWalker{
216		current:  root,
217		root:     nil,
218		entering: true,
219	}
220}
221
222func (nw *NodeWalker) next() (*Node, bool) {
223	if nw.current == nil {
224		return nil, false
225	}
226	if nw.root == nil {
227		nw.root = nw.current
228		return nw.current, nw.entering
229	}
230	if nw.entering && nw.current.isContainer() {
231		if nw.current.FirstChild != nil {
232			nw.current = nw.current.FirstChild
233			nw.entering = true
234		} else {
235			nw.entering = false
236		}
237	} else if nw.current.Next == nil {
238		nw.current = nw.current.Parent
239		nw.entering = false
240	} else {
241		nw.current = nw.current.Next
242		nw.entering = true
243	}
244	if nw.current == nw.root {
245		return nil, false
246	}
247	return nw.current, nw.entering
248}
249
250func (nw *NodeWalker) resumeAt(node *Node, entering bool) {
251	nw.current = node
252	nw.entering = entering
253}
254
255func ForEachNode(root *Node, f func(node *Node, entering bool)) {
256	walker := NewNodeWalker(root)
257	node, entering := walker.next()
258	for node != nil {
259		f(node, entering)
260		node, entering = walker.next()
261	}
262}
263
264func dump(ast *Node) {
265	fmt.Println(dumpString(ast))
266}
267
268func dump_r(ast *Node, depth int) string {
269	if ast == nil {
270		return ""
271	}
272	indent := bytes.Repeat([]byte("\t"), depth)
273	content := ast.Literal
274	if content == nil {
275		content = ast.content
276	}
277	result := fmt.Sprintf("%s%s(%q)\n", indent, ast.Type, content)
278	for n := ast.FirstChild; n != nil; n = n.Next {
279		result += dump_r(n, depth+1)
280	}
281	return result
282}
283
284func dumpString(ast *Node) string {
285	return dump_r(ast, 0)
286}