list_item.go 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. package parser
  2. import (
  3. "github.com/yuin/goldmark/ast"
  4. "github.com/yuin/goldmark/text"
  5. "github.com/yuin/goldmark/util"
  6. )
  7. type listItemParser struct {
  8. }
  9. var defaultListItemParser = &listItemParser{}
  10. // NewListItemParser returns a new BlockParser that
  11. // parses list items.
  12. func NewListItemParser() BlockParser {
  13. return defaultListItemParser
  14. }
  15. func (b *listItemParser) Trigger() []byte {
  16. return []byte{'-', '+', '*', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
  17. }
  18. func (b *listItemParser) Open(parent ast.Node, reader text.Reader, pc Context) (ast.Node, State) {
  19. list, lok := parent.(*ast.List)
  20. if !lok { // list item must be a child of a list
  21. return nil, NoChildren
  22. }
  23. offset := lastOffset(list)
  24. line, _ := reader.PeekLine()
  25. match, typ := matchesListItem(line, false)
  26. if typ == notList {
  27. return nil, NoChildren
  28. }
  29. if match[1]-offset > 3 {
  30. return nil, NoChildren
  31. }
  32. pc.Set(emptyListItemWithBlankLines, nil)
  33. itemOffset := calcListOffset(line, match)
  34. node := ast.NewListItem(match[3] + itemOffset)
  35. if match[4] < 0 || util.IsBlank(line[match[4]:match[5]]) {
  36. return node, NoChildren
  37. }
  38. pos, padding := util.IndentPosition(line[match[4]:], match[4], itemOffset)
  39. child := match[3] + pos
  40. reader.AdvanceAndSetPadding(child, padding)
  41. return node, HasChildren
  42. }
  43. func (b *listItemParser) Continue(node ast.Node, reader text.Reader, pc Context) State {
  44. line, _ := reader.PeekLine()
  45. if util.IsBlank(line) {
  46. reader.Advance(len(line) - 1)
  47. return Continue | HasChildren
  48. }
  49. offset := lastOffset(node.Parent())
  50. isEmpty := node.ChildCount() == 0
  51. indent, _ := util.IndentWidth(line, reader.LineOffset())
  52. if (isEmpty || indent < offset) && indent < 4 {
  53. _, typ := matchesListItem(line, true)
  54. // new list item found
  55. if typ != notList {
  56. pc.Set(skipListParserKey, listItemFlagValue)
  57. return Close
  58. }
  59. if !isEmpty {
  60. return Close
  61. }
  62. }
  63. pos, padding := util.IndentPosition(line, reader.LineOffset(), offset)
  64. reader.AdvanceAndSetPadding(pos, padding)
  65. return Continue | HasChildren
  66. }
  67. func (b *listItemParser) Close(node ast.Node, reader text.Reader, pc Context) {
  68. // nothing to do
  69. }
  70. func (b *listItemParser) CanInterruptParagraph() bool {
  71. return true
  72. }
  73. func (b *listItemParser) CanAcceptIndentedLine() bool {
  74. return false
  75. }