code-fenced.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. 'use strict'
  2. module.exports = fencedCode
  3. var lineFeed = '\n'
  4. var tab = '\t'
  5. var space = ' '
  6. var tilde = '~'
  7. var graveAccent = '`'
  8. var minFenceCount = 3
  9. var tabSize = 4
  10. function fencedCode(eat, value, silent) {
  11. var self = this
  12. var gfm = self.options.gfm
  13. var length = value.length + 1
  14. var index = 0
  15. var subvalue = ''
  16. var fenceCount
  17. var marker
  18. var character
  19. var flag
  20. var lang
  21. var meta
  22. var queue
  23. var content
  24. var exdentedContent
  25. var closing
  26. var exdentedClosing
  27. var indent
  28. var now
  29. if (!gfm) {
  30. return
  31. }
  32. // Eat initial spacing.
  33. while (index < length) {
  34. character = value.charAt(index)
  35. if (character !== space && character !== tab) {
  36. break
  37. }
  38. subvalue += character
  39. index++
  40. }
  41. indent = index
  42. // Eat the fence.
  43. character = value.charAt(index)
  44. if (character !== tilde && character !== graveAccent) {
  45. return
  46. }
  47. index++
  48. marker = character
  49. fenceCount = 1
  50. subvalue += character
  51. while (index < length) {
  52. character = value.charAt(index)
  53. if (character !== marker) {
  54. break
  55. }
  56. subvalue += character
  57. fenceCount++
  58. index++
  59. }
  60. if (fenceCount < minFenceCount) {
  61. return
  62. }
  63. // Eat spacing before flag.
  64. while (index < length) {
  65. character = value.charAt(index)
  66. if (character !== space && character !== tab) {
  67. break
  68. }
  69. subvalue += character
  70. index++
  71. }
  72. // Eat flag.
  73. flag = ''
  74. queue = ''
  75. while (index < length) {
  76. character = value.charAt(index)
  77. if (
  78. character === lineFeed ||
  79. (marker === graveAccent && character === marker)
  80. ) {
  81. break
  82. }
  83. if (character === space || character === tab) {
  84. queue += character
  85. } else {
  86. flag += queue + character
  87. queue = ''
  88. }
  89. index++
  90. }
  91. character = value.charAt(index)
  92. if (character && character !== lineFeed) {
  93. return
  94. }
  95. if (silent) {
  96. return true
  97. }
  98. now = eat.now()
  99. now.column += subvalue.length
  100. now.offset += subvalue.length
  101. subvalue += flag
  102. flag = self.decode.raw(self.unescape(flag), now)
  103. if (queue) {
  104. subvalue += queue
  105. }
  106. queue = ''
  107. closing = ''
  108. exdentedClosing = ''
  109. content = ''
  110. exdentedContent = ''
  111. var skip = true
  112. // Eat content.
  113. while (index < length) {
  114. character = value.charAt(index)
  115. content += closing
  116. exdentedContent += exdentedClosing
  117. closing = ''
  118. exdentedClosing = ''
  119. if (character !== lineFeed) {
  120. content += character
  121. exdentedClosing += character
  122. index++
  123. continue
  124. }
  125. // The first line feed is ignored. Others aren’t.
  126. if (skip) {
  127. subvalue += character
  128. skip = false
  129. } else {
  130. closing += character
  131. exdentedClosing += character
  132. }
  133. queue = ''
  134. index++
  135. while (index < length) {
  136. character = value.charAt(index)
  137. if (character !== space) {
  138. break
  139. }
  140. queue += character
  141. index++
  142. }
  143. closing += queue
  144. exdentedClosing += queue.slice(indent)
  145. if (queue.length >= tabSize) {
  146. continue
  147. }
  148. queue = ''
  149. while (index < length) {
  150. character = value.charAt(index)
  151. if (character !== marker) {
  152. break
  153. }
  154. queue += character
  155. index++
  156. }
  157. closing += queue
  158. exdentedClosing += queue
  159. if (queue.length < fenceCount) {
  160. continue
  161. }
  162. queue = ''
  163. while (index < length) {
  164. character = value.charAt(index)
  165. if (character !== space && character !== tab) {
  166. break
  167. }
  168. closing += character
  169. exdentedClosing += character
  170. index++
  171. }
  172. if (!character || character === lineFeed) {
  173. break
  174. }
  175. }
  176. subvalue += content + closing
  177. // Get lang and meta from the flag.
  178. index = -1
  179. length = flag.length
  180. while (++index < length) {
  181. character = flag.charAt(index)
  182. if (character === space || character === tab) {
  183. if (!lang) {
  184. lang = flag.slice(0, index)
  185. }
  186. } else if (lang) {
  187. meta = flag.slice(index)
  188. break
  189. }
  190. }
  191. return eat(subvalue)({
  192. type: 'code',
  193. lang: lang || flag || null,
  194. meta: meta || null,
  195. value: exdentedContent
  196. })
  197. }