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// SoftLineBreak returns true if this node ends with a new line,
 84// otherwise false.
 85func (n *Text) SoftLineBreak() bool {
 86	return n.flags&textSoftLineBreak != 0
 87}
 88
 89// SetSoftLineBreak sets whether this node ends with a new line.
 90func (n *Text) SetSoftLineBreak(v bool) {
 91	if v {
 92		n.flags |= textSoftLineBreak
 93	} else {
 94		n.flags = n.flags &^ textSoftLineBreak
 95	}
 96}
 97
 98// IsRaw returns true if this text should be rendered without unescaping
 99// back slash escapes and resolving references.
100func (n *Text) IsRaw() bool {
101	return n.flags&textRaw != 0
102}
103
104// SetRaw sets whether this text should be rendered as raw contents.
105func (n *Text) SetRaw(v bool) {
106	if v {
107		n.flags |= textRaw
108	} else {
109		n.flags = n.flags &^ textRaw
110	}
111}
112
113// HardLineBreak returns true if this node ends with a hard line break.
114// See https://spec.commonmark.org/0.30/#hard-line-breaks for details.
115func (n *Text) HardLineBreak() bool {
116	return n.flags&textHardLineBreak != 0
117}
118
119// SetHardLineBreak sets whether this node ends with a hard line break.
120func (n *Text) SetHardLineBreak(v bool) {
121	if v {
122		n.flags |= textHardLineBreak
123	} else {
124		n.flags = n.flags &^ textHardLineBreak
125	}
126}
127
128// Merge merges a Node n into this node.
129// Merge returns true if the given node has been merged, otherwise false.
130func (n *Text) Merge(node Node, source []byte) bool {
131	t, ok := node.(*Text)
132	if !ok {
133		return false
134	}
135	if n.Segment.Stop != t.Segment.Start || t.Segment.Padding != 0 || source[n.Segment.Stop-1] == '\n' || t.IsRaw() != n.IsRaw() {
136		return false
137	}
138	n.Segment.Stop = t.Segment.Stop
139	n.SetSoftLineBreak(t.SoftLineBreak())
140	n.SetHardLineBreak(t.HardLineBreak())
141	return true
142}
143
144// Text implements Node.Text.
145func (n *Text) Text(source []byte) []byte {
146	return n.Segment.Value(source)
147}
148
149// Dump implements Node.Dump.
150func (n *Text) Dump(source []byte, level int) {
151	fs := textFlagsString(n.flags)
152	if len(fs) != 0 {
153		fs = "(" + fs + ")"
154	}
155	fmt.Printf("%sText%s: \"%s\"\n", strings.Repeat("    ", level), fs, strings.TrimRight(string(n.Text(source)), "\n"))
156}
157
158// KindText is a NodeKind of the Text node.
159var KindText = NewNodeKind("Text")
160
161// Kind implements Node.Kind.
162func (n *Text) Kind() NodeKind {
163	return KindText
164}
165
166// NewText returns a new Text node.
167func NewText() *Text {
168	return &Text{
169		BaseInline: BaseInline{},
170	}
171}
172
173// NewTextSegment returns a new Text node with the given source position.
174func NewTextSegment(v textm.Segment) *Text {
175	return &Text{
176		BaseInline: BaseInline{},
177		Segment:    v,
178	}
179}
180
181// NewRawTextSegment returns a new Text node with the given source position.
182// The new node should be rendered as raw contents.
183func NewRawTextSegment(v textm.Segment) *Text {
184	t := &Text{
185		BaseInline: BaseInline{},
186		Segment:    v,
187	}
188	t.SetRaw(true)
189	return t
190}
191
192// MergeOrAppendTextSegment merges a given s into the last child of the parent if
193// it can be merged, otherwise creates a new Text node and appends it to after current
194// last child.
195func MergeOrAppendTextSegment(parent Node, s textm.Segment) {
196	last := parent.LastChild()
197	t, ok := last.(*Text)
198	if ok && t.Segment.Stop == s.Start && !t.SoftLineBreak() {
199		t.Segment = t.Segment.WithStop(s.Stop)
200	} else {
201		parent.AppendChild(parent, NewTextSegment(s))
202	}
203}
204
205// MergeOrReplaceTextSegment merges a given s into a previous sibling of the node n
206// if a previous sibling of the node n is *Text, otherwise replaces Node n with s.
207func MergeOrReplaceTextSegment(parent Node, n Node, s textm.Segment) {
208	prev := n.PreviousSibling()
209	if t, ok := prev.(*Text); ok && t.Segment.Stop == s.Start && !t.SoftLineBreak() {
210		t.Segment = t.Segment.WithStop(s.Stop)
211		parent.RemoveChild(parent, n)
212	} else {
213		parent.ReplaceChild(parent, n, NewTextSegment(s))
214	}
215}
216
217// A String struct is a textual content that has a concrete value
218type String struct {
219	BaseInline
220
221	Value []byte
222	flags uint8
223}
224
225// Inline implements Inline.Inline.
226func (n *String) Inline() {
227}
228
229// IsRaw returns true if this text should be rendered without unescaping
230// back slash escapes and resolving references.
231func (n *String) IsRaw() bool {
232	return n.flags&textRaw != 0
233}
234
235// SetRaw sets whether this text should be rendered as raw contents.
236func (n *String) SetRaw(v bool) {
237	if v {
238		n.flags |= textRaw
239	} else {
240		n.flags = n.flags &^ textRaw
241	}
242}
243
244// IsCode returns true if this text should be rendered without any
245// modifications.
246func (n *String) IsCode() bool {
247	return n.flags&textCode != 0
248}
249
250// SetCode sets whether this text should be rendered without any modifications.
251func (n *String) SetCode(v bool) {
252	if v {
253		n.flags |= textCode
254	} else {
255		n.flags = n.flags &^ textCode
256	}
257}
258
259// Text implements Node.Text.
260func (n *String) Text(source []byte) []byte {
261	return n.Value
262}
263
264// Dump implements Node.Dump.
265func (n *String) Dump(source []byte, level int) {
266	fs := textFlagsString(n.flags)
267	if len(fs) != 0 {
268		fs = "(" + fs + ")"
269	}
270	fmt.Printf("%sString%s: \"%s\"\n", strings.Repeat("    ", level), fs, strings.TrimRight(string(n.Value), "\n"))
271}
272
273// KindString is a NodeKind of the String node.
274var KindString = NewNodeKind("String")
275
276// Kind implements Node.Kind.
277func (n *String) Kind() NodeKind {
278	return KindString
279}
280
281// NewString returns a new String node.
282func NewString(v []byte) *String {
283	return &String{
284		Value: v,
285	}
286}
287
288// A CodeSpan struct represents a code span of Markdown text.
289type CodeSpan struct {
290	BaseInline
291}
292
293// Inline implements Inline.Inline .
294func (n *CodeSpan) Inline() {
295}
296
297// IsBlank returns true if this node consists of spaces, otherwise false.
298func (n *CodeSpan) IsBlank(source []byte) bool {
299	for c := n.FirstChild(); c != nil; c = c.NextSibling() {
300		text := c.(*Text).Segment
301		if !util.IsBlank(text.Value(source)) {
302			return false
303		}
304	}
305	return true
306}
307
308// Dump implements Node.Dump
309func (n *CodeSpan) Dump(source []byte, level int) {
310	DumpHelper(n, source, level, nil, nil)
311}
312
313// KindCodeSpan is a NodeKind of the CodeSpan node.
314var KindCodeSpan = NewNodeKind("CodeSpan")
315
316// Kind implements Node.Kind.
317func (n *CodeSpan) Kind() NodeKind {
318	return KindCodeSpan
319}
320
321// NewCodeSpan returns a new CodeSpan node.
322func NewCodeSpan() *CodeSpan {
323	return &CodeSpan{
324		BaseInline: BaseInline{},
325	}
326}
327
328// An Emphasis struct represents an emphasis of Markdown text.
329type Emphasis struct {
330	BaseInline
331
332	// Level is a level of the emphasis.
333	Level int
334}
335
336// Dump implements Node.Dump.
337func (n *Emphasis) Dump(source []byte, level int) {
338	m := map[string]string{
339		"Level": fmt.Sprintf("%v", n.Level),
340	}
341	DumpHelper(n, source, level, m, nil)
342}
343
344// KindEmphasis is a NodeKind of the Emphasis node.
345var KindEmphasis = NewNodeKind("Emphasis")
346
347// Kind implements Node.Kind.
348func (n *Emphasis) Kind() NodeKind {
349	return KindEmphasis
350}
351
352// NewEmphasis returns a new Emphasis node with the given level.
353func NewEmphasis(level int) *Emphasis {
354	return &Emphasis{
355		BaseInline: BaseInline{},
356		Level:      level,
357	}
358}
359
360type baseLink struct {
361	BaseInline
362
363	// Destination is a destination(URL) of this link.
364	Destination []byte
365
366	// Title is a title of this link.
367	Title []byte
368}
369
370// Inline implements Inline.Inline.
371func (n *baseLink) Inline() {
372}
373
374// A Link struct represents a link of the Markdown text.
375type Link struct {
376	baseLink
377}
378
379// Dump implements Node.Dump.
380func (n *Link) Dump(source []byte, level int) {
381	m := map[string]string{}
382	m["Destination"] = string(n.Destination)
383	m["Title"] = string(n.Title)
384	DumpHelper(n, source, level, m, nil)
385}
386
387// KindLink is a NodeKind of the Link node.
388var KindLink = NewNodeKind("Link")
389
390// Kind implements Node.Kind.
391func (n *Link) Kind() NodeKind {
392	return KindLink
393}
394
395// NewLink returns a new Link node.
396func NewLink() *Link {
397	c := &Link{
398		baseLink: baseLink{
399			BaseInline: BaseInline{},
400		},
401	}
402	return c
403}
404
405// An Image struct represents an image of the Markdown text.
406type Image struct {
407	baseLink
408}
409
410// Dump implements Node.Dump.
411func (n *Image) Dump(source []byte, level int) {
412	m := map[string]string{}
413	m["Destination"] = string(n.Destination)
414	m["Title"] = string(n.Title)
415	DumpHelper(n, source, level, m, nil)
416}
417
418// KindImage is a NodeKind of the Image node.
419var KindImage = NewNodeKind("Image")
420
421// Kind implements Node.Kind.
422func (n *Image) Kind() NodeKind {
423	return KindImage
424}
425
426// NewImage returns a new Image node.
427func NewImage(link *Link) *Image {
428	c := &Image{
429		baseLink: baseLink{
430			BaseInline: BaseInline{},
431		},
432	}
433	c.Destination = link.Destination
434	c.Title = link.Title
435	for n := link.FirstChild(); n != nil; {
436		next := n.NextSibling()
437		link.RemoveChild(link, n)
438		c.AppendChild(c, n)
439		n = next
440	}
441
442	return c
443}
444
445// AutoLinkType defines kind of auto links.
446type AutoLinkType int
447
448const (
449	// AutoLinkEmail indicates that an autolink is an email address.
450	AutoLinkEmail AutoLinkType = iota + 1
451	// AutoLinkURL indicates that an autolink is a generic URL.
452	AutoLinkURL
453)
454
455// An AutoLink struct represents an autolink of the Markdown text.
456type AutoLink struct {
457	BaseInline
458	// Type is a type of this autolink.
459	AutoLinkType AutoLinkType
460
461	// Protocol specified a protocol of the link.
462	Protocol []byte
463
464	value *Text
465}
466
467// Inline implements Inline.Inline.
468func (n *AutoLink) Inline() {}
469
470// Dump implements Node.Dump
471func (n *AutoLink) Dump(source []byte, level int) {
472	segment := n.value.Segment
473	m := map[string]string{
474		"Value": string(segment.Value(source)),
475	}
476	DumpHelper(n, source, level, m, nil)
477}
478
479// KindAutoLink is a NodeKind of the AutoLink node.
480var KindAutoLink = NewNodeKind("AutoLink")
481
482// Kind implements Node.Kind.
483func (n *AutoLink) Kind() NodeKind {
484	return KindAutoLink
485}
486
487// URL returns an url of this node.
488func (n *AutoLink) URL(source []byte) []byte {
489	if n.Protocol != nil {
490		s := n.value.Segment
491		ret := make([]byte, 0, len(n.Protocol)+s.Len()+3)
492		ret = append(ret, n.Protocol...)
493		ret = append(ret, ':', '/', '/')
494		ret = append(ret, n.value.Text(source)...)
495		return ret
496	}
497	return n.value.Text(source)
498}
499
500// Label returns a label of this node.
501func (n *AutoLink) Label(source []byte) []byte {
502	return n.value.Text(source)
503}
504
505// NewAutoLink returns a new AutoLink node.
506func NewAutoLink(typ AutoLinkType, value *Text) *AutoLink {
507	return &AutoLink{
508		BaseInline:   BaseInline{},
509		value:        value,
510		AutoLinkType: typ,
511	}
512}
513
514// A RawHTML struct represents an inline raw HTML of the Markdown text.
515type RawHTML struct {
516	BaseInline
517	Segments *textm.Segments
518}
519
520// Inline implements Inline.Inline.
521func (n *RawHTML) Inline() {}
522
523// Dump implements Node.Dump.
524func (n *RawHTML) Dump(source []byte, level int) {
525	m := map[string]string{}
526	t := []string{}
527	for i := 0; i < n.Segments.Len(); i++ {
528		segment := n.Segments.At(i)
529		t = append(t, string(segment.Value(source)))
530	}
531	m["RawText"] = strings.Join(t, "")
532	DumpHelper(n, source, level, m, nil)
533}
534
535// KindRawHTML is a NodeKind of the RawHTML node.
536var KindRawHTML = NewNodeKind("RawHTML")
537
538// Kind implements Node.Kind.
539func (n *RawHTML) Kind() NodeKind {
540	return KindRawHTML
541}
542
543// NewRawHTML returns a new RawHTML node.
544func NewRawHTML() *RawHTML {
545	return &RawHTML{
546		Segments: textm.NewSegments(),
547	}
548}