inline.go (12686B)
1 package ast 2 3 import ( 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. 12 type BaseInline struct { 13 BaseNode 14 } 15 16 // Type implements Node.Type 17 func (b *BaseInline) Type() NodeType { 18 return TypeInline 19 } 20 21 // IsRaw implements Node.IsRaw 22 func (b *BaseInline) IsRaw() bool { 23 return false 24 } 25 26 // HasBlankPreviousLines implements Node.HasBlankPreviousLines. 27 func (b *BaseInline) HasBlankPreviousLines() bool { 28 panic("can not call with inline nodes.") 29 } 30 31 // SetBlankPreviousLines implements Node.SetBlankPreviousLines. 32 func (b *BaseInline) SetBlankPreviousLines(v bool) { 33 panic("can not call with inline nodes.") 34 } 35 36 // Lines implements Node.Lines 37 func (b *BaseInline) Lines() *textm.Segments { 38 panic("can not call with inline nodes.") 39 } 40 41 // SetLines implements Node.SetLines 42 func (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. 47 type Text struct { 48 BaseInline 49 // Segment is a position in a source text. 50 Segment textm.Segment 51 52 flags uint8 53 } 54 55 const ( 56 textSoftLineBreak = 1 << iota 57 textHardLineBreak 58 textRaw 59 textCode 60 ) 61 62 func 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. 80 func (n *Text) Inline() { 81 } 82 83 // SoftLineBreak returns true if this node ends with a new line, 84 // otherwise false. 85 func (n *Text) SoftLineBreak() bool { 86 return n.flags&textSoftLineBreak != 0 87 } 88 89 // SetSoftLineBreak sets whether this node ends with a new line. 90 func (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. 100 func (n *Text) IsRaw() bool { 101 return n.flags&textRaw != 0 102 } 103 104 // SetRaw sets whether this text should be rendered as raw contents. 105 func (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. 115 func (n *Text) HardLineBreak() bool { 116 return n.flags&textHardLineBreak != 0 117 } 118 119 // SetHardLineBreak sets whether this node ends with a hard line break. 120 func (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. 130 func (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. 145 func (n *Text) Text(source []byte) []byte { 146 return n.Segment.Value(source) 147 } 148 149 // Dump implements Node.Dump. 150 func (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. 159 var KindText = NewNodeKind("Text") 160 161 // Kind implements Node.Kind. 162 func (n *Text) Kind() NodeKind { 163 return KindText 164 } 165 166 // NewText returns a new Text node. 167 func NewText() *Text { 168 return &Text{ 169 BaseInline: BaseInline{}, 170 } 171 } 172 173 // NewTextSegment returns a new Text node with the given source position. 174 func 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. 183 func 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. 195 func 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. 207 func 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 218 type String struct { 219 BaseInline 220 221 Value []byte 222 flags uint8 223 } 224 225 // Inline implements Inline.Inline. 226 func (n *String) Inline() { 227 } 228 229 // IsRaw returns true if this text should be rendered without unescaping 230 // back slash escapes and resolving references. 231 func (n *String) IsRaw() bool { 232 return n.flags&textRaw != 0 233 } 234 235 // SetRaw sets whether this text should be rendered as raw contents. 236 func (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. 246 func (n *String) IsCode() bool { 247 return n.flags&textCode != 0 248 } 249 250 // SetCode sets whether this text should be rendered without any modifications. 251 func (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. 260 func (n *String) Text(source []byte) []byte { 261 return n.Value 262 } 263 264 // Dump implements Node.Dump. 265 func (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. 274 var KindString = NewNodeKind("String") 275 276 // Kind implements Node.Kind. 277 func (n *String) Kind() NodeKind { 278 return KindString 279 } 280 281 // NewString returns a new String node. 282 func NewString(v []byte) *String { 283 return &String{ 284 Value: v, 285 } 286 } 287 288 // A CodeSpan struct represents a code span of Markdown text. 289 type CodeSpan struct { 290 BaseInline 291 } 292 293 // Inline implements Inline.Inline . 294 func (n *CodeSpan) Inline() { 295 } 296 297 // IsBlank returns true if this node consists of spaces, otherwise false. 298 func (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 309 func (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. 314 var KindCodeSpan = NewNodeKind("CodeSpan") 315 316 // Kind implements Node.Kind. 317 func (n *CodeSpan) Kind() NodeKind { 318 return KindCodeSpan 319 } 320 321 // NewCodeSpan returns a new CodeSpan node. 322 func NewCodeSpan() *CodeSpan { 323 return &CodeSpan{ 324 BaseInline: BaseInline{}, 325 } 326 } 327 328 // An Emphasis struct represents an emphasis of Markdown text. 329 type Emphasis struct { 330 BaseInline 331 332 // Level is a level of the emphasis. 333 Level int 334 } 335 336 // Dump implements Node.Dump. 337 func (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. 345 var KindEmphasis = NewNodeKind("Emphasis") 346 347 // Kind implements Node.Kind. 348 func (n *Emphasis) Kind() NodeKind { 349 return KindEmphasis 350 } 351 352 // NewEmphasis returns a new Emphasis node with the given level. 353 func NewEmphasis(level int) *Emphasis { 354 return &Emphasis{ 355 BaseInline: BaseInline{}, 356 Level: level, 357 } 358 } 359 360 type 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. 371 func (n *baseLink) Inline() { 372 } 373 374 // A Link struct represents a link of the Markdown text. 375 type Link struct { 376 baseLink 377 } 378 379 // Dump implements Node.Dump. 380 func (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. 388 var KindLink = NewNodeKind("Link") 389 390 // Kind implements Node.Kind. 391 func (n *Link) Kind() NodeKind { 392 return KindLink 393 } 394 395 // NewLink returns a new Link node. 396 func 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. 406 type Image struct { 407 baseLink 408 } 409 410 // Dump implements Node.Dump. 411 func (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. 419 var KindImage = NewNodeKind("Image") 420 421 // Kind implements Node.Kind. 422 func (n *Image) Kind() NodeKind { 423 return KindImage 424 } 425 426 // NewImage returns a new Image node. 427 func 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. 446 type AutoLinkType int 447 448 const ( 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. 456 type 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. 468 func (n *AutoLink) Inline() {} 469 470 // Dump implements Node.Dump 471 func (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. 480 var KindAutoLink = NewNodeKind("AutoLink") 481 482 // Kind implements Node.Kind. 483 func (n *AutoLink) Kind() NodeKind { 484 return KindAutoLink 485 } 486 487 // URL returns an url of this node. 488 func (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. 501 func (n *AutoLink) Label(source []byte) []byte { 502 return n.value.Text(source) 503 } 504 505 // NewAutoLink returns a new AutoLink node. 506 func 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. 515 type RawHTML struct { 516 BaseInline 517 Segments *textm.Segments 518 } 519 520 // Inline implements Inline.Inline. 521 func (n *RawHTML) Inline() {} 522 523 // Dump implements Node.Dump. 524 func (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. 536 var KindRawHTML = NewNodeKind("RawHTML") 537 538 // Kind implements Node.Kind. 539 func (n *RawHTML) Kind() NodeKind { 540 return KindRawHTML 541 } 542 543 // NewRawHTML returns a new RawHTML node. 544 func NewRawHTML() *RawHTML { 545 return &RawHTML{ 546 Segments: textm.NewSegments(), 547 } 548 }