Mercurial > yakumo_izuru > aya
comparison vendor/github.com/russross/blackfriday/v2/node.go @ 66:787b5ee0289d draft
Use vendored modules
Signed-off-by: Izuru Yakumo <yakumo.izuru@chaotic.ninja>
author | yakumo.izuru |
---|---|
date | Sun, 23 Jul 2023 13:18:53 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
65:6d985efa0f7a | 66:787b5ee0289d |
---|---|
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 } |