delimiter.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. package parser
  2. import (
  3. "fmt"
  4. "strings"
  5. "github.com/yuin/goldmark/ast"
  6. "github.com/yuin/goldmark/text"
  7. "github.com/yuin/goldmark/util"
  8. )
  9. // A DelimiterProcessor interface provides a set of functions about
  10. // Delimiter nodes.
  11. type DelimiterProcessor interface {
  12. // IsDelimiter returns true if given character is a delimiter, otherwise false.
  13. IsDelimiter(byte) bool
  14. // CanOpenCloser returns true if given opener can close given closer, otherwise false.
  15. CanOpenCloser(opener, closer *Delimiter) bool
  16. // OnMatch will be called when new matched delimiter found.
  17. // OnMatch should return a new Node correspond to the matched delimiter.
  18. OnMatch(consumes int) ast.Node
  19. }
  20. // A Delimiter struct represents a delimiter like '*' of the Markdown text.
  21. type Delimiter struct {
  22. ast.BaseInline
  23. Segment text.Segment
  24. // CanOpen is set true if this delimiter can open a span for a new node.
  25. // See https://spec.commonmark.org/0.30/#can-open-emphasis for details.
  26. CanOpen bool
  27. // CanClose is set true if this delimiter can close a span for a new node.
  28. // See https://spec.commonmark.org/0.30/#can-open-emphasis for details.
  29. CanClose bool
  30. // Length is a remaining length of this delimiter.
  31. Length int
  32. // OriginalLength is a original length of this delimiter.
  33. OriginalLength int
  34. // Char is a character of this delimiter.
  35. Char byte
  36. // PreviousDelimiter is a previous sibling delimiter node of this delimiter.
  37. PreviousDelimiter *Delimiter
  38. // NextDelimiter is a next sibling delimiter node of this delimiter.
  39. NextDelimiter *Delimiter
  40. // Processor is a DelimiterProcessor associated with this delimiter.
  41. Processor DelimiterProcessor
  42. }
  43. // Inline implements Inline.Inline.
  44. func (d *Delimiter) Inline() {}
  45. // Dump implements Node.Dump.
  46. func (d *Delimiter) Dump(source []byte, level int) {
  47. fmt.Printf("%sDelimiter: \"%s\"\n", strings.Repeat(" ", level), string(d.Text(source)))
  48. }
  49. var kindDelimiter = ast.NewNodeKind("Delimiter")
  50. // Kind implements Node.Kind.
  51. func (d *Delimiter) Kind() ast.NodeKind {
  52. return kindDelimiter
  53. }
  54. // Text implements Node.Text.
  55. func (d *Delimiter) Text(source []byte) []byte {
  56. return d.Segment.Value(source)
  57. }
  58. // ConsumeCharacters consumes delimiters.
  59. func (d *Delimiter) ConsumeCharacters(n int) {
  60. d.Length -= n
  61. d.Segment = d.Segment.WithStop(d.Segment.Start + d.Length)
  62. }
  63. // CalcComsumption calculates how many characters should be used for opening
  64. // a new span correspond to given closer.
  65. func (d *Delimiter) CalcComsumption(closer *Delimiter) int {
  66. if (d.CanClose || closer.CanOpen) && (d.OriginalLength+closer.OriginalLength)%3 == 0 && closer.OriginalLength%3 != 0 {
  67. return 0
  68. }
  69. if d.Length >= 2 && closer.Length >= 2 {
  70. return 2
  71. }
  72. return 1
  73. }
  74. // NewDelimiter returns a new Delimiter node.
  75. func NewDelimiter(canOpen, canClose bool, length int, char byte, processor DelimiterProcessor) *Delimiter {
  76. c := &Delimiter{
  77. BaseInline: ast.BaseInline{},
  78. CanOpen: canOpen,
  79. CanClose: canClose,
  80. Length: length,
  81. OriginalLength: length,
  82. Char: char,
  83. PreviousDelimiter: nil,
  84. NextDelimiter: nil,
  85. Processor: processor,
  86. }
  87. return c
  88. }
  89. // ScanDelimiter scans a delimiter by given DelimiterProcessor.
  90. func ScanDelimiter(line []byte, before rune, min int, processor DelimiterProcessor) *Delimiter {
  91. i := 0
  92. c := line[i]
  93. j := i
  94. if !processor.IsDelimiter(c) {
  95. return nil
  96. }
  97. for ; j < len(line) && c == line[j]; j++ {
  98. }
  99. if (j - i) >= min {
  100. after := rune(' ')
  101. if j != len(line) {
  102. after = util.ToRune(line, j)
  103. }
  104. var canOpen, canClose bool
  105. beforeIsPunctuation := util.IsPunctRune(before)
  106. beforeIsWhitespace := util.IsSpaceRune(before)
  107. afterIsPunctuation := util.IsPunctRune(after)
  108. afterIsWhitespace := util.IsSpaceRune(after)
  109. isLeft := !afterIsWhitespace &&
  110. (!afterIsPunctuation || beforeIsWhitespace || beforeIsPunctuation)
  111. isRight := !beforeIsWhitespace &&
  112. (!beforeIsPunctuation || afterIsWhitespace || afterIsPunctuation)
  113. if line[i] == '_' {
  114. canOpen = isLeft && (!isRight || beforeIsPunctuation)
  115. canClose = isRight && (!isLeft || afterIsPunctuation)
  116. } else {
  117. canOpen = isLeft
  118. canClose = isRight
  119. }
  120. return NewDelimiter(canOpen, canClose, j-i, c, processor)
  121. }
  122. return nil
  123. }
  124. // ProcessDelimiters processes the delimiter list in the context.
  125. // Processing will be stop when reaching the bottom.
  126. //
  127. // If you implement an inline parser that can have other inline nodes as
  128. // children, you should call this function when nesting span has closed.
  129. func ProcessDelimiters(bottom ast.Node, pc Context) {
  130. lastDelimiter := pc.LastDelimiter()
  131. if lastDelimiter == nil {
  132. return
  133. }
  134. var closer *Delimiter
  135. if bottom != nil {
  136. if bottom != lastDelimiter {
  137. for c := lastDelimiter.PreviousSibling(); c != nil && c != bottom; {
  138. if d, ok := c.(*Delimiter); ok {
  139. closer = d
  140. }
  141. c = c.PreviousSibling()
  142. }
  143. }
  144. } else {
  145. closer = pc.FirstDelimiter()
  146. }
  147. if closer == nil {
  148. pc.ClearDelimiters(bottom)
  149. return
  150. }
  151. for closer != nil {
  152. if !closer.CanClose {
  153. closer = closer.NextDelimiter
  154. continue
  155. }
  156. consume := 0
  157. found := false
  158. maybeOpener := false
  159. var opener *Delimiter
  160. for opener = closer.PreviousDelimiter; opener != nil && opener != bottom; opener = opener.PreviousDelimiter {
  161. if opener.CanOpen && opener.Processor.CanOpenCloser(opener, closer) {
  162. maybeOpener = true
  163. consume = opener.CalcComsumption(closer)
  164. if consume > 0 {
  165. found = true
  166. break
  167. }
  168. }
  169. }
  170. if !found {
  171. next := closer.NextDelimiter
  172. if !maybeOpener && !closer.CanOpen {
  173. pc.RemoveDelimiter(closer)
  174. }
  175. closer = next
  176. continue
  177. }
  178. opener.ConsumeCharacters(consume)
  179. closer.ConsumeCharacters(consume)
  180. node := opener.Processor.OnMatch(consume)
  181. parent := opener.Parent()
  182. child := opener.NextSibling()
  183. for child != nil && child != closer {
  184. next := child.NextSibling()
  185. node.AppendChild(node, child)
  186. child = next
  187. }
  188. parent.InsertAfter(parent, opener, node)
  189. for c := opener.NextDelimiter; c != nil && c != closer; {
  190. next := c.NextDelimiter
  191. pc.RemoveDelimiter(c)
  192. c = next
  193. }
  194. if opener.Length == 0 {
  195. pc.RemoveDelimiter(opener)
  196. }
  197. if closer.Length == 0 {
  198. next := closer.NextDelimiter
  199. pc.RemoveDelimiter(closer)
  200. closer = next
  201. }
  202. }
  203. pc.ClearDelimiters(bottom)
  204. }