block.go

  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}