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}