index.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. /**
  2. * @import {Effects, State, TokenType} from 'micromark-util-types'
  3. */
  4. import { asciiControl, markdownLineEndingOrSpace, markdownLineEnding } from 'micromark-util-character';
  5. /**
  6. * Parse destinations.
  7. *
  8. * ###### Examples
  9. *
  10. * ```markdown
  11. * <a>
  12. * <a\>b>
  13. * <a b>
  14. * <a)>
  15. * a
  16. * a\)b
  17. * a(b)c
  18. * a(b)
  19. * ```
  20. *
  21. * @param {Effects} effects
  22. * Context.
  23. * @param {State} ok
  24. * State switched to when successful.
  25. * @param {State} nok
  26. * State switched to when unsuccessful.
  27. * @param {TokenType} type
  28. * Type for whole (`<a>` or `b`).
  29. * @param {TokenType} literalType
  30. * Type when enclosed (`<a>`).
  31. * @param {TokenType} literalMarkerType
  32. * Type for enclosing (`<` and `>`).
  33. * @param {TokenType} rawType
  34. * Type when not enclosed (`b`).
  35. * @param {TokenType} stringType
  36. * Type for the value (`a` or `b`).
  37. * @param {number | undefined} [max=Infinity]
  38. * Depth of nested parens (inclusive).
  39. * @returns {State}
  40. * Start state.
  41. */
  42. export function factoryDestination(effects, ok, nok, type, literalType, literalMarkerType, rawType, stringType, max) {
  43. const limit = max || Number.POSITIVE_INFINITY;
  44. let balance = 0;
  45. return start;
  46. /**
  47. * Start of destination.
  48. *
  49. * ```markdown
  50. * > | <aa>
  51. * ^
  52. * > | aa
  53. * ^
  54. * ```
  55. *
  56. * @type {State}
  57. */
  58. function start(code) {
  59. if (code === 60) {
  60. effects.enter(type);
  61. effects.enter(literalType);
  62. effects.enter(literalMarkerType);
  63. effects.consume(code);
  64. effects.exit(literalMarkerType);
  65. return enclosedBefore;
  66. }
  67. // ASCII control, space, closing paren.
  68. if (code === null || code === 32 || code === 41 || asciiControl(code)) {
  69. return nok(code);
  70. }
  71. effects.enter(type);
  72. effects.enter(rawType);
  73. effects.enter(stringType);
  74. effects.enter("chunkString", {
  75. contentType: "string"
  76. });
  77. return raw(code);
  78. }
  79. /**
  80. * After `<`, at an enclosed destination.
  81. *
  82. * ```markdown
  83. * > | <aa>
  84. * ^
  85. * ```
  86. *
  87. * @type {State}
  88. */
  89. function enclosedBefore(code) {
  90. if (code === 62) {
  91. effects.enter(literalMarkerType);
  92. effects.consume(code);
  93. effects.exit(literalMarkerType);
  94. effects.exit(literalType);
  95. effects.exit(type);
  96. return ok;
  97. }
  98. effects.enter(stringType);
  99. effects.enter("chunkString", {
  100. contentType: "string"
  101. });
  102. return enclosed(code);
  103. }
  104. /**
  105. * In enclosed destination.
  106. *
  107. * ```markdown
  108. * > | <aa>
  109. * ^
  110. * ```
  111. *
  112. * @type {State}
  113. */
  114. function enclosed(code) {
  115. if (code === 62) {
  116. effects.exit("chunkString");
  117. effects.exit(stringType);
  118. return enclosedBefore(code);
  119. }
  120. if (code === null || code === 60 || markdownLineEnding(code)) {
  121. return nok(code);
  122. }
  123. effects.consume(code);
  124. return code === 92 ? enclosedEscape : enclosed;
  125. }
  126. /**
  127. * After `\`, at a special character.
  128. *
  129. * ```markdown
  130. * > | <a\*a>
  131. * ^
  132. * ```
  133. *
  134. * @type {State}
  135. */
  136. function enclosedEscape(code) {
  137. if (code === 60 || code === 62 || code === 92) {
  138. effects.consume(code);
  139. return enclosed;
  140. }
  141. return enclosed(code);
  142. }
  143. /**
  144. * In raw destination.
  145. *
  146. * ```markdown
  147. * > | aa
  148. * ^
  149. * ```
  150. *
  151. * @type {State}
  152. */
  153. function raw(code) {
  154. if (!balance && (code === null || code === 41 || markdownLineEndingOrSpace(code))) {
  155. effects.exit("chunkString");
  156. effects.exit(stringType);
  157. effects.exit(rawType);
  158. effects.exit(type);
  159. return ok(code);
  160. }
  161. if (balance < limit && code === 40) {
  162. effects.consume(code);
  163. balance++;
  164. return raw;
  165. }
  166. if (code === 41) {
  167. effects.consume(code);
  168. balance--;
  169. return raw;
  170. }
  171. // ASCII control (but *not* `\0`) and space and `(`.
  172. // Note: in `markdown-rs`, `\0` exists in codes, in `micromark-js` it
  173. // doesn’t.
  174. if (code === null || code === 32 || code === 40 || asciiControl(code)) {
  175. return nok(code);
  176. }
  177. effects.consume(code);
  178. return code === 92 ? rawEscape : raw;
  179. }
  180. /**
  181. * After `\`, at special character.
  182. *
  183. * ```markdown
  184. * > | a\*a
  185. * ^
  186. * ```
  187. *
  188. * @type {State}
  189. */
  190. function rawEscape(code) {
  191. if (code === 40 || code === 41 || code === 92) {
  192. effects.consume(code);
  193. return raw;
  194. }
  195. return raw(code);
  196. }
  197. }