block-quote.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. /**
  2. * @typedef {import('micromark-util-types').Construct} Construct
  3. * @typedef {import('micromark-util-types').Exiter} Exiter
  4. * @typedef {import('micromark-util-types').State} State
  5. * @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext
  6. * @typedef {import('micromark-util-types').Tokenizer} Tokenizer
  7. */
  8. import {factorySpace} from 'micromark-factory-space'
  9. import {markdownSpace} from 'micromark-util-character'
  10. /** @type {Construct} */
  11. export const blockQuote = {
  12. name: 'blockQuote',
  13. tokenize: tokenizeBlockQuoteStart,
  14. continuation: {
  15. tokenize: tokenizeBlockQuoteContinuation
  16. },
  17. exit
  18. }
  19. /**
  20. * @this {TokenizeContext}
  21. * @type {Tokenizer}
  22. */
  23. function tokenizeBlockQuoteStart(effects, ok, nok) {
  24. const self = this
  25. return start
  26. /**
  27. * Start of block quote.
  28. *
  29. * ```markdown
  30. * > | > a
  31. * ^
  32. * ```
  33. *
  34. * @type {State}
  35. */
  36. function start(code) {
  37. if (code === 62) {
  38. const state = self.containerState
  39. if (!state.open) {
  40. effects.enter('blockQuote', {
  41. _container: true
  42. })
  43. state.open = true
  44. }
  45. effects.enter('blockQuotePrefix')
  46. effects.enter('blockQuoteMarker')
  47. effects.consume(code)
  48. effects.exit('blockQuoteMarker')
  49. return after
  50. }
  51. return nok(code)
  52. }
  53. /**
  54. * After `>`, before optional whitespace.
  55. *
  56. * ```markdown
  57. * > | > a
  58. * ^
  59. * ```
  60. *
  61. * @type {State}
  62. */
  63. function after(code) {
  64. if (markdownSpace(code)) {
  65. effects.enter('blockQuotePrefixWhitespace')
  66. effects.consume(code)
  67. effects.exit('blockQuotePrefixWhitespace')
  68. effects.exit('blockQuotePrefix')
  69. return ok
  70. }
  71. effects.exit('blockQuotePrefix')
  72. return ok(code)
  73. }
  74. }
  75. /**
  76. * Start of block quote continuation.
  77. *
  78. * ```markdown
  79. * | > a
  80. * > | > b
  81. * ^
  82. * ```
  83. *
  84. * @this {TokenizeContext}
  85. * @type {Tokenizer}
  86. */
  87. function tokenizeBlockQuoteContinuation(effects, ok, nok) {
  88. const self = this
  89. return contStart
  90. /**
  91. * Start of block quote continuation.
  92. *
  93. * Also used to parse the first block quote opening.
  94. *
  95. * ```markdown
  96. * | > a
  97. * > | > b
  98. * ^
  99. * ```
  100. *
  101. * @type {State}
  102. */
  103. function contStart(code) {
  104. if (markdownSpace(code)) {
  105. // Always populated by defaults.
  106. return factorySpace(
  107. effects,
  108. contBefore,
  109. 'linePrefix',
  110. self.parser.constructs.disable.null.includes('codeIndented')
  111. ? undefined
  112. : 4
  113. )(code)
  114. }
  115. return contBefore(code)
  116. }
  117. /**
  118. * At `>`, after optional whitespace.
  119. *
  120. * Also used to parse the first block quote opening.
  121. *
  122. * ```markdown
  123. * | > a
  124. * > | > b
  125. * ^
  126. * ```
  127. *
  128. * @type {State}
  129. */
  130. function contBefore(code) {
  131. return effects.attempt(blockQuote, ok, nok)(code)
  132. }
  133. }
  134. /** @type {Exiter} */
  135. function exit(effects) {
  136. effects.exit('blockQuote')
  137. }