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}