1package ast
  2
  3import (
  4	"fmt"
  5	"strings"
  6
  7	textm "github.com/yuin/goldmark/text"
  8	"github.com/yuin/goldmark/util"
  9)
 10
 11// A BaseInline struct implements the Node interface partialliy.
 12type BaseInline struct {
 13	BaseNode
 14}
 15
 16// Type implements Node.Type.
 17func (b *BaseInline) Type() NodeType {
 18	return TypeInline
 19}
 20
 21// IsRaw implements Node.IsRaw.
 22func (b *BaseInline) IsRaw() bool {
 23	return false
 24}
 25
 26// HasBlankPreviousLines implements Node.HasBlankPreviousLines.
 27func (b *BaseInline) HasBlankPreviousLines() bool {
 28	panic("can not call with inline nodes.")
 29}
 30
 31// SetBlankPreviousLines implements Node.SetBlankPreviousLines.
 32func (b *BaseInline) SetBlankPreviousLines(v bool) {
 33	panic("can not call with inline nodes.")
 34}
 35
 36// Lines implements Node.Lines.
 37func (b *BaseInline) Lines() *textm.Segments {
 38	panic("can not call with inline nodes.")
 39}
 40
 41// SetLines implements Node.SetLines.
 42func (b *BaseInline) SetLines(v *textm.Segments) {
 43	panic("can not call with inline nodes.")
 44}
 45
 46// A Text struct represents a textual content of the Markdown text.
 47type Text struct {
 48	BaseInline
 49	// Segment is a position in a source text.
 50	Segment textm.Segment
 51
 52	flags uint8
 53}
 54
 55const (
 56	textSoftLineBreak = 1 << iota
 57	textHardLineBreak
 58	textRaw
 59	textCode
 60)
 61
 62func textFlagsString(flags uint8) string {
 63	buf := []string{}
 64	if flags&textSoftLineBreak != 0 {
 65		buf = append(buf, "SoftLineBreak")
 66	}
 67	if flags&textHardLineBreak != 0 {
 68		buf = append(buf, "HardLineBreak")
 69	}
 70	if flags&textRaw != 0 {
 71		buf = append(buf, "Raw")
 72	}
 73	if flags&textCode != 0 {
 74		buf = append(buf, "Code")
 75	}
 76	return strings.Join(buf, ", ")
 77}
 78
 79// Inline implements Inline.Inline.
 80func (n *Text) Inline() {
 81}
 82
 83// Pos implements Node.Pos.
 84func (n *Text) Pos() int {
 85	return n.Segment.Start
 86}
 87
 88// SoftLineBreak returns true if this node ends with a new line,
 89// otherwise false.
 90func (n *Text) SoftLineBreak() bool {
 91	return n.flags&textSoftLineBreak != 0
 92}
 93
 94// SetSoftLineBreak sets whether this node ends with a new line.
 95func (n *Text) SetSoftLineBreak(v bool) {
 96	if v {
 97		n.flags |= textSoftLineBreak
 98	} else {
 99		n.flags = n.flags &^ textSoftLineBreak
100	}
101}
102
103// IsRaw returns true if this text should be rendered without unescaping
104// back slash escapes and resolving references.
105func (n *Text) IsRaw() bool {
106	return n.flags&textRaw != 0
107}
108
109// SetRaw sets whether this text should be rendered as raw contents.
110func (n *Text) SetRaw(v bool) {
111	if v {
112		n.flags |= textRaw
113	} else {
114		n.flags = n.flags &^ textRaw
115	}
116}
117
118// HardLineBreak returns true if this node ends with a hard line break.
119// See https://spec.commonmark.org/0.30/#hard-line-breaks for details.
120func (n *Text) HardLineBreak() bool {
121	return n.flags&textHardLineBreak != 0
122}
123
124// SetHardLineBreak sets whether this node ends with a hard line break.
125func (n *Text) SetHardLineBreak(v bool) {
126	if v {
127		n.flags |= textHardLineBreak
128	} else {
129		n.flags = n.flags &^ textHardLineBreak
130	}
131}
132
133// Merge merges a Node n into this node.
134// Merge returns true if the given node has been merged, otherwise false.
135func (n *Text) Merge(node Node, source []byte) bool {
136	t, ok := node.(*Text)
137	if !ok {
138		return false
139	}
140	if n.Segment.Stop != t.Segment.Start || t.Segment.Padding != 0 ||
141		source[n.Segment.Stop-1] == '\n' || t.IsRaw() != n.IsRaw() {
142		return false
143	}
144	n.Segment.Stop = t.Segment.Stop
145	n.SetSoftLineBreak(t.SoftLineBreak())
146	n.SetHardLineBreak(t.HardLineBreak())
147	return true
148}
149
150// Text implements Node.Text.
151//
152// Deprecated: Use other properties of the node to get the text value(i.e. Text.Value).
153func (n *Text) Text(source []byte) []byte {
154	return n.Segment.Value(source)
155}
156
157// Value returns a value of this node.
158// SoftLineBreaks are not included in the returned value.
159func (n *Text) Value(source []byte) []byte {
160	return n.Segment.Value(source)
161}
162
163// Dump implements Node.Dump.
164func (n *Text) Dump(source []byte, level int) {
165	m := map[string]string{
166		"Value": "\"" + strings.TrimRight(string(n.Value(source)), "\n") + "\"",
167	}
168	fs := textFlagsString(n.flags)
169	if len(fs) != 0 {
170		m["Flags"] = fs
171	}
172	DumpHelper(n, source, level, m, nil)
173}
174
175// KindText is a NodeKind of the Text node.
176var KindText = NewNodeKind("Text")
177
178// Kind implements Node.Kind.
179func (n *Text) Kind() NodeKind {
180	return KindText
181}
182
183// NewText returns a new Text node.
184func NewText() *Text {
185	return &Text{
186		BaseInline: BaseInline{},
187	}
188}
189
190// NewTextSegment returns a new Text node with the given source position.
191func NewTextSegment(v textm.Segment) *Text {
192	return &Text{
193		BaseInline: BaseInline{},
194		Segment:    v,
195	}
196}
197
198// NewRawTextSegment returns a new Text node with the given source position.
199// The new node should be rendered as raw contents.
200func NewRawTextSegment(v textm.Segment) *Text {
201	t := &Text{
202		BaseInline: BaseInline{},
203		Segment:    v,
204	}
205	t.SetRaw(true)
206	return t
207}
208
209// MergeOrAppendTextSegment merges a given s into the last child of the parent if
210// it can be merged, otherwise creates a new Text node and appends it to after current
211// last child.
212func MergeOrAppendTextSegment(parent Node, s textm.Segment) {
213	last := parent.LastChild()
214	t, ok := last.(*Text)
215	if ok && t.Segment.Stop == s.Start && !t.SoftLineBreak() {
216		t.Segment = t.Segment.WithStop(s.Stop)
217	} else {
218		parent.AppendChild(parent, NewTextSegment(s))
219	}
220}
221
222// MergeOrReplaceTextSegment merges a given s into a previous sibling of the node n
223// if a previous sibling of the node n is *Text, otherwise replaces Node n with s.
224func MergeOrReplaceTextSegment(parent Node, n Node, s textm.Segment) {
225	prev := n.PreviousSibling()
226	if t, ok := prev.(*Text); ok && t.Segment.Stop == s.Start && !t.SoftLineBreak() {
227		t.Segment = t.Segment.WithStop(s.Stop)
228		parent.RemoveChild(parent, n)
229	} else {
230		parent.ReplaceChild(parent, n, NewTextSegment(s))
231	}
232}
233
234// A String struct is a textual content that has a concrete value.
235type String struct {
236	BaseInline
237
238	Value []byte
239	flags uint8
240}
241
242// Inline implements Inline.Inline.
243func (n *String) Inline() {
244}
245
246// Pos implements Node.Pos.
247// String node does not have a position because it is not associated with a source text.
248func (n *String) Pos() int {
249	return -1
250}
251
252// IsRaw returns true if this text should be rendered without unescaping
253// back slash escapes and resolving references.
254func (n *String) IsRaw() bool {
255	return n.flags&textRaw != 0
256}
257
258// SetRaw sets whether this text should be rendered as raw contents.
259func (n *String) SetRaw(v bool) {
260	if v {
261		n.flags |= textRaw
262	} else {
263		n.flags = n.flags &^ textRaw
264	}
265}
266
267// IsCode returns true if this text should be rendered without any
268// modifications.
269func (n *String) IsCode() bool {
270	return n.flags&textCode != 0
271}
272
273// SetCode sets whether this text should be rendered without any modifications.
274func (n *String) SetCode(v bool) {
275	if v {
276		n.flags |= textCode
277	} else {
278		n.flags = n.flags &^ textCode
279	}
280}
281
282// Text implements Node.Text.
283//
284// Deprecated: Use other properties of the node to get the text value(i.e. String.Value).
285func (n *String) Text(source []byte) []byte {
286	return n.Value
287}
288
289// Dump implements Node.Dump.
290func (n *String) Dump(source []byte, level int) {
291	fs := textFlagsString(n.flags)
292	if len(fs) != 0 {
293		fs = "(" + fs + ")"
294	}
295	fmt.Printf("%sString%s: \"%s\"\n", strings.Repeat("    ", level), fs, strings.TrimRight(string(n.Value), "\n"))
296}
297
298// KindString is a NodeKind of the String node.
299var KindString = NewNodeKind("String")
300
301// Kind implements Node.Kind.
302func (n *String) Kind() NodeKind {
303	return KindString
304}
305
306// NewString returns a new String node.
307func NewString(v []byte) *String {
308	return &String{
309		Value: v,
310	}
311}
312
313// A CodeSpan struct represents a code span of Markdown text.
314type CodeSpan struct {
315	BaseInline
316}
317
318// Inline implements Inline.Inline .
319func (n *CodeSpan) Inline() {
320}
321
322// IsBlank returns true if this node consists of spaces, otherwise false.
323func (n *CodeSpan) IsBlank(source []byte) bool {
324	for c := n.FirstChild(); c != nil; c = c.NextSibling() {
325		text := c.(*Text).Segment
326		if !util.IsBlank(text.Value(source)) {
327			return false
328		}
329	}
330	return true
331}
332
333// Dump implements Node.Dump.
334func (n *CodeSpan) Dump(source []byte, level int) {
335	DumpHelper(n, source, level, nil, nil)
336}
337
338// KindCodeSpan is a NodeKind of the CodeSpan node.
339var KindCodeSpan = NewNodeKind("CodeSpan")
340
341// Kind implements Node.Kind.
342func (n *CodeSpan) Kind() NodeKind {
343	return KindCodeSpan
344}
345
346// NewCodeSpan returns a new CodeSpan node.
347func NewCodeSpan() *CodeSpan {
348	return &CodeSpan{
349		BaseInline: BaseInline{},
350	}
351}
352
353// An Emphasis struct represents an emphasis of Markdown text.
354type Emphasis struct {
355	BaseInline
356
357	// Level is a level of the emphasis.
358	Level int
359}
360
361// Dump implements Node.Dump.
362func (n *Emphasis) Dump(source []byte, level int) {
363	m := map[string]string{
364		"Level": fmt.Sprintf("%v", n.Level),
365	}
366	DumpHelper(n, source, level, m, nil)
367}
368
369// KindEmphasis is a NodeKind of the Emphasis node.
370var KindEmphasis = NewNodeKind("Emphasis")
371
372// Kind implements Node.Kind.
373func (n *Emphasis) Kind() NodeKind {
374	return KindEmphasis
375}
376
377// NewEmphasis returns a new Emphasis node with the given level.
378func NewEmphasis(level int) *Emphasis {
379	return &Emphasis{
380		BaseInline: BaseInline{},
381		Level:      level,
382	}
383}
384
385type baseLink struct {
386	BaseInline
387
388	// Destination is a destination(URL) of this link.
389	Destination []byte
390
391	// Title is a title of this link.
392	Title []byte
393
394	// Reference is a reference of this link. This field is used for reference links.
395	// If this link is not a reference link, this field is nil.
396	Reference *ReferenceLink
397}
398
399// Inline implements Inline.Inline.
400func (n *baseLink) Inline() {
401}
402
403// ReferenceLinkType defines a kind of reference link.
404type ReferenceLinkType int
405
406const (
407	// ReferenceLinkFull indicates that a reference link has a full reference like [foo][bar].
408	ReferenceLinkFull ReferenceLinkType = iota + 1
409	// ReferenceLinkCollapsed indicates that a reference link has a collapsed reference like [foo][].
410	ReferenceLinkCollapsed
411	// ReferenceLinkShortcut indicates that a reference link has a shortcut reference like [foo].
412	ReferenceLinkShortcut
413)
414
415// String returns a string representation of this reference link type.
416func (t ReferenceLinkType) String() string {
417	switch t {
418	case ReferenceLinkFull:
419		return "Full"
420	case ReferenceLinkCollapsed:
421		return "Collapsed"
422	case ReferenceLinkShortcut:
423		return "Shortcut"
424	default:
425		return fmt.Sprintf("Unknown(%d)", t)
426	}
427}
428
429// ReferenceLink struct represents a reference link of the Markdown text.
430type ReferenceLink struct {
431	// Type is a kind of this reference link.
432	Type ReferenceLinkType
433
434	// Value is a value of this reference link.
435	Value []byte
436}
437
438// NewReferenceLink returns a new ReferenceLink with the given type and value.
439func NewReferenceLink(typ ReferenceLinkType, value []byte) *ReferenceLink {
440	return &ReferenceLink{
441		Type:  typ,
442		Value: value,
443	}
444}
445
446// A Link struct represents a link of the Markdown text.
447type Link struct {
448	baseLink
449}
450
451// Dump implements Node.Dump.
452func (n *Link) Dump(source []byte, level int) {
453	m := map[string]string{}
454	m["Destination"] = string(n.Destination)
455	if len(n.Title) != 0 {
456		m["Title"] = string(n.Title)
457	}
458	cb := func(int) {}
459	if n.Reference != nil {
460		cb = func(level int) {
461			indent := strings.Repeat("    ", level)
462			fmt.Printf("%sReference {\n", indent)
463			indent2 := strings.Repeat("    ", level+1)
464			fmt.Printf("%sType : %s\n", indent2, n.Reference.Type.String())
465			fmt.Printf("%sValue : %s\n", indent2, string(n.Reference.Value))
466			fmt.Printf("%s}\n", indent)
467
468		}
469	}
470	DumpHelper(n, source, level, m, cb)
471}
472
473// KindLink is a NodeKind of the Link node.
474var KindLink = NewNodeKind("Link")
475
476// Kind implements Node.Kind.
477func (n *Link) Kind() NodeKind {
478	return KindLink
479}
480
481// NewLink returns a new Link node.
482func NewLink() *Link {
483	c := &Link{
484		baseLink: baseLink{
485			BaseInline: BaseInline{},
486		},
487	}
488	return c
489}
490
491// An Image struct represents an image of the Markdown text.
492type Image struct {
493	baseLink
494}
495
496// Dump implements Node.Dump.
497func (n *Image) Dump(source []byte, level int) {
498	m := map[string]string{}
499	m["Destination"] = string(n.Destination)
500	if len(n.Title) != 0 {
501		m["Title"] = string(n.Title)
502	}
503	cb := func(int) {}
504	if n.Reference != nil {
505		cb = func(level int) {
506			indent := strings.Repeat("    ", level)
507			fmt.Printf("%sReference {\n", indent)
508			indent2 := strings.Repeat("    ", level+1)
509			fmt.Printf("%sType : %s\n", indent2, n.Reference.Type.String())
510			fmt.Printf("%sValue : %s\n", indent2, string(n.Reference.Value))
511			fmt.Printf("%s}\n", indent)
512
513		}
514	}
515	DumpHelper(n, source, level, m, cb)
516}
517
518// KindImage is a NodeKind of the Image node.
519var KindImage = NewNodeKind("Image")
520
521// Kind implements Node.Kind.
522func (n *Image) Kind() NodeKind {
523	return KindImage
524}
525
526// NewImage returns a new Image node.
527func NewImage(link *Link) *Image {
528	c := &Image{
529		baseLink: baseLink{
530			BaseInline: BaseInline{},
531		},
532	}
533	c.Destination = link.Destination
534	c.Title = link.Title
535	c.Reference = link.Reference
536	for n := link.FirstChild(); n != nil; {
537		next := n.NextSibling()
538		link.RemoveChild(link, n)
539		c.AppendChild(c, n)
540		n = next
541	}
542
543	return c
544}
545
546// AutoLinkType defines kind of auto links.
547type AutoLinkType int
548
549const (
550	// AutoLinkEmail indicates that an autolink is an email address.
551	AutoLinkEmail AutoLinkType = iota + 1
552	// AutoLinkURL indicates that an autolink is a generic URL.
553	AutoLinkURL
554)
555
556// An AutoLink struct represents an autolink of the Markdown text.
557type AutoLink struct {
558	BaseInline
559	// Type is a type of this autolink.
560	AutoLinkType AutoLinkType
561
562	// Protocol specified a protocol of the link.
563	Protocol []byte
564
565	value *Text
566}
567
568// Inline implements Inline.Inline.
569func (n *AutoLink) Inline() {}
570
571// Dump implements Node.Dump.
572func (n *AutoLink) Dump(source []byte, level int) {
573	segment := n.value.Segment
574	m := map[string]string{
575		"Value": string(segment.Value(source)),
576	}
577	DumpHelper(n, source, level, m, nil)
578}
579
580// KindAutoLink is a NodeKind of the AutoLink node.
581var KindAutoLink = NewNodeKind("AutoLink")
582
583// Kind implements Node.Kind.
584func (n *AutoLink) Kind() NodeKind {
585	return KindAutoLink
586}
587
588// URL returns an url of this node.
589func (n *AutoLink) URL(source []byte) []byte {
590	if n.Protocol != nil {
591		s := n.value.Segment
592		ret := make([]byte, 0, len(n.Protocol)+s.Len()+3)
593		ret = append(ret, n.Protocol...)
594		ret = append(ret, ':', '/', '/')
595		ret = append(ret, n.value.Value(source)...)
596		return ret
597	}
598	return n.value.Value(source)
599}
600
601// Label returns a label of this node.
602func (n *AutoLink) Label(source []byte) []byte {
603	return n.value.Value(source)
604}
605
606// Text implements Node.Text.
607//
608// Deprecated: Use other properties of the node to get the text value(i.e. AutoLink.Label).
609func (n *AutoLink) Text(source []byte) []byte {
610	return n.value.Value(source)
611}
612
613// NewAutoLink returns a new AutoLink node.
614func NewAutoLink(typ AutoLinkType, value *Text) *AutoLink {
615	return &AutoLink{
616		BaseInline:   BaseInline{},
617		value:        value,
618		AutoLinkType: typ,
619	}
620}
621
622// A RawHTML struct represents an inline raw HTML of the Markdown text.
623type RawHTML struct {
624	BaseInline
625	Segments *textm.Segments
626}
627
628// Inline implements Inline.Inline.
629func (n *RawHTML) Inline() {}
630
631// Dump implements Node.Dump.
632func (n *RawHTML) Dump(source []byte, level int) {
633	m := map[string]string{}
634	t := []string{}
635	for i := range n.Segments.Len() {
636		segment := n.Segments.At(i)
637		t = append(t, string(segment.Value(source)))
638	}
639	m["RawText"] = strings.Join(t, "")
640	DumpHelper(n, source, level, m, nil)
641}
642
643// KindRawHTML is a NodeKind of the RawHTML node.
644var KindRawHTML = NewNodeKind("RawHTML")
645
646// Kind implements Node.Kind.
647func (n *RawHTML) Kind() NodeKind {
648	return KindRawHTML
649}
650
651// Text implements Node.Text.
652//
653// Deprecated: Use other properties of the node to get the text value(i.e. RawHTML.Segments).
654func (n *RawHTML) Text(source []byte) []byte {
655	return n.Segments.Value(source)
656}
657
658// NewRawHTML returns a new RawHTML node.
659func NewRawHTML() *RawHTML {
660	return &RawHTML{
661		Segments: textm.NewSegments(),
662	}
663}