link_ref.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  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 linkReferenceParagraphTransformer struct {
  8. }
  9. // LinkReferenceParagraphTransformer is a ParagraphTransformer implementation
  10. // that parses and extracts link reference from paragraphs.
  11. var LinkReferenceParagraphTransformer = &linkReferenceParagraphTransformer{}
  12. func (p *linkReferenceParagraphTransformer) Transform(node *ast.Paragraph, reader text.Reader, pc Context) {
  13. lines := node.Lines()
  14. block := text.NewBlockReader(reader.Source(), lines)
  15. removes := [][2]int{}
  16. for {
  17. start, end := parseLinkReferenceDefinition(block, pc)
  18. if start > -1 {
  19. if start == end {
  20. end++
  21. }
  22. removes = append(removes, [2]int{start, end})
  23. continue
  24. }
  25. break
  26. }
  27. offset := 0
  28. for _, remove := range removes {
  29. if lines.Len() == 0 {
  30. break
  31. }
  32. s := lines.Sliced(remove[1]-offset, lines.Len())
  33. lines.SetSliced(0, remove[0]-offset)
  34. lines.AppendAll(s)
  35. offset = remove[1]
  36. }
  37. if lines.Len() == 0 {
  38. t := ast.NewTextBlock()
  39. t.SetBlankPreviousLines(node.HasBlankPreviousLines())
  40. node.Parent().ReplaceChild(node.Parent(), node, t)
  41. return
  42. }
  43. node.SetLines(lines)
  44. }
  45. func parseLinkReferenceDefinition(block text.Reader, pc Context) (int, int) {
  46. block.SkipSpaces()
  47. line, _ := block.PeekLine()
  48. if line == nil {
  49. return -1, -1
  50. }
  51. startLine, _ := block.Position()
  52. width, pos := util.IndentWidth(line, 0)
  53. if width > 3 {
  54. return -1, -1
  55. }
  56. if width != 0 {
  57. pos++
  58. }
  59. if line[pos] != '[' {
  60. return -1, -1
  61. }
  62. block.Advance(pos + 1)
  63. segments, found := block.FindClosure('[', ']', linkFindClosureOptions)
  64. if !found {
  65. return -1, -1
  66. }
  67. var label []byte
  68. if segments.Len() == 1 {
  69. label = block.Value(segments.At(0))
  70. } else {
  71. for i := 0; i < segments.Len(); i++ {
  72. s := segments.At(i)
  73. label = append(label, block.Value(s)...)
  74. }
  75. }
  76. if util.IsBlank(label) {
  77. return -1, -1
  78. }
  79. if block.Peek() != ':' {
  80. return -1, -1
  81. }
  82. block.Advance(1)
  83. block.SkipSpaces()
  84. destination, ok := parseLinkDestination(block)
  85. if !ok {
  86. return -1, -1
  87. }
  88. line, _ = block.PeekLine()
  89. isNewLine := line == nil || util.IsBlank(line)
  90. endLine, _ := block.Position()
  91. _, spaces, _ := block.SkipSpaces()
  92. opener := block.Peek()
  93. if opener != '"' && opener != '\'' && opener != '(' {
  94. if !isNewLine {
  95. return -1, -1
  96. }
  97. ref := NewReference(label, destination, nil)
  98. pc.AddReference(ref)
  99. return startLine, endLine + 1
  100. }
  101. if spaces == 0 {
  102. return -1, -1
  103. }
  104. block.Advance(1)
  105. closer := opener
  106. if opener == '(' {
  107. closer = ')'
  108. }
  109. segments, found = block.FindClosure(opener, closer, linkFindClosureOptions)
  110. if !found {
  111. if !isNewLine {
  112. return -1, -1
  113. }
  114. ref := NewReference(label, destination, nil)
  115. pc.AddReference(ref)
  116. block.AdvanceLine()
  117. return startLine, endLine + 1
  118. }
  119. var title []byte
  120. if segments.Len() == 1 {
  121. title = block.Value(segments.At(0))
  122. } else {
  123. for i := 0; i < segments.Len(); i++ {
  124. s := segments.At(i)
  125. title = append(title, block.Value(s)...)
  126. }
  127. }
  128. line, _ = block.PeekLine()
  129. if line != nil && !util.IsBlank(line) {
  130. if !isNewLine {
  131. return -1, -1
  132. }
  133. ref := NewReference(label, destination, title)
  134. pc.AddReference(ref)
  135. return startLine, endLine
  136. }
  137. endLine, _ = block.Position()
  138. ref := NewReference(label, destination, title)
  139. pc.AddReference(ref)
  140. return startLine, endLine + 1
  141. }