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