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// NewTextBlock returns a new TextBlock node.
134func NewTextBlock() *TextBlock {
135	return &TextBlock{
136		BaseBlock: BaseBlock{},
137	}
138}
139
140// A Paragraph struct represents a paragraph of Markdown text.
141type Paragraph struct {
142	BaseBlock
143}
144
145// Dump implements Node.Dump .
146func (n *Paragraph) Dump(source []byte, level int) {
147	DumpHelper(n, source, level, nil, nil)
148}
149
150// KindParagraph is a NodeKind of the Paragraph node.
151var KindParagraph = NewNodeKind("Paragraph")
152
153// Kind implements Node.Kind.
154func (n *Paragraph) Kind() NodeKind {
155	return KindParagraph
156}
157
158// NewParagraph returns a new Paragraph node.
159func NewParagraph() *Paragraph {
160	return &Paragraph{
161		BaseBlock: BaseBlock{},
162	}
163}
164
165// IsParagraph returns true if the given node implements the Paragraph interface,
166// otherwise false.
167func IsParagraph(node Node) bool {
168	_, ok := node.(*Paragraph)
169	return ok
170}
171
172// A Heading struct represents headings like SetextHeading and ATXHeading.
173type Heading struct {
174	BaseBlock
175	// Level returns a level of this heading.
176	// This value is between 1 and 6.
177	Level int
178}
179
180// Dump implements Node.Dump .
181func (n *Heading) Dump(source []byte, level int) {
182	m := map[string]string{
183		"Level": fmt.Sprintf("%d", n.Level),
184	}
185	DumpHelper(n, source, level, m, nil)
186}
187
188// KindHeading is a NodeKind of the Heading node.
189var KindHeading = NewNodeKind("Heading")
190
191// Kind implements Node.Kind.
192func (n *Heading) Kind() NodeKind {
193	return KindHeading
194}
195
196// NewHeading returns a new Heading node.
197func NewHeading(level int) *Heading {
198	return &Heading{
199		BaseBlock: BaseBlock{},
200		Level:     level,
201	}
202}
203
204// A ThematicBreak struct represents a thematic break of Markdown text.
205type ThematicBreak struct {
206	BaseBlock
207}
208
209// Dump implements Node.Dump .
210func (n *ThematicBreak) Dump(source []byte, level int) {
211	DumpHelper(n, source, level, nil, nil)
212}
213
214// KindThematicBreak is a NodeKind of the ThematicBreak node.
215var KindThematicBreak = NewNodeKind("ThematicBreak")
216
217// Kind implements Node.Kind.
218func (n *ThematicBreak) Kind() NodeKind {
219	return KindThematicBreak
220}
221
222// NewThematicBreak returns a new ThematicBreak node.
223func NewThematicBreak() *ThematicBreak {
224	return &ThematicBreak{
225		BaseBlock: BaseBlock{},
226	}
227}
228
229// A CodeBlock interface represents an indented code block of Markdown text.
230type CodeBlock struct {
231	BaseBlock
232}
233
234// IsRaw implements Node.IsRaw.
235func (n *CodeBlock) IsRaw() bool {
236	return true
237}
238
239// Dump implements Node.Dump .
240func (n *CodeBlock) Dump(source []byte, level int) {
241	DumpHelper(n, source, level, nil, nil)
242}
243
244// KindCodeBlock is a NodeKind of the CodeBlock node.
245var KindCodeBlock = NewNodeKind("CodeBlock")
246
247// Kind implements Node.Kind.
248func (n *CodeBlock) Kind() NodeKind {
249	return KindCodeBlock
250}
251
252// NewCodeBlock returns a new CodeBlock node.
253func NewCodeBlock() *CodeBlock {
254	return &CodeBlock{
255		BaseBlock: BaseBlock{},
256	}
257}
258
259// A FencedCodeBlock struct represents a fenced code block of Markdown text.
260type FencedCodeBlock struct {
261	BaseBlock
262	// Info returns a info text of this fenced code block.
263	Info *Text
264
265	language []byte
266}
267
268// Language returns an language in an info string.
269// Language returns nil if this node does not have an info string.
270func (n *FencedCodeBlock) Language(source []byte) []byte {
271	if n.language == nil && n.Info != nil {
272		segment := n.Info.Segment
273		info := segment.Value(source)
274		i := 0
275		for ; i < len(info); i++ {
276			if info[i] == ' ' {
277				break
278			}
279		}
280		n.language = info[:i]
281	}
282	return n.language
283}
284
285// IsRaw implements Node.IsRaw.
286func (n *FencedCodeBlock) IsRaw() bool {
287	return true
288}
289
290// Dump implements Node.Dump .
291func (n *FencedCodeBlock) Dump(source []byte, level int) {
292	m := map[string]string{}
293	if n.Info != nil {
294		m["Info"] = fmt.Sprintf("\"%s\"", n.Info.Text(source))
295	}
296	DumpHelper(n, source, level, m, nil)
297}
298
299// KindFencedCodeBlock is a NodeKind of the FencedCodeBlock node.
300var KindFencedCodeBlock = NewNodeKind("FencedCodeBlock")
301
302// Kind implements Node.Kind.
303func (n *FencedCodeBlock) Kind() NodeKind {
304	return KindFencedCodeBlock
305}
306
307// NewFencedCodeBlock return a new FencedCodeBlock node.
308func NewFencedCodeBlock(info *Text) *FencedCodeBlock {
309	return &FencedCodeBlock{
310		BaseBlock: BaseBlock{},
311		Info:      info,
312	}
313}
314
315// A Blockquote struct represents an blockquote block of Markdown text.
316type Blockquote struct {
317	BaseBlock
318}
319
320// Dump implements Node.Dump .
321func (n *Blockquote) Dump(source []byte, level int) {
322	DumpHelper(n, source, level, nil, nil)
323}
324
325// KindBlockquote is a NodeKind of the Blockquote node.
326var KindBlockquote = NewNodeKind("Blockquote")
327
328// Kind implements Node.Kind.
329func (n *Blockquote) Kind() NodeKind {
330	return KindBlockquote
331}
332
333// NewBlockquote returns a new Blockquote node.
334func NewBlockquote() *Blockquote {
335	return &Blockquote{
336		BaseBlock: BaseBlock{},
337	}
338}
339
340// A List struct represents a list of Markdown text.
341type List struct {
342	BaseBlock
343
344	// Marker is a marker character like '-', '+', ')' and '.'.
345	Marker byte
346
347	// IsTight is a true if this list is a 'tight' list.
348	// See https://spec.commonmark.org/0.30/#loose for details.
349	IsTight bool
350
351	// Start is an initial number of this ordered list.
352	// If this list is not an ordered list, Start is 0.
353	Start int
354}
355
356// IsOrdered returns true if this list is an ordered list, otherwise false.
357func (l *List) IsOrdered() bool {
358	return l.Marker == '.' || l.Marker == ')'
359}
360
361// CanContinue returns true if this list can continue with
362// the given mark and a list type, otherwise false.
363func (l *List) CanContinue(marker byte, isOrdered bool) bool {
364	return marker == l.Marker && isOrdered == l.IsOrdered()
365}
366
367// Dump implements Node.Dump.
368func (l *List) Dump(source []byte, level int) {
369	m := map[string]string{
370		"Ordered": fmt.Sprintf("%v", l.IsOrdered()),
371		"Marker":  fmt.Sprintf("%c", l.Marker),
372		"Tight":   fmt.Sprintf("%v", l.IsTight),
373	}
374	if l.IsOrdered() {
375		m["Start"] = fmt.Sprintf("%d", l.Start)
376	}
377	DumpHelper(l, source, level, m, nil)
378}
379
380// KindList is a NodeKind of the List node.
381var KindList = NewNodeKind("List")
382
383// Kind implements Node.Kind.
384func (l *List) Kind() NodeKind {
385	return KindList
386}
387
388// NewList returns a new List node.
389func NewList(marker byte) *List {
390	return &List{
391		BaseBlock: BaseBlock{},
392		Marker:    marker,
393		IsTight:   true,
394	}
395}
396
397// A ListItem struct represents a list item of Markdown text.
398type ListItem struct {
399	BaseBlock
400
401	// Offset is an offset position of this item.
402	Offset int
403}
404
405// Dump implements Node.Dump.
406func (n *ListItem) Dump(source []byte, level int) {
407	m := map[string]string{
408		"Offset": fmt.Sprintf("%d", n.Offset),
409	}
410	DumpHelper(n, source, level, m, nil)
411}
412
413// KindListItem is a NodeKind of the ListItem node.
414var KindListItem = NewNodeKind("ListItem")
415
416// Kind implements Node.Kind.
417func (n *ListItem) Kind() NodeKind {
418	return KindListItem
419}
420
421// NewListItem returns a new ListItem node.
422func NewListItem(offset int) *ListItem {
423	return &ListItem{
424		BaseBlock: BaseBlock{},
425		Offset:    offset,
426	}
427}
428
429// HTMLBlockType represents kinds of an html blocks.
430// See https://spec.commonmark.org/0.30/#html-blocks
431type HTMLBlockType int
432
433const (
434	// HTMLBlockType1 represents type 1 html blocks
435	HTMLBlockType1 HTMLBlockType = iota + 1
436	// HTMLBlockType2 represents type 2 html blocks
437	HTMLBlockType2
438	// HTMLBlockType3 represents type 3 html blocks
439	HTMLBlockType3
440	// HTMLBlockType4 represents type 4 html blocks
441	HTMLBlockType4
442	// HTMLBlockType5 represents type 5 html blocks
443	HTMLBlockType5
444	// HTMLBlockType6 represents type 6 html blocks
445	HTMLBlockType6
446	// HTMLBlockType7 represents type 7 html blocks
447	HTMLBlockType7
448)
449
450// An HTMLBlock struct represents an html block of Markdown text.
451type HTMLBlock struct {
452	BaseBlock
453
454	// Type is a type of this html block.
455	HTMLBlockType HTMLBlockType
456
457	// ClosureLine is a line that closes this html block.
458	ClosureLine textm.Segment
459}
460
461// IsRaw implements Node.IsRaw.
462func (n *HTMLBlock) IsRaw() bool {
463	return true
464}
465
466// HasClosure returns true if this html block has a closure line,
467// otherwise false.
468func (n *HTMLBlock) HasClosure() bool {
469	return n.ClosureLine.Start >= 0
470}
471
472// Dump implements Node.Dump.
473func (n *HTMLBlock) Dump(source []byte, level int) {
474	indent := strings.Repeat("    ", level)
475	fmt.Printf("%s%s {\n", indent, "HTMLBlock")
476	indent2 := strings.Repeat("    ", level+1)
477	fmt.Printf("%sRawText: \"", indent2)
478	for i := 0; i < n.Lines().Len(); i++ {
479		s := n.Lines().At(i)
480		fmt.Print(string(source[s.Start:s.Stop]))
481	}
482	fmt.Printf("\"\n")
483	for c := n.FirstChild(); c != nil; c = c.NextSibling() {
484		c.Dump(source, level+1)
485	}
486	if n.HasClosure() {
487		cl := n.ClosureLine
488		fmt.Printf("%sClosure: \"%s\"\n", indent2, string(cl.Value(source)))
489	}
490	fmt.Printf("%s}\n", indent)
491}
492
493// KindHTMLBlock is a NodeKind of the HTMLBlock node.
494var KindHTMLBlock = NewNodeKind("HTMLBlock")
495
496// Kind implements Node.Kind.
497func (n *HTMLBlock) Kind() NodeKind {
498	return KindHTMLBlock
499}
500
501// NewHTMLBlock returns a new HTMLBlock node.
502func NewHTMLBlock(typ HTMLBlockType) *HTMLBlock {
503	return &HTMLBlock{
504		BaseBlock:     BaseBlock{},
505		HTMLBlockType: typ,
506		ClosureLine:   textm.NewSegment(-1, -1),
507	}
508}