raw_html.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. package parser
  2. import (
  3. "bytes"
  4. "regexp"
  5. "github.com/yuin/goldmark/ast"
  6. "github.com/yuin/goldmark/text"
  7. "github.com/yuin/goldmark/util"
  8. )
  9. type rawHTMLParser struct {
  10. }
  11. var defaultRawHTMLParser = &rawHTMLParser{}
  12. // NewRawHTMLParser return a new InlineParser that can parse
  13. // inline htmls
  14. func NewRawHTMLParser() InlineParser {
  15. return defaultRawHTMLParser
  16. }
  17. func (s *rawHTMLParser) Trigger() []byte {
  18. return []byte{'<'}
  19. }
  20. func (s *rawHTMLParser) Parse(parent ast.Node, block text.Reader, pc Context) ast.Node {
  21. line, _ := block.PeekLine()
  22. if len(line) > 1 && util.IsAlphaNumeric(line[1]) {
  23. return s.parseMultiLineRegexp(openTagRegexp, block, pc)
  24. }
  25. if len(line) > 2 && line[1] == '/' && util.IsAlphaNumeric(line[2]) {
  26. return s.parseMultiLineRegexp(closeTagRegexp, block, pc)
  27. }
  28. if bytes.HasPrefix(line, openComment) {
  29. return s.parseComment(block, pc)
  30. }
  31. if bytes.HasPrefix(line, openProcessingInstruction) {
  32. return s.parseUntil(block, closeProcessingInstruction, pc)
  33. }
  34. if len(line) > 2 && line[1] == '!' && line[2] >= 'A' && line[2] <= 'Z' {
  35. return s.parseUntil(block, closeDecl, pc)
  36. }
  37. if bytes.HasPrefix(line, openCDATA) {
  38. return s.parseUntil(block, closeCDATA, pc)
  39. }
  40. return nil
  41. }
  42. var tagnamePattern = `([A-Za-z][A-Za-z0-9-]*)`
  43. var attributePattern = `(?:[\r\n \t]+[a-zA-Z_:][a-zA-Z0-9:._-]*(?:[\r\n \t]*=[\r\n \t]*(?:[^\"'=<>` + "`" + `\x00-\x20]+|'[^']*'|"[^"]*"))?)`
  44. var openTagRegexp = regexp.MustCompile("^<" + tagnamePattern + attributePattern + `*[ \t]*/?>`)
  45. var closeTagRegexp = regexp.MustCompile("^</" + tagnamePattern + `\s*>`)
  46. var openProcessingInstruction = []byte("<?")
  47. var closeProcessingInstruction = []byte("?>")
  48. var openCDATA = []byte("<![CDATA[")
  49. var closeCDATA = []byte("]]>")
  50. var closeDecl = []byte(">")
  51. var emptyComment = []byte("<!---->")
  52. var invalidComment1 = []byte("<!-->")
  53. var invalidComment2 = []byte("<!--->")
  54. var openComment = []byte("<!--")
  55. var closeComment = []byte("-->")
  56. var doubleHyphen = []byte("--")
  57. func (s *rawHTMLParser) parseComment(block text.Reader, pc Context) ast.Node {
  58. savedLine, savedSegment := block.Position()
  59. node := ast.NewRawHTML()
  60. line, segment := block.PeekLine()
  61. if bytes.HasPrefix(line, emptyComment) {
  62. node.Segments.Append(segment.WithStop(segment.Start + len(emptyComment)))
  63. block.Advance(len(emptyComment))
  64. return node
  65. }
  66. if bytes.HasPrefix(line, invalidComment1) || bytes.HasPrefix(line, invalidComment2) {
  67. return nil
  68. }
  69. offset := len(openComment)
  70. line = line[offset:]
  71. for {
  72. hindex := bytes.Index(line, doubleHyphen)
  73. if hindex > -1 {
  74. hindex += offset
  75. }
  76. index := bytes.Index(line, closeComment) + offset
  77. if index > -1 && hindex == index {
  78. if index == 0 || len(line) < 2 || line[index-offset-1] != '-' {
  79. node.Segments.Append(segment.WithStop(segment.Start + index + len(closeComment)))
  80. block.Advance(index + len(closeComment))
  81. return node
  82. }
  83. }
  84. if hindex > 0 {
  85. break
  86. }
  87. node.Segments.Append(segment)
  88. block.AdvanceLine()
  89. line, segment = block.PeekLine()
  90. offset = 0
  91. if line == nil {
  92. break
  93. }
  94. }
  95. block.SetPosition(savedLine, savedSegment)
  96. return nil
  97. }
  98. func (s *rawHTMLParser) parseUntil(block text.Reader, closer []byte, pc Context) ast.Node {
  99. savedLine, savedSegment := block.Position()
  100. node := ast.NewRawHTML()
  101. for {
  102. line, segment := block.PeekLine()
  103. if line == nil {
  104. break
  105. }
  106. index := bytes.Index(line, closer)
  107. if index > -1 {
  108. node.Segments.Append(segment.WithStop(segment.Start + index + len(closer)))
  109. block.Advance(index + len(closer))
  110. return node
  111. }
  112. node.Segments.Append(segment)
  113. block.AdvanceLine()
  114. }
  115. block.SetPosition(savedLine, savedSegment)
  116. return nil
  117. }
  118. func (s *rawHTMLParser) parseMultiLineRegexp(reg *regexp.Regexp, block text.Reader, pc Context) ast.Node {
  119. sline, ssegment := block.Position()
  120. if block.Match(reg) {
  121. node := ast.NewRawHTML()
  122. eline, esegment := block.Position()
  123. block.SetPosition(sline, ssegment)
  124. for {
  125. line, segment := block.PeekLine()
  126. if line == nil {
  127. break
  128. }
  129. l, _ := block.Position()
  130. start := segment.Start
  131. if l == sline {
  132. start = ssegment.Start
  133. }
  134. end := segment.Stop
  135. if l == eline {
  136. end = esegment.Start
  137. }
  138. node.Segments.Append(text.NewSegment(start, end))
  139. if l == eline {
  140. block.Advance(end - start)
  141. break
  142. } else {
  143. block.AdvanceLine()
  144. }
  145. }
  146. return node
  147. }
  148. return nil
  149. }