inline.go

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