content.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. /**
  2. * @typedef {import('micromark-util-types').Construct} Construct
  3. * @typedef {import('micromark-util-types').Resolver} Resolver
  4. * @typedef {import('micromark-util-types').State} State
  5. * @typedef {import('micromark-util-types').Token} Token
  6. * @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext
  7. * @typedef {import('micromark-util-types').Tokenizer} Tokenizer
  8. */
  9. import {factorySpace} from 'micromark-factory-space'
  10. import {markdownLineEnding} from 'micromark-util-character'
  11. import {subtokenize} from 'micromark-util-subtokenize'
  12. /**
  13. * No name because it must not be turned off.
  14. * @type {Construct}
  15. */
  16. export const content = {
  17. tokenize: tokenizeContent,
  18. resolve: resolveContent
  19. }
  20. /** @type {Construct} */
  21. const continuationConstruct = {
  22. tokenize: tokenizeContinuation,
  23. partial: true
  24. }
  25. /**
  26. * Content is transparent: it’s parsed right now. That way, definitions are also
  27. * parsed right now: before text in paragraphs (specifically, media) are parsed.
  28. *
  29. * @type {Resolver}
  30. */
  31. function resolveContent(events) {
  32. subtokenize(events)
  33. return events
  34. }
  35. /**
  36. * @this {TokenizeContext}
  37. * @type {Tokenizer}
  38. */
  39. function tokenizeContent(effects, ok) {
  40. /** @type {Token | undefined} */
  41. let previous
  42. return chunkStart
  43. /**
  44. * Before a content chunk.
  45. *
  46. * ```markdown
  47. * > | abc
  48. * ^
  49. * ```
  50. *
  51. * @type {State}
  52. */
  53. function chunkStart(code) {
  54. effects.enter('content')
  55. previous = effects.enter('chunkContent', {
  56. contentType: 'content'
  57. })
  58. return chunkInside(code)
  59. }
  60. /**
  61. * In a content chunk.
  62. *
  63. * ```markdown
  64. * > | abc
  65. * ^^^
  66. * ```
  67. *
  68. * @type {State}
  69. */
  70. function chunkInside(code) {
  71. if (code === null) {
  72. return contentEnd(code)
  73. }
  74. // To do: in `markdown-rs`, each line is parsed on its own, and everything
  75. // is stitched together resolving.
  76. if (markdownLineEnding(code)) {
  77. return effects.check(
  78. continuationConstruct,
  79. contentContinue,
  80. contentEnd
  81. )(code)
  82. }
  83. // Data.
  84. effects.consume(code)
  85. return chunkInside
  86. }
  87. /**
  88. *
  89. *
  90. * @type {State}
  91. */
  92. function contentEnd(code) {
  93. effects.exit('chunkContent')
  94. effects.exit('content')
  95. return ok(code)
  96. }
  97. /**
  98. *
  99. *
  100. * @type {State}
  101. */
  102. function contentContinue(code) {
  103. effects.consume(code)
  104. effects.exit('chunkContent')
  105. previous.next = effects.enter('chunkContent', {
  106. contentType: 'content',
  107. previous
  108. })
  109. previous = previous.next
  110. return chunkInside
  111. }
  112. }
  113. /**
  114. * @this {TokenizeContext}
  115. * @type {Tokenizer}
  116. */
  117. function tokenizeContinuation(effects, ok, nok) {
  118. const self = this
  119. return startLookahead
  120. /**
  121. *
  122. *
  123. * @type {State}
  124. */
  125. function startLookahead(code) {
  126. effects.exit('chunkContent')
  127. effects.enter('lineEnding')
  128. effects.consume(code)
  129. effects.exit('lineEnding')
  130. return factorySpace(effects, prefixed, 'linePrefix')
  131. }
  132. /**
  133. *
  134. *
  135. * @type {State}
  136. */
  137. function prefixed(code) {
  138. if (code === null || markdownLineEnding(code)) {
  139. return nok(code)
  140. }
  141. // Always populated by defaults.
  142. const tail = self.events[self.events.length - 1]
  143. if (
  144. !self.parser.constructs.disable.null.includes('codeIndented') &&
  145. tail &&
  146. tail[1].type === 'linePrefix' &&
  147. tail[2].sliceSerialize(tail[1], true).length >= 4
  148. ) {
  149. return ok(code)
  150. }
  151. return effects.interrupt(self.parser.constructs.flow, nok, ok)(code)
  152. }
  153. }