definition.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. /**
  2. * @import {
  3. * Construct,
  4. * State,
  5. * TokenizeContext,
  6. * Tokenizer
  7. * } from 'micromark-util-types'
  8. */
  9. import { factoryDestination } from 'micromark-factory-destination';
  10. import { factoryLabel } from 'micromark-factory-label';
  11. import { factorySpace } from 'micromark-factory-space';
  12. import { factoryTitle } from 'micromark-factory-title';
  13. import { factoryWhitespace } from 'micromark-factory-whitespace';
  14. import { markdownLineEndingOrSpace, markdownLineEnding, markdownSpace } from 'micromark-util-character';
  15. import { normalizeIdentifier } from 'micromark-util-normalize-identifier';
  16. /** @type {Construct} */
  17. export const definition = {
  18. name: 'definition',
  19. tokenize: tokenizeDefinition
  20. };
  21. /** @type {Construct} */
  22. const titleBefore = {
  23. partial: true,
  24. tokenize: tokenizeTitleBefore
  25. };
  26. /**
  27. * @this {TokenizeContext}
  28. * Context.
  29. * @type {Tokenizer}
  30. */
  31. function tokenizeDefinition(effects, ok, nok) {
  32. const self = this;
  33. /** @type {string} */
  34. let identifier;
  35. return start;
  36. /**
  37. * At start of a definition.
  38. *
  39. * ```markdown
  40. * > | [a]: b "c"
  41. * ^
  42. * ```
  43. *
  44. * @type {State}
  45. */
  46. function start(code) {
  47. // Do not interrupt paragraphs (but do follow definitions).
  48. // To do: do `interrupt` the way `markdown-rs` does.
  49. // To do: parse whitespace the way `markdown-rs` does.
  50. effects.enter("definition");
  51. return before(code);
  52. }
  53. /**
  54. * After optional whitespace, at `[`.
  55. *
  56. * ```markdown
  57. * > | [a]: b "c"
  58. * ^
  59. * ```
  60. *
  61. * @type {State}
  62. */
  63. function before(code) {
  64. // To do: parse whitespace the way `markdown-rs` does.
  65. return factoryLabel.call(self, effects, labelAfter,
  66. // Note: we don’t need to reset the way `markdown-rs` does.
  67. nok, "definitionLabel", "definitionLabelMarker", "definitionLabelString")(code);
  68. }
  69. /**
  70. * After label.
  71. *
  72. * ```markdown
  73. * > | [a]: b "c"
  74. * ^
  75. * ```
  76. *
  77. * @type {State}
  78. */
  79. function labelAfter(code) {
  80. identifier = normalizeIdentifier(self.sliceSerialize(self.events[self.events.length - 1][1]).slice(1, -1));
  81. if (code === 58) {
  82. effects.enter("definitionMarker");
  83. effects.consume(code);
  84. effects.exit("definitionMarker");
  85. return markerAfter;
  86. }
  87. return nok(code);
  88. }
  89. /**
  90. * After marker.
  91. *
  92. * ```markdown
  93. * > | [a]: b "c"
  94. * ^
  95. * ```
  96. *
  97. * @type {State}
  98. */
  99. function markerAfter(code) {
  100. // Note: whitespace is optional.
  101. return markdownLineEndingOrSpace(code) ? factoryWhitespace(effects, destinationBefore)(code) : destinationBefore(code);
  102. }
  103. /**
  104. * Before destination.
  105. *
  106. * ```markdown
  107. * > | [a]: b "c"
  108. * ^
  109. * ```
  110. *
  111. * @type {State}
  112. */
  113. function destinationBefore(code) {
  114. return factoryDestination(effects, destinationAfter,
  115. // Note: we don’t need to reset the way `markdown-rs` does.
  116. nok, "definitionDestination", "definitionDestinationLiteral", "definitionDestinationLiteralMarker", "definitionDestinationRaw", "definitionDestinationString")(code);
  117. }
  118. /**
  119. * After destination.
  120. *
  121. * ```markdown
  122. * > | [a]: b "c"
  123. * ^
  124. * ```
  125. *
  126. * @type {State}
  127. */
  128. function destinationAfter(code) {
  129. return effects.attempt(titleBefore, after, after)(code);
  130. }
  131. /**
  132. * After definition.
  133. *
  134. * ```markdown
  135. * > | [a]: b
  136. * ^
  137. * > | [a]: b "c"
  138. * ^
  139. * ```
  140. *
  141. * @type {State}
  142. */
  143. function after(code) {
  144. return markdownSpace(code) ? factorySpace(effects, afterWhitespace, "whitespace")(code) : afterWhitespace(code);
  145. }
  146. /**
  147. * After definition, after optional whitespace.
  148. *
  149. * ```markdown
  150. * > | [a]: b
  151. * ^
  152. * > | [a]: b "c"
  153. * ^
  154. * ```
  155. *
  156. * @type {State}
  157. */
  158. function afterWhitespace(code) {
  159. if (code === null || markdownLineEnding(code)) {
  160. effects.exit("definition");
  161. // Note: we don’t care about uniqueness.
  162. // It’s likely that that doesn’t happen very frequently.
  163. // It is more likely that it wastes precious time.
  164. self.parser.defined.push(identifier);
  165. // To do: `markdown-rs` interrupt.
  166. // // You’d be interrupting.
  167. // tokenizer.interrupt = true
  168. return ok(code);
  169. }
  170. return nok(code);
  171. }
  172. }
  173. /**
  174. * @this {TokenizeContext}
  175. * Context.
  176. * @type {Tokenizer}
  177. */
  178. function tokenizeTitleBefore(effects, ok, nok) {
  179. return titleBefore;
  180. /**
  181. * After destination, at whitespace.
  182. *
  183. * ```markdown
  184. * > | [a]: b
  185. * ^
  186. * > | [a]: b "c"
  187. * ^
  188. * ```
  189. *
  190. * @type {State}
  191. */
  192. function titleBefore(code) {
  193. return markdownLineEndingOrSpace(code) ? factoryWhitespace(effects, beforeMarker)(code) : nok(code);
  194. }
  195. /**
  196. * At title.
  197. *
  198. * ```markdown
  199. * | [a]: b
  200. * > | "c"
  201. * ^
  202. * ```
  203. *
  204. * @type {State}
  205. */
  206. function beforeMarker(code) {
  207. return factoryTitle(effects, titleAfter, nok, "definitionTitle", "definitionTitleMarker", "definitionTitleString")(code);
  208. }
  209. /**
  210. * After title.
  211. *
  212. * ```markdown
  213. * > | [a]: b "c"
  214. * ^
  215. * ```
  216. *
  217. * @type {State}
  218. */
  219. function titleAfter(code) {
  220. return markdownSpace(code) ? factorySpace(effects, titleAfterOptionalWhitespace, "whitespace")(code) : titleAfterOptionalWhitespace(code);
  221. }
  222. /**
  223. * After title, after optional whitespace.
  224. *
  225. * ```markdown
  226. * > | [a]: b "c"
  227. * ^
  228. * ```
  229. *
  230. * @type {State}
  231. */
  232. function titleAfterOptionalWhitespace(code) {
  233. return code === null || markdownLineEnding(code) ? ok(code) : nok(code);
  234. }
  235. }