...
1 package blackfriday
2
3 import (
4 "bytes"
5 "fmt"
6 )
7
8
9
10
11 type NodeType int
12
13
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
73 type ListData struct {
74 ListFlags ListType
75 Tight bool
76 BulletChar byte
77 Delimiter byte
78 RefLink []byte
79 IsFootnotesList bool
80 }
81
82
83 type LinkData struct {
84 Destination []byte
85 Title []byte
86 NoteID int
87 Footnote *Node
88 }
89
90
91 type CodeBlockData struct {
92 IsFenced bool
93 Info []byte
94 FenceChar byte
95 FenceLength int
96 FenceOffset int
97 }
98
99
100 type TableCellData struct {
101 IsHeader bool
102 Align CellAlignFlags
103 }
104
105
106 type HeadingData struct {
107 Level int
108 HeadingID string
109 IsTitleblock bool
110 }
111
112
113
114
115 type Node struct {
116 Type NodeType
117 Parent *Node
118 FirstChild *Node
119 LastChild *Node
120 Prev *Node
121 Next *Node
122
123 Literal []byte
124
125 HeadingData
126 ListData
127 CodeBlockData
128 LinkData
129 TableCellData
130
131 content []byte
132 open bool
133 }
134
135
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
154
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
172
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
187
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
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
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
267
268
269 type WalkStatus int
270
271 const (
272
273 GoToNext WalkStatus = iota
274
275 SkipChildren
276
277 Terminate
278 )
279
280
281
282
283 type NodeVisitor func(node *Node, entering bool) WalkStatus
284
285
286
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