/** * @import { * Effects, * State, * TokenizeContext, * TokenType * } from 'micromark-util-types' */ import { markdownLineEnding, markdownSpace } from 'micromark-util-character'; /** * Parse labels. * * > 👉 **Note**: labels in markdown are capped at 999 characters in the string. * * ###### Examples * * ```markdown * [a] * [a * b] * [a\]b] * ``` * * @this {TokenizeContext} * Tokenize context. * @param {Effects} effects * Context. * @param {State} ok * State switched to when successful. * @param {State} nok * State switched to when unsuccessful. * @param {TokenType} type * Type of the whole label (`[a]`). * @param {TokenType} markerType * Type for the markers (`[` and `]`). * @param {TokenType} stringType * Type for the identifier (`a`). * @returns {State} * Start state. */ export function factoryLabel(effects, ok, nok, type, markerType, stringType) { const self = this; let size = 0; /** @type {boolean} */ let seen; return start; /** * Start of label. * * ```markdown * > | [a] * ^ * ``` * * @type {State} */ function start(code) { effects.enter(type); effects.enter(markerType); effects.consume(code); effects.exit(markerType); effects.enter(stringType); return atBreak; } /** * In label, at something, before something else. * * ```markdown * > | [a] * ^ * ``` * * @type {State} */ function atBreak(code) { if (size > 999 || code === null || code === 91 || code === 93 && !seen || // To do: remove in the future once we’ve switched from // `micromark-extension-footnote` to `micromark-extension-gfm-footnote`, // which doesn’t need this. // Hidden footnotes hook. /* c8 ignore next 3 */ code === 94 && !size && '_hiddenFootnoteSupport' in self.parser.constructs) { return nok(code); } if (code === 93) { effects.exit(stringType); effects.enter(markerType); effects.consume(code); effects.exit(markerType); effects.exit(type); return ok; } // To do: indent? Link chunks and EOLs together? if (markdownLineEnding(code)) { effects.enter("lineEnding"); effects.consume(code); effects.exit("lineEnding"); return atBreak; } effects.enter("chunkString", { contentType: "string" }); return labelInside(code); } /** * In label, in text. * * ```markdown * > | [a] * ^ * ``` * * @type {State} */ function labelInside(code) { if (code === null || code === 91 || code === 93 || markdownLineEnding(code) || size++ > 999) { effects.exit("chunkString"); return atBreak(code); } effects.consume(code); if (!seen) seen = !markdownSpace(code); return code === 92 ? labelEscape : labelInside; } /** * After `\`, at a special character. * * ```markdown * > | [a\*a] * ^ * ``` * * @type {State} */ function labelEscape(code) { if (code === 91 || code === 92 || code === 93) { effects.consume(code); size++; return labelInside; } return labelInside(code); } }