1package ast
2
3import (
4 "fmt"
5 "strings"
6
7 textm "github.com/yuin/goldmark/text"
8)
9
10// A BaseBlock struct implements the Node interface partialliy.
11type BaseBlock struct {
12 BaseNode
13 blankPreviousLines bool
14 lines *textm.Segments
15}
16
17// Type implements Node.Type.
18func (b *BaseBlock) Type() NodeType {
19 return TypeBlock
20}
21
22// IsRaw implements Node.IsRaw.
23func (b *BaseBlock) IsRaw() bool {
24 return false
25}
26
27// HasBlankPreviousLines implements Node.HasBlankPreviousLines.
28func (b *BaseBlock) HasBlankPreviousLines() bool {
29 return b.blankPreviousLines
30}
31
32// SetBlankPreviousLines implements Node.SetBlankPreviousLines.
33func (b *BaseBlock) SetBlankPreviousLines(v bool) {
34 b.blankPreviousLines = v
35}
36
37// Lines implements Node.Lines.
38func (b *BaseBlock) Lines() *textm.Segments {
39 if b.lines == nil {
40 b.lines = textm.NewSegments()
41 }
42 return b.lines
43}
44
45// SetLines implements Node.SetLines.
46func (b *BaseBlock) SetLines(v *textm.Segments) {
47 b.lines = v
48}
49
50// A Document struct is a root node of Markdown text.
51type Document struct {
52 BaseBlock
53
54 meta map[string]interface{}
55}
56
57// KindDocument is a NodeKind of the Document node.
58var KindDocument = NewNodeKind("Document")
59
60// Dump implements Node.Dump .
61func (n *Document) Dump(source []byte, level int) {
62 DumpHelper(n, source, level, nil, nil)
63}
64
65// Type implements Node.Type .
66func (n *Document) Type() NodeType {
67 return TypeDocument
68}
69
70// Kind implements Node.Kind.
71func (n *Document) Kind() NodeKind {
72 return KindDocument
73}
74
75// OwnerDocument implements Node.OwnerDocument.
76func (n *Document) OwnerDocument() *Document {
77 return n
78}
79
80// Meta returns metadata of this document.
81func (n *Document) Meta() map[string]interface{} {
82 if n.meta == nil {
83 n.meta = map[string]interface{}{}
84 }
85 return n.meta
86}
87
88// SetMeta sets given metadata to this document.
89func (n *Document) SetMeta(meta map[string]interface{}) {
90 if n.meta == nil {
91 n.meta = map[string]interface{}{}
92 }
93 for k, v := range meta {
94 n.meta[k] = v
95 }
96}
97
98// AddMeta adds given metadata to this document.
99func (n *Document) AddMeta(key string, value interface{}) {
100 if n.meta == nil {
101 n.meta = map[string]interface{}{}
102 }
103 n.meta[key] = value
104}
105
106// NewDocument returns a new Document node.
107func NewDocument() *Document {
108 return &Document{
109 BaseBlock: BaseBlock{},
110 meta: nil,
111 }
112}
113
114// A TextBlock struct is a node whose lines
115// should be rendered without any containers.
116type TextBlock struct {
117 BaseBlock
118}
119
120// Dump implements Node.Dump .
121func (n *TextBlock) Dump(source []byte, level int) {
122 DumpHelper(n, source, level, nil, nil)
123}
124
125// KindTextBlock is a NodeKind of the TextBlock node.
126var KindTextBlock = NewNodeKind("TextBlock")
127
128// Kind implements Node.Kind.
129func (n *TextBlock) Kind() NodeKind {
130 return KindTextBlock
131}
132
133// Text implements Node.Text.
134//
135// Deprecated: Use other properties of the node to get the text value(i.e. TextBlock.Lines).
136func (n *TextBlock) Text(source []byte) []byte {
137 return n.Lines().Value(source)
138}
139
140// NewTextBlock returns a new TextBlock node.
141func NewTextBlock() *TextBlock {
142 return &TextBlock{
143 BaseBlock: BaseBlock{},
144 }
145}
146
147// A Paragraph struct represents a paragraph of Markdown text.
148type Paragraph struct {
149 BaseBlock
150}
151
152// Dump implements Node.Dump .
153func (n *Paragraph) Dump(source []byte, level int) {
154 DumpHelper(n, source, level, nil, nil)
155}
156
157// KindParagraph is a NodeKind of the Paragraph node.
158var KindParagraph = NewNodeKind("Paragraph")
159
160// Kind implements Node.Kind.
161func (n *Paragraph) Kind() NodeKind {
162 return KindParagraph
163}
164
165// Text implements Node.Text.
166//
167// Deprecated: Use other properties of the node to get the text value(i.e. Paragraph.Lines).
168func (n *Paragraph) Text(source []byte) []byte {
169 return n.Lines().Value(source)
170}
171
172// NewParagraph returns a new Paragraph node.
173func NewParagraph() *Paragraph {
174 return &Paragraph{
175 BaseBlock: BaseBlock{},
176 }
177}
178
179// IsParagraph returns true if the given node implements the Paragraph interface,
180// otherwise false.
181func IsParagraph(node Node) bool {
182 _, ok := node.(*Paragraph)
183 return ok
184}
185
186// A Heading struct represents headings like SetextHeading and ATXHeading.
187type Heading struct {
188 BaseBlock
189 // Level returns a level of this heading.
190 // This value is between 1 and 6.
191 Level int
192}
193
194// Dump implements Node.Dump .
195func (n *Heading) Dump(source []byte, level int) {
196 m := map[string]string{
197 "Level": fmt.Sprintf("%d", n.Level),
198 }
199 DumpHelper(n, source, level, m, nil)
200}
201
202// KindHeading is a NodeKind of the Heading node.
203var KindHeading = NewNodeKind("Heading")
204
205// Kind implements Node.Kind.
206func (n *Heading) Kind() NodeKind {
207 return KindHeading
208}
209
210// NewHeading returns a new Heading node.
211func NewHeading(level int) *Heading {
212 return &Heading{
213 BaseBlock: BaseBlock{},
214 Level: level,
215 }
216}
217
218// A ThematicBreak struct represents a thematic break of Markdown text.
219type ThematicBreak struct {
220 BaseBlock
221}
222
223// Dump implements Node.Dump .
224func (n *ThematicBreak) Dump(source []byte, level int) {
225 DumpHelper(n, source, level, nil, nil)
226}
227
228// KindThematicBreak is a NodeKind of the ThematicBreak node.
229var KindThematicBreak = NewNodeKind("ThematicBreak")
230
231// Kind implements Node.Kind.
232func (n *ThematicBreak) Kind() NodeKind {
233 return KindThematicBreak
234}
235
236// NewThematicBreak returns a new ThematicBreak node.
237func NewThematicBreak() *ThematicBreak {
238 return &ThematicBreak{
239 BaseBlock: BaseBlock{},
240 }
241}
242
243// A CodeBlock interface represents an indented code block of Markdown text.
244type CodeBlock struct {
245 BaseBlock
246}
247
248// IsRaw implements Node.IsRaw.
249func (n *CodeBlock) IsRaw() bool {
250 return true
251}
252
253// Dump implements Node.Dump .
254func (n *CodeBlock) Dump(source []byte, level int) {
255 DumpHelper(n, source, level, nil, nil)
256}
257
258// KindCodeBlock is a NodeKind of the CodeBlock node.
259var KindCodeBlock = NewNodeKind("CodeBlock")
260
261// Kind implements Node.Kind.
262func (n *CodeBlock) Kind() NodeKind {
263 return KindCodeBlock
264}
265
266// Text implements Node.Text.
267//
268// Deprecated: Use other properties of the node to get the text value(i.e. CodeBlock.Lines).
269func (n *CodeBlock) Text(source []byte) []byte {
270 return n.Lines().Value(source)
271}
272
273// NewCodeBlock returns a new CodeBlock node.
274func NewCodeBlock() *CodeBlock {
275 return &CodeBlock{
276 BaseBlock: BaseBlock{},
277 }
278}
279
280// A FencedCodeBlock struct represents a fenced code block of Markdown text.
281type FencedCodeBlock struct {
282 BaseBlock
283 // Info returns a info text of this fenced code block.
284 Info *Text
285
286 language []byte
287}
288
289// Language returns an language in an info string.
290// Language returns nil if this node does not have an info string.
291func (n *FencedCodeBlock) Language(source []byte) []byte {
292 if n.language == nil && n.Info != nil {
293 segment := n.Info.Segment
294 info := segment.Value(source)
295 i := 0
296 for ; i < len(info); i++ {
297 if info[i] == ' ' {
298 break
299 }
300 }
301 n.language = info[:i]
302 }
303 return n.language
304}
305
306// IsRaw implements Node.IsRaw.
307func (n *FencedCodeBlock) IsRaw() bool {
308 return true
309}
310
311// Dump implements Node.Dump .
312func (n *FencedCodeBlock) Dump(source []byte, level int) {
313 m := map[string]string{}
314 if n.Info != nil {
315 m["Info"] = fmt.Sprintf("\"%s\"", n.Info.Text(source))
316 }
317 DumpHelper(n, source, level, m, nil)
318}
319
320// KindFencedCodeBlock is a NodeKind of the FencedCodeBlock node.
321var KindFencedCodeBlock = NewNodeKind("FencedCodeBlock")
322
323// Kind implements Node.Kind.
324func (n *FencedCodeBlock) Kind() NodeKind {
325 return KindFencedCodeBlock
326}
327
328// Text implements Node.Text.
329//
330// Deprecated: Use other properties of the node to get the text value(i.e. FencedCodeBlock.Lines).
331func (n *FencedCodeBlock) Text(source []byte) []byte {
332 return n.Lines().Value(source)
333}
334
335// NewFencedCodeBlock return a new FencedCodeBlock node.
336func NewFencedCodeBlock(info *Text) *FencedCodeBlock {
337 return &FencedCodeBlock{
338 BaseBlock: BaseBlock{},
339 Info: info,
340 }
341}
342
343// A Blockquote struct represents an blockquote block of Markdown text.
344type Blockquote struct {
345 BaseBlock
346}
347
348// Dump implements Node.Dump .
349func (n *Blockquote) Dump(source []byte, level int) {
350 DumpHelper(n, source, level, nil, nil)
351}
352
353// KindBlockquote is a NodeKind of the Blockquote node.
354var KindBlockquote = NewNodeKind("Blockquote")
355
356// Kind implements Node.Kind.
357func (n *Blockquote) Kind() NodeKind {
358 return KindBlockquote
359}
360
361// NewBlockquote returns a new Blockquote node.
362func NewBlockquote() *Blockquote {
363 return &Blockquote{
364 BaseBlock: BaseBlock{},
365 }
366}
367
368// A List struct represents a list of Markdown text.
369type List struct {
370 BaseBlock
371
372 // Marker is a marker character like '-', '+', ')' and '.'.
373 Marker byte
374
375 // IsTight is a true if this list is a 'tight' list.
376 // See https://spec.commonmark.org/0.30/#loose for details.
377 IsTight bool
378
379 // Start is an initial number of this ordered list.
380 // If this list is not an ordered list, Start is 0.
381 Start int
382}
383
384// IsOrdered returns true if this list is an ordered list, otherwise false.
385func (l *List) IsOrdered() bool {
386 return l.Marker == '.' || l.Marker == ')'
387}
388
389// CanContinue returns true if this list can continue with
390// the given mark and a list type, otherwise false.
391func (l *List) CanContinue(marker byte, isOrdered bool) bool {
392 return marker == l.Marker && isOrdered == l.IsOrdered()
393}
394
395// Dump implements Node.Dump.
396func (l *List) Dump(source []byte, level int) {
397 m := map[string]string{
398 "Ordered": fmt.Sprintf("%v", l.IsOrdered()),
399 "Marker": fmt.Sprintf("%c", l.Marker),
400 "Tight": fmt.Sprintf("%v", l.IsTight),
401 }
402 if l.IsOrdered() {
403 m["Start"] = fmt.Sprintf("%d", l.Start)
404 }
405 DumpHelper(l, source, level, m, nil)
406}
407
408// KindList is a NodeKind of the List node.
409var KindList = NewNodeKind("List")
410
411// Kind implements Node.Kind.
412func (l *List) Kind() NodeKind {
413 return KindList
414}
415
416// NewList returns a new List node.
417func NewList(marker byte) *List {
418 return &List{
419 BaseBlock: BaseBlock{},
420 Marker: marker,
421 IsTight: true,
422 }
423}
424
425// A ListItem struct represents a list item of Markdown text.
426type ListItem struct {
427 BaseBlock
428
429 // Offset is an offset position of this item.
430 Offset int
431}
432
433// Dump implements Node.Dump.
434func (n *ListItem) Dump(source []byte, level int) {
435 m := map[string]string{
436 "Offset": fmt.Sprintf("%d", n.Offset),
437 }
438 DumpHelper(n, source, level, m, nil)
439}
440
441// KindListItem is a NodeKind of the ListItem node.
442var KindListItem = NewNodeKind("ListItem")
443
444// Kind implements Node.Kind.
445func (n *ListItem) Kind() NodeKind {
446 return KindListItem
447}
448
449// NewListItem returns a new ListItem node.
450func NewListItem(offset int) *ListItem {
451 return &ListItem{
452 BaseBlock: BaseBlock{},
453 Offset: offset,
454 }
455}
456
457// HTMLBlockType represents kinds of an html blocks.
458// See https://spec.commonmark.org/0.30/#html-blocks
459type HTMLBlockType int
460
461const (
462 // HTMLBlockType1 represents type 1 html blocks.
463 HTMLBlockType1 HTMLBlockType = iota + 1
464 // HTMLBlockType2 represents type 2 html blocks.
465 HTMLBlockType2
466 // HTMLBlockType3 represents type 3 html blocks.
467 HTMLBlockType3
468 // HTMLBlockType4 represents type 4 html blocks.
469 HTMLBlockType4
470 // HTMLBlockType5 represents type 5 html blocks.
471 HTMLBlockType5
472 // HTMLBlockType6 represents type 6 html blocks.
473 HTMLBlockType6
474 // HTMLBlockType7 represents type 7 html blocks.
475 HTMLBlockType7
476)
477
478// An HTMLBlock struct represents an html block of Markdown text.
479type HTMLBlock struct {
480 BaseBlock
481
482 // Type is a type of this html block.
483 HTMLBlockType HTMLBlockType
484
485 // ClosureLine is a line that closes this html block.
486 ClosureLine textm.Segment
487}
488
489// IsRaw implements Node.IsRaw.
490func (n *HTMLBlock) IsRaw() bool {
491 return true
492}
493
494// HasClosure returns true if this html block has a closure line,
495// otherwise false.
496func (n *HTMLBlock) HasClosure() bool {
497 return n.ClosureLine.Start >= 0
498}
499
500// Dump implements Node.Dump.
501func (n *HTMLBlock) Dump(source []byte, level int) {
502 indent := strings.Repeat(" ", level)
503 fmt.Printf("%s%s {\n", indent, "HTMLBlock")
504 indent2 := strings.Repeat(" ", level+1)
505 fmt.Printf("%sRawText: \"", indent2)
506 for i := 0; i < n.Lines().Len(); i++ {
507 s := n.Lines().At(i)
508 fmt.Print(string(source[s.Start:s.Stop]))
509 }
510 fmt.Printf("\"\n")
511 for c := n.FirstChild(); c != nil; c = c.NextSibling() {
512 c.Dump(source, level+1)
513 }
514 if n.HasClosure() {
515 cl := n.ClosureLine
516 fmt.Printf("%sClosure: \"%s\"\n", indent2, string(cl.Value(source)))
517 }
518 fmt.Printf("%s}\n", indent)
519}
520
521// KindHTMLBlock is a NodeKind of the HTMLBlock node.
522var KindHTMLBlock = NewNodeKind("HTMLBlock")
523
524// Kind implements Node.Kind.
525func (n *HTMLBlock) Kind() NodeKind {
526 return KindHTMLBlock
527}
528
529// Text implements Node.Text.
530//
531// Deprecated: Use other properties of the node to get the text value(i.e. HTMLBlock.Lines).
532func (n *HTMLBlock) Text(source []byte) []byte {
533 ret := n.Lines().Value(source)
534 if n.HasClosure() {
535 ret = append(ret, n.ClosureLine.Value(source)...)
536 }
537 return ret
538}
539
540// NewHTMLBlock returns a new HTMLBlock node.
541func NewHTMLBlock(typ HTMLBlockType) *HTMLBlock {
542 return &HTMLBlock{
543 BaseBlock: BaseBlock{},
544 HTMLBlockType: typ,
545 ClosureLine: textm.NewSegment(-1, -1),
546 }
547}