definition.js 6.1 KB

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