all repos — grayfriday @ bcd5b5b78026800098fd6e4be677763966ffd22b

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 TableCellData struct {
 91	IsHeader bool           // This tells if it's under the header row
 92	Align    CellAlignFlags // This holds the value for align attribute
 93}
 94
 95type Node struct {
 96	Type       NodeType
 97	Parent     *Node
 98	FirstChild *Node
 99	LastChild  *Node
100	Prev       *Node // prev sibling
101	Next       *Node // next sibling
102
103	content []byte
104	open    bool
105
106	Level   uint32 // If Type == Header, this holds the heading level number
107	Literal []byte
108
109	ListData             // If Type == List, this holds list info
110	CodeBlockData        // If Type == CodeBlock, this holds its properties
111	LinkData             // If Type == Link, this holds link info
112	TableCellData        // If Type == TableCell, this holds its properties
113	HeaderID      string // If Type == Header, this might hold header ID, if present
114	IsTitleblock  bool
115}
116
117func NewNode(typ NodeType) *Node {
118	return &Node{
119		Type: typ,
120		open: true,
121	}
122}
123
124func (n *Node) unlink() {
125	if n.Prev != nil {
126		n.Prev.Next = n.Next
127	} else if n.Parent != nil {
128		n.Parent.FirstChild = n.Next
129	}
130	if n.Next != nil {
131		n.Next.Prev = n.Prev
132	} else if n.Parent != nil {
133		n.Parent.LastChild = n.Prev
134	}
135	n.Parent = nil
136	n.Next = nil
137	n.Prev = nil
138}
139
140func (n *Node) appendChild(child *Node) {
141	child.unlink()
142	child.Parent = n
143	if n.LastChild != nil {
144		n.LastChild.Next = child
145		child.Prev = n.LastChild
146		n.LastChild = child
147	} else {
148		n.FirstChild = child
149		n.LastChild = child
150	}
151}
152
153func (n *Node) isContainer() bool {
154	switch n.Type {
155	case Document:
156		fallthrough
157	case BlockQuote:
158		fallthrough
159	case List:
160		fallthrough
161	case Item:
162		fallthrough
163	case Paragraph:
164		fallthrough
165	case Header:
166		fallthrough
167	case Emph:
168		fallthrough
169	case Strong:
170		fallthrough
171	case Del:
172		fallthrough
173	case Link:
174		fallthrough
175	case Image:
176		fallthrough
177	case Table:
178		fallthrough
179	case TableHead:
180		fallthrough
181	case TableBody:
182		fallthrough
183	case TableRow:
184		fallthrough
185	case TableCell:
186		return true
187	default:
188		return false
189	}
190	return false
191}
192
193func (n *Node) canContain(t NodeType) bool {
194	if n.Type == List {
195		return t == Item
196	}
197	if n.Type == Document || n.Type == BlockQuote || n.Type == Item {
198		return t != Item
199	}
200	if n.Type == Table {
201		return t == TableHead || t == TableBody
202	}
203	if n.Type == TableHead || n.Type == TableBody {
204		return t == TableRow
205	}
206	if n.Type == TableRow {
207		return t == TableCell
208	}
209	return false
210}
211
212type NodeWalker struct {
213	current  *Node
214	root     *Node
215	entering bool
216}
217
218func NewNodeWalker(root *Node) *NodeWalker {
219	return &NodeWalker{
220		current:  root,
221		root:     nil,
222		entering: true,
223	}
224}
225
226func (nw *NodeWalker) next() (*Node, bool) {
227	if nw.current == nil {
228		return nil, false
229	}
230	if nw.root == nil {
231		nw.root = nw.current
232		return nw.current, nw.entering
233	}
234	if nw.entering && nw.current.isContainer() {
235		if nw.current.FirstChild != nil {
236			nw.current = nw.current.FirstChild
237			nw.entering = true
238		} else {
239			nw.entering = false
240		}
241	} else if nw.current.Next == nil {
242		nw.current = nw.current.Parent
243		nw.entering = false
244	} else {
245		nw.current = nw.current.Next
246		nw.entering = true
247	}
248	if nw.current == nw.root {
249		return nil, false
250	}
251	return nw.current, nw.entering
252}
253
254func (nw *NodeWalker) resumeAt(node *Node, entering bool) {
255	nw.current = node
256	nw.entering = entering
257}
258
259func ForEachNode(root *Node, f func(node *Node, entering bool)) {
260	walker := NewNodeWalker(root)
261	node, entering := walker.next()
262	for node != nil {
263		f(node, entering)
264		node, entering = walker.next()
265	}
266}
267
268func dump(ast *Node) {
269	fmt.Println(dumpString(ast))
270}
271
272func dump_r(ast *Node, depth int) string {
273	if ast == nil {
274		return ""
275	}
276	indent := bytes.Repeat([]byte("\t"), depth)
277	content := ast.Literal
278	if content == nil {
279		content = ast.content
280	}
281	result := fmt.Sprintf("%s%s(%q)\n", indent, ast.Type, content)
282	for n := ast.FirstChild; n != nil; n = n.Next {
283		result += dump_r(n, depth+1)
284	}
285	return result
286}
287
288func dumpString(ast *Node) string {
289	return dump_r(ast, 0)
290}