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}