/** * @import { * Construct, * State, * TokenizeContext, * Tokenizer * } from 'micromark-util-types' */ import { factoryDestination } from 'micromark-factory-destination'; import { factoryLabel } from 'micromark-factory-label'; import { factorySpace } from 'micromark-factory-space'; import { factoryTitle } from 'micromark-factory-title'; import { factoryWhitespace } from 'micromark-factory-whitespace'; import { markdownLineEndingOrSpace, markdownLineEnding, markdownSpace } from 'micromark-util-character'; import { normalizeIdentifier } from 'micromark-util-normalize-identifier'; /** @type {Construct} */ export const definition = { name: 'definition', tokenize: tokenizeDefinition }; /** @type {Construct} */ const titleBefore = { partial: true, tokenize: tokenizeTitleBefore }; /** * @this {TokenizeContext} * Context. * @type {Tokenizer} */ function tokenizeDefinition(effects, ok, nok) { const self = this; /** @type {string} */ let identifier; return start; /** * At start of a definition. * * ```markdown * > | [a]: b "c" * ^ * ``` * * @type {State} */ function start(code) { // Do not interrupt paragraphs (but do follow definitions). // To do: do `interrupt` the way `markdown-rs` does. // To do: parse whitespace the way `markdown-rs` does. effects.enter("definition"); return before(code); } /** * After optional whitespace, at `[`. * * ```markdown * > | [a]: b "c" * ^ * ``` * * @type {State} */ function before(code) { // To do: parse whitespace the way `markdown-rs` does. return factoryLabel.call(self, effects, labelAfter, // Note: we don’t need to reset the way `markdown-rs` does. nok, "definitionLabel", "definitionLabelMarker", "definitionLabelString")(code); } /** * After label. * * ```markdown * > | [a]: b "c" * ^ * ``` * * @type {State} */ function labelAfter(code) { identifier = normalizeIdentifier(self.sliceSerialize(self.events[self.events.length - 1][1]).slice(1, -1)); if (code === 58) { effects.enter("definitionMarker"); effects.consume(code); effects.exit("definitionMarker"); return markerAfter; } return nok(code); } /** * After marker. * * ```markdown * > | [a]: b "c" * ^ * ``` * * @type {State} */ function markerAfter(code) { // Note: whitespace is optional. return markdownLineEndingOrSpace(code) ? factoryWhitespace(effects, destinationBefore)(code) : destinationBefore(code); } /** * Before destination. * * ```markdown * > | [a]: b "c" * ^ * ``` * * @type {State} */ function destinationBefore(code) { return factoryDestination(effects, destinationAfter, // Note: we don’t need to reset the way `markdown-rs` does. nok, "definitionDestination", "definitionDestinationLiteral", "definitionDestinationLiteralMarker", "definitionDestinationRaw", "definitionDestinationString")(code); } /** * After destination. * * ```markdown * > | [a]: b "c" * ^ * ``` * * @type {State} */ function destinationAfter(code) { return effects.attempt(titleBefore, after, after)(code); } /** * After definition. * * ```markdown * > | [a]: b * ^ * > | [a]: b "c" * ^ * ``` * * @type {State} */ function after(code) { return markdownSpace(code) ? factorySpace(effects, afterWhitespace, "whitespace")(code) : afterWhitespace(code); } /** * After definition, after optional whitespace. * * ```markdown * > | [a]: b * ^ * > | [a]: b "c" * ^ * ``` * * @type {State} */ function afterWhitespace(code) { if (code === null || markdownLineEnding(code)) { effects.exit("definition"); // Note: we don’t care about uniqueness. // It’s likely that that doesn’t happen very frequently. // It is more likely that it wastes precious time. self.parser.defined.push(identifier); // To do: `markdown-rs` interrupt. // // You’d be interrupting. // tokenizer.interrupt = true return ok(code); } return nok(code); } } /** * @this {TokenizeContext} * Context. * @type {Tokenizer} */ function tokenizeTitleBefore(effects, ok, nok) { return titleBefore; /** * After destination, at whitespace. * * ```markdown * > | [a]: b * ^ * > | [a]: b "c" * ^ * ``` * * @type {State} */ function titleBefore(code) { return markdownLineEndingOrSpace(code) ? factoryWhitespace(effects, beforeMarker)(code) : nok(code); } /** * At title. * * ```markdown * | [a]: b * > | "c" * ^ * ``` * * @type {State} */ function beforeMarker(code) { return factoryTitle(effects, titleAfter, nok, "definitionTitle", "definitionTitleMarker", "definitionTitleString")(code); } /** * After title. * * ```markdown * > | [a]: b "c" * ^ * ``` * * @type {State} */ function titleAfter(code) { return markdownSpace(code) ? factorySpace(effects, titleAfterOptionalWhitespace, "whitespace")(code) : titleAfterOptionalWhitespace(code); } /** * After title, after optional whitespace. * * ```markdown * > | [a]: b "c" * ^ * ``` * * @type {State} */ function titleAfterOptionalWhitespace(code) { return code === null || markdownLineEnding(code) ? ok(code) : nok(code); } }