code-indented.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. /**
  2. * @import {
  3. * Construct,
  4. * State,
  5. * TokenizeContext,
  6. * Tokenizer
  7. * } from 'micromark-util-types'
  8. */
  9. import { factorySpace } from 'micromark-factory-space';
  10. import { markdownLineEnding, markdownSpace } from 'micromark-util-character';
  11. /** @type {Construct} */
  12. export const codeIndented = {
  13. name: 'codeIndented',
  14. tokenize: tokenizeCodeIndented
  15. };
  16. /** @type {Construct} */
  17. const furtherStart = {
  18. partial: true,
  19. tokenize: tokenizeFurtherStart
  20. };
  21. /**
  22. * @this {TokenizeContext}
  23. * Context.
  24. * @type {Tokenizer}
  25. */
  26. function tokenizeCodeIndented(effects, ok, nok) {
  27. const self = this;
  28. return start;
  29. /**
  30. * Start of code (indented).
  31. *
  32. * > **Parsing note**: it is not needed to check if this first line is a
  33. * > filled line (that it has a non-whitespace character), because blank lines
  34. * > are parsed already, so we never run into that.
  35. *
  36. * ```markdown
  37. * > | aaa
  38. * ^
  39. * ```
  40. *
  41. * @type {State}
  42. */
  43. function start(code) {
  44. // To do: manually check if interrupting like `markdown-rs`.
  45. effects.enter("codeIndented");
  46. // To do: use an improved `space_or_tab` function like `markdown-rs`,
  47. // so that we can drop the next state.
  48. return factorySpace(effects, afterPrefix, "linePrefix", 4 + 1)(code);
  49. }
  50. /**
  51. * At start, after 1 or 4 spaces.
  52. *
  53. * ```markdown
  54. * > | aaa
  55. * ^
  56. * ```
  57. *
  58. * @type {State}
  59. */
  60. function afterPrefix(code) {
  61. const tail = self.events[self.events.length - 1];
  62. return tail && tail[1].type === "linePrefix" && tail[2].sliceSerialize(tail[1], true).length >= 4 ? atBreak(code) : nok(code);
  63. }
  64. /**
  65. * At a break.
  66. *
  67. * ```markdown
  68. * > | aaa
  69. * ^ ^
  70. * ```
  71. *
  72. * @type {State}
  73. */
  74. function atBreak(code) {
  75. if (code === null) {
  76. return after(code);
  77. }
  78. if (markdownLineEnding(code)) {
  79. return effects.attempt(furtherStart, atBreak, after)(code);
  80. }
  81. effects.enter("codeFlowValue");
  82. return inside(code);
  83. }
  84. /**
  85. * In code content.
  86. *
  87. * ```markdown
  88. * > | aaa
  89. * ^^^^
  90. * ```
  91. *
  92. * @type {State}
  93. */
  94. function inside(code) {
  95. if (code === null || markdownLineEnding(code)) {
  96. effects.exit("codeFlowValue");
  97. return atBreak(code);
  98. }
  99. effects.consume(code);
  100. return inside;
  101. }
  102. /** @type {State} */
  103. function after(code) {
  104. effects.exit("codeIndented");
  105. // To do: allow interrupting like `markdown-rs`.
  106. // Feel free to interrupt.
  107. // tokenizer.interrupt = false
  108. return ok(code);
  109. }
  110. }
  111. /**
  112. * @this {TokenizeContext}
  113. * Context.
  114. * @type {Tokenizer}
  115. */
  116. function tokenizeFurtherStart(effects, ok, nok) {
  117. const self = this;
  118. return furtherStart;
  119. /**
  120. * At eol, trying to parse another indent.
  121. *
  122. * ```markdown
  123. * > | aaa
  124. * ^
  125. * | bbb
  126. * ```
  127. *
  128. * @type {State}
  129. */
  130. function furtherStart(code) {
  131. // To do: improve `lazy` / `pierce` handling.
  132. // If this is a lazy line, it can’t be code.
  133. if (self.parser.lazy[self.now().line]) {
  134. return nok(code);
  135. }
  136. if (markdownLineEnding(code)) {
  137. effects.enter("lineEnding");
  138. effects.consume(code);
  139. effects.exit("lineEnding");
  140. return furtherStart;
  141. }
  142. // To do: the code here in `micromark-js` is a bit different from
  143. // `markdown-rs` because there it can attempt spaces.
  144. // We can’t yet.
  145. //
  146. // To do: use an improved `space_or_tab` function like `markdown-rs`,
  147. // so that we can drop the next state.
  148. return factorySpace(effects, afterPrefix, "linePrefix", 4 + 1)(code);
  149. }
  150. /**
  151. * At start, after 1 or 4 spaces.
  152. *
  153. * ```markdown
  154. * > | aaa
  155. * ^
  156. * ```
  157. *
  158. * @type {State}
  159. */
  160. function afterPrefix(code) {
  161. const tail = self.events[self.events.length - 1];
  162. return tail && tail[1].type === "linePrefix" && tail[2].sliceSerialize(tail[1], true).length >= 4 ? ok(code) : markdownLineEnding(code) ? furtherStart(code) : nok(code);
  163. }
  164. }