block.go (11848B)
1 package ast 2 3 import ( 4 "fmt" 5 "strings" 6 7 textm "github.com/yuin/goldmark/text" 8 ) 9 10 // A BaseBlock struct implements the Node interface partialliy. 11 type BaseBlock struct { 12 BaseNode 13 blankPreviousLines bool 14 lines *textm.Segments 15 } 16 17 // Type implements Node.Type 18 func (b *BaseBlock) Type() NodeType { 19 return TypeBlock 20 } 21 22 // IsRaw implements Node.IsRaw 23 func (b *BaseBlock) IsRaw() bool { 24 return false 25 } 26 27 // HasBlankPreviousLines implements Node.HasBlankPreviousLines. 28 func (b *BaseBlock) HasBlankPreviousLines() bool { 29 return b.blankPreviousLines 30 } 31 32 // SetBlankPreviousLines implements Node.SetBlankPreviousLines. 33 func (b *BaseBlock) SetBlankPreviousLines(v bool) { 34 b.blankPreviousLines = v 35 } 36 37 // Lines implements Node.Lines 38 func (b *BaseBlock) Lines() *textm.Segments { 39 if b.lines == nil { 40 b.lines = textm.NewSegments() 41 } 42 return b.lines 43 } 44 45 // SetLines implements Node.SetLines 46 func (b *BaseBlock) SetLines(v *textm.Segments) { 47 b.lines = v 48 } 49 50 // A Document struct is a root node of Markdown text. 51 type Document struct { 52 BaseBlock 53 54 meta map[string]interface{} 55 } 56 57 // KindDocument is a NodeKind of the Document node. 58 var KindDocument = NewNodeKind("Document") 59 60 // Dump implements Node.Dump . 61 func (n *Document) Dump(source []byte, level int) { 62 DumpHelper(n, source, level, nil, nil) 63 } 64 65 // Type implements Node.Type . 66 func (n *Document) Type() NodeType { 67 return TypeDocument 68 } 69 70 // Kind implements Node.Kind. 71 func (n *Document) Kind() NodeKind { 72 return KindDocument 73 } 74 75 // OwnerDocument implements Node.OwnerDocument 76 func (n *Document) OwnerDocument() *Document { 77 return n 78 } 79 80 // Meta returns metadata of this document. 81 func (n *Document) Meta() map[string]interface{} { 82 if n.meta == nil { 83 n.meta = map[string]interface{}{} 84 } 85 return n.meta 86 } 87 88 // SetMeta sets given metadata to this document. 89 func (n *Document) SetMeta(meta map[string]interface{}) { 90 if n.meta == nil { 91 n.meta = map[string]interface{}{} 92 } 93 for k, v := range meta { 94 n.meta[k] = v 95 } 96 } 97 98 // AddMeta adds given metadata to this document. 99 func (n *Document) AddMeta(key string, value interface{}) { 100 if n.meta == nil { 101 n.meta = map[string]interface{}{} 102 } 103 n.meta[key] = value 104 } 105 106 // NewDocument returns a new Document node. 107 func NewDocument() *Document { 108 return &Document{ 109 BaseBlock: BaseBlock{}, 110 meta: nil, 111 } 112 } 113 114 // A TextBlock struct is a node whose lines 115 // should be rendered without any containers. 116 type TextBlock struct { 117 BaseBlock 118 } 119 120 // Dump implements Node.Dump . 121 func (n *TextBlock) Dump(source []byte, level int) { 122 DumpHelper(n, source, level, nil, nil) 123 } 124 125 // KindTextBlock is a NodeKind of the TextBlock node. 126 var KindTextBlock = NewNodeKind("TextBlock") 127 128 // Kind implements Node.Kind. 129 func (n *TextBlock) Kind() NodeKind { 130 return KindTextBlock 131 } 132 133 // NewTextBlock returns a new TextBlock node. 134 func NewTextBlock() *TextBlock { 135 return &TextBlock{ 136 BaseBlock: BaseBlock{}, 137 } 138 } 139 140 // A Paragraph struct represents a paragraph of Markdown text. 141 type Paragraph struct { 142 BaseBlock 143 } 144 145 // Dump implements Node.Dump . 146 func (n *Paragraph) Dump(source []byte, level int) { 147 DumpHelper(n, source, level, nil, nil) 148 } 149 150 // KindParagraph is a NodeKind of the Paragraph node. 151 var KindParagraph = NewNodeKind("Paragraph") 152 153 // Kind implements Node.Kind. 154 func (n *Paragraph) Kind() NodeKind { 155 return KindParagraph 156 } 157 158 // NewParagraph returns a new Paragraph node. 159 func NewParagraph() *Paragraph { 160 return &Paragraph{ 161 BaseBlock: BaseBlock{}, 162 } 163 } 164 165 // IsParagraph returns true if the given node implements the Paragraph interface, 166 // otherwise false. 167 func IsParagraph(node Node) bool { 168 _, ok := node.(*Paragraph) 169 return ok 170 } 171 172 // A Heading struct represents headings like SetextHeading and ATXHeading. 173 type Heading struct { 174 BaseBlock 175 // Level returns a level of this heading. 176 // This value is between 1 and 6. 177 Level int 178 } 179 180 // Dump implements Node.Dump . 181 func (n *Heading) Dump(source []byte, level int) { 182 m := map[string]string{ 183 "Level": fmt.Sprintf("%d", n.Level), 184 } 185 DumpHelper(n, source, level, m, nil) 186 } 187 188 // KindHeading is a NodeKind of the Heading node. 189 var KindHeading = NewNodeKind("Heading") 190 191 // Kind implements Node.Kind. 192 func (n *Heading) Kind() NodeKind { 193 return KindHeading 194 } 195 196 // NewHeading returns a new Heading node. 197 func NewHeading(level int) *Heading { 198 return &Heading{ 199 BaseBlock: BaseBlock{}, 200 Level: level, 201 } 202 } 203 204 // A ThematicBreak struct represents a thematic break of Markdown text. 205 type ThematicBreak struct { 206 BaseBlock 207 } 208 209 // Dump implements Node.Dump . 210 func (n *ThematicBreak) Dump(source []byte, level int) { 211 DumpHelper(n, source, level, nil, nil) 212 } 213 214 // KindThematicBreak is a NodeKind of the ThematicBreak node. 215 var KindThematicBreak = NewNodeKind("ThematicBreak") 216 217 // Kind implements Node.Kind. 218 func (n *ThematicBreak) Kind() NodeKind { 219 return KindThematicBreak 220 } 221 222 // NewThematicBreak returns a new ThematicBreak node. 223 func NewThematicBreak() *ThematicBreak { 224 return &ThematicBreak{ 225 BaseBlock: BaseBlock{}, 226 } 227 } 228 229 // A CodeBlock interface represents an indented code block of Markdown text. 230 type CodeBlock struct { 231 BaseBlock 232 } 233 234 // IsRaw implements Node.IsRaw. 235 func (n *CodeBlock) IsRaw() bool { 236 return true 237 } 238 239 // Dump implements Node.Dump . 240 func (n *CodeBlock) Dump(source []byte, level int) { 241 DumpHelper(n, source, level, nil, nil) 242 } 243 244 // KindCodeBlock is a NodeKind of the CodeBlock node. 245 var KindCodeBlock = NewNodeKind("CodeBlock") 246 247 // Kind implements Node.Kind. 248 func (n *CodeBlock) Kind() NodeKind { 249 return KindCodeBlock 250 } 251 252 // NewCodeBlock returns a new CodeBlock node. 253 func NewCodeBlock() *CodeBlock { 254 return &CodeBlock{ 255 BaseBlock: BaseBlock{}, 256 } 257 } 258 259 // A FencedCodeBlock struct represents a fenced code block of Markdown text. 260 type FencedCodeBlock struct { 261 BaseBlock 262 // Info returns a info text of this fenced code block. 263 Info *Text 264 265 language []byte 266 } 267 268 // Language returns an language in an info string. 269 // Language returns nil if this node does not have an info string. 270 func (n *FencedCodeBlock) Language(source []byte) []byte { 271 if n.language == nil && n.Info != nil { 272 segment := n.Info.Segment 273 info := segment.Value(source) 274 i := 0 275 for ; i < len(info); i++ { 276 if info[i] == ' ' { 277 break 278 } 279 } 280 n.language = info[:i] 281 } 282 return n.language 283 } 284 285 // IsRaw implements Node.IsRaw. 286 func (n *FencedCodeBlock) IsRaw() bool { 287 return true 288 } 289 290 // Dump implements Node.Dump . 291 func (n *FencedCodeBlock) Dump(source []byte, level int) { 292 m := map[string]string{} 293 if n.Info != nil { 294 m["Info"] = fmt.Sprintf("\"%s\"", n.Info.Text(source)) 295 } 296 DumpHelper(n, source, level, m, nil) 297 } 298 299 // KindFencedCodeBlock is a NodeKind of the FencedCodeBlock node. 300 var KindFencedCodeBlock = NewNodeKind("FencedCodeBlock") 301 302 // Kind implements Node.Kind. 303 func (n *FencedCodeBlock) Kind() NodeKind { 304 return KindFencedCodeBlock 305 } 306 307 // NewFencedCodeBlock return a new FencedCodeBlock node. 308 func NewFencedCodeBlock(info *Text) *FencedCodeBlock { 309 return &FencedCodeBlock{ 310 BaseBlock: BaseBlock{}, 311 Info: info, 312 } 313 } 314 315 // A Blockquote struct represents an blockquote block of Markdown text. 316 type Blockquote struct { 317 BaseBlock 318 } 319 320 // Dump implements Node.Dump . 321 func (n *Blockquote) Dump(source []byte, level int) { 322 DumpHelper(n, source, level, nil, nil) 323 } 324 325 // KindBlockquote is a NodeKind of the Blockquote node. 326 var KindBlockquote = NewNodeKind("Blockquote") 327 328 // Kind implements Node.Kind. 329 func (n *Blockquote) Kind() NodeKind { 330 return KindBlockquote 331 } 332 333 // NewBlockquote returns a new Blockquote node. 334 func NewBlockquote() *Blockquote { 335 return &Blockquote{ 336 BaseBlock: BaseBlock{}, 337 } 338 } 339 340 // A List struct represents a list of Markdown text. 341 type List struct { 342 BaseBlock 343 344 // Marker is a marker character like '-', '+', ')' and '.'. 345 Marker byte 346 347 // IsTight is a true if this list is a 'tight' list. 348 // See https://spec.commonmark.org/0.30/#loose for details. 349 IsTight bool 350 351 // Start is an initial number of this ordered list. 352 // If this list is not an ordered list, Start is 0. 353 Start int 354 } 355 356 // IsOrdered returns true if this list is an ordered list, otherwise false. 357 func (l *List) IsOrdered() bool { 358 return l.Marker == '.' || l.Marker == ')' 359 } 360 361 // CanContinue returns true if this list can continue with 362 // the given mark and a list type, otherwise false. 363 func (l *List) CanContinue(marker byte, isOrdered bool) bool { 364 return marker == l.Marker && isOrdered == l.IsOrdered() 365 } 366 367 // Dump implements Node.Dump. 368 func (l *List) Dump(source []byte, level int) { 369 m := map[string]string{ 370 "Ordered": fmt.Sprintf("%v", l.IsOrdered()), 371 "Marker": fmt.Sprintf("%c", l.Marker), 372 "Tight": fmt.Sprintf("%v", l.IsTight), 373 } 374 if l.IsOrdered() { 375 m["Start"] = fmt.Sprintf("%d", l.Start) 376 } 377 DumpHelper(l, source, level, m, nil) 378 } 379 380 // KindList is a NodeKind of the List node. 381 var KindList = NewNodeKind("List") 382 383 // Kind implements Node.Kind. 384 func (l *List) Kind() NodeKind { 385 return KindList 386 } 387 388 // NewList returns a new List node. 389 func NewList(marker byte) *List { 390 return &List{ 391 BaseBlock: BaseBlock{}, 392 Marker: marker, 393 IsTight: true, 394 } 395 } 396 397 // A ListItem struct represents a list item of Markdown text. 398 type ListItem struct { 399 BaseBlock 400 401 // Offset is an offset position of this item. 402 Offset int 403 } 404 405 // Dump implements Node.Dump. 406 func (n *ListItem) Dump(source []byte, level int) { 407 m := map[string]string{ 408 "Offset": fmt.Sprintf("%d", n.Offset), 409 } 410 DumpHelper(n, source, level, m, nil) 411 } 412 413 // KindListItem is a NodeKind of the ListItem node. 414 var KindListItem = NewNodeKind("ListItem") 415 416 // Kind implements Node.Kind. 417 func (n *ListItem) Kind() NodeKind { 418 return KindListItem 419 } 420 421 // NewListItem returns a new ListItem node. 422 func NewListItem(offset int) *ListItem { 423 return &ListItem{ 424 BaseBlock: BaseBlock{}, 425 Offset: offset, 426 } 427 } 428 429 // HTMLBlockType represents kinds of an html blocks. 430 // See https://spec.commonmark.org/0.30/#html-blocks 431 type HTMLBlockType int 432 433 const ( 434 // HTMLBlockType1 represents type 1 html blocks 435 HTMLBlockType1 HTMLBlockType = iota + 1 436 // HTMLBlockType2 represents type 2 html blocks 437 HTMLBlockType2 438 // HTMLBlockType3 represents type 3 html blocks 439 HTMLBlockType3 440 // HTMLBlockType4 represents type 4 html blocks 441 HTMLBlockType4 442 // HTMLBlockType5 represents type 5 html blocks 443 HTMLBlockType5 444 // HTMLBlockType6 represents type 6 html blocks 445 HTMLBlockType6 446 // HTMLBlockType7 represents type 7 html blocks 447 HTMLBlockType7 448 ) 449 450 // An HTMLBlock struct represents an html block of Markdown text. 451 type HTMLBlock struct { 452 BaseBlock 453 454 // Type is a type of this html block. 455 HTMLBlockType HTMLBlockType 456 457 // ClosureLine is a line that closes this html block. 458 ClosureLine textm.Segment 459 } 460 461 // IsRaw implements Node.IsRaw. 462 func (n *HTMLBlock) IsRaw() bool { 463 return true 464 } 465 466 // HasClosure returns true if this html block has a closure line, 467 // otherwise false. 468 func (n *HTMLBlock) HasClosure() bool { 469 return n.ClosureLine.Start >= 0 470 } 471 472 // Dump implements Node.Dump. 473 func (n *HTMLBlock) Dump(source []byte, level int) { 474 indent := strings.Repeat(" ", level) 475 fmt.Printf("%s%s {\n", indent, "HTMLBlock") 476 indent2 := strings.Repeat(" ", level+1) 477 fmt.Printf("%sRawText: \"", indent2) 478 for i := 0; i < n.Lines().Len(); i++ { 479 s := n.Lines().At(i) 480 fmt.Print(string(source[s.Start:s.Stop])) 481 } 482 fmt.Printf("\"\n") 483 for c := n.FirstChild(); c != nil; c = c.NextSibling() { 484 c.Dump(source, level+1) 485 } 486 if n.HasClosure() { 487 cl := n.ClosureLine 488 fmt.Printf("%sClosure: \"%s\"\n", indent2, string(cl.Value(source))) 489 } 490 fmt.Printf("%s}\n", indent) 491 } 492 493 // KindHTMLBlock is a NodeKind of the HTMLBlock node. 494 var KindHTMLBlock = NewNodeKind("HTMLBlock") 495 496 // Kind implements Node.Kind. 497 func (n *HTMLBlock) Kind() NodeKind { 498 return KindHTMLBlock 499 } 500 501 // NewHTMLBlock returns a new HTMLBlock node. 502 func NewHTMLBlock(typ HTMLBlockType) *HTMLBlock { 503 return &HTMLBlock{ 504 BaseBlock: BaseBlock{}, 505 HTMLBlockType: typ, 506 ClosureLine: textm.NewSegment(-1, -1), 507 } 508 }