block-quote.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  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. import {codes, constants, types} from 'micromark-util-symbol'
  11. import {ok as assert} from 'devlop'
  12. /** @type {Construct} */
  13. export const blockQuote = {
  14. name: 'blockQuote',
  15. tokenize: tokenizeBlockQuoteStart,
  16. continuation: {tokenize: tokenizeBlockQuoteContinuation},
  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 === codes.greaterThan) {
  38. const state = self.containerState
  39. assert(state, 'expected `containerState` to be defined in container')
  40. if (!state.open) {
  41. effects.enter(types.blockQuote, {_container: true})
  42. state.open = true
  43. }
  44. effects.enter(types.blockQuotePrefix)
  45. effects.enter(types.blockQuoteMarker)
  46. effects.consume(code)
  47. effects.exit(types.blockQuoteMarker)
  48. return after
  49. }
  50. return nok(code)
  51. }
  52. /**
  53. * After `>`, before optional whitespace.
  54. *
  55. * ```markdown
  56. * > | > a
  57. * ^
  58. * ```
  59. *
  60. * @type {State}
  61. */
  62. function after(code) {
  63. if (markdownSpace(code)) {
  64. effects.enter(types.blockQuotePrefixWhitespace)
  65. effects.consume(code)
  66. effects.exit(types.blockQuotePrefixWhitespace)
  67. effects.exit(types.blockQuotePrefix)
  68. return ok
  69. }
  70. effects.exit(types.blockQuotePrefix)
  71. return ok(code)
  72. }
  73. }
  74. /**
  75. * Start of block quote continuation.
  76. *
  77. * ```markdown
  78. * | > a
  79. * > | > b
  80. * ^
  81. * ```
  82. *
  83. * @this {TokenizeContext}
  84. * @type {Tokenizer}
  85. */
  86. function tokenizeBlockQuoteContinuation(effects, ok, nok) {
  87. const self = this
  88. return contStart
  89. /**
  90. * Start of block quote continuation.
  91. *
  92. * Also used to parse the first block quote opening.
  93. *
  94. * ```markdown
  95. * | > a
  96. * > | > b
  97. * ^
  98. * ```
  99. *
  100. * @type {State}
  101. */
  102. function contStart(code) {
  103. if (markdownSpace(code)) {
  104. // Always populated by defaults.
  105. assert(
  106. self.parser.constructs.disable.null,
  107. 'expected `disable.null` to be populated'
  108. )
  109. return factorySpace(
  110. effects,
  111. contBefore,
  112. types.linePrefix,
  113. self.parser.constructs.disable.null.includes('codeIndented')
  114. ? undefined
  115. : constants.tabSize
  116. )(code)
  117. }
  118. return contBefore(code)
  119. }
  120. /**
  121. * At `>`, after optional whitespace.
  122. *
  123. * Also used to parse the first block quote opening.
  124. *
  125. * ```markdown
  126. * | > a
  127. * > | > b
  128. * ^
  129. * ```
  130. *
  131. * @type {State}
  132. */
  133. function contBefore(code) {
  134. return effects.attempt(blockQuote, ok, nok)(code)
  135. }
  136. }
  137. /** @type {Exiter} */
  138. function exit(effects) {
  139. effects.exit(types.blockQuote)
  140. }