/** * @import {Effects, State, TokenType} from 'micromark-util-types' */ import { asciiControl, markdownLineEndingOrSpace, markdownLineEnding } from 'micromark-util-character'; /** * Parse destinations. * * ###### Examples * * ```markdown * * b> * * * a * a\)b * a(b)c * a(b) * ``` * * @param {Effects} effects * Context. * @param {State} ok * State switched to when successful. * @param {State} nok * State switched to when unsuccessful. * @param {TokenType} type * Type for whole (`` or `b`). * @param {TokenType} literalType * Type when enclosed (``). * @param {TokenType} literalMarkerType * Type for enclosing (`<` and `>`). * @param {TokenType} rawType * Type when not enclosed (`b`). * @param {TokenType} stringType * Type for the value (`a` or `b`). * @param {number | undefined} [max=Infinity] * Depth of nested parens (inclusive). * @returns {State} * Start state. */ export function factoryDestination(effects, ok, nok, type, literalType, literalMarkerType, rawType, stringType, max) { const limit = max || Number.POSITIVE_INFINITY; let balance = 0; return start; /** * Start of destination. * * ```markdown * > | * ^ * > | aa * ^ * ``` * * @type {State} */ function start(code) { if (code === 60) { effects.enter(type); effects.enter(literalType); effects.enter(literalMarkerType); effects.consume(code); effects.exit(literalMarkerType); return enclosedBefore; } // ASCII control, space, closing paren. if (code === null || code === 32 || code === 41 || asciiControl(code)) { return nok(code); } effects.enter(type); effects.enter(rawType); effects.enter(stringType); effects.enter("chunkString", { contentType: "string" }); return raw(code); } /** * After `<`, at an enclosed destination. * * ```markdown * > | * ^ * ``` * * @type {State} */ function enclosedBefore(code) { if (code === 62) { effects.enter(literalMarkerType); effects.consume(code); effects.exit(literalMarkerType); effects.exit(literalType); effects.exit(type); return ok; } effects.enter(stringType); effects.enter("chunkString", { contentType: "string" }); return enclosed(code); } /** * In enclosed destination. * * ```markdown * > | * ^ * ``` * * @type {State} */ function enclosed(code) { if (code === 62) { effects.exit("chunkString"); effects.exit(stringType); return enclosedBefore(code); } if (code === null || code === 60 || markdownLineEnding(code)) { return nok(code); } effects.consume(code); return code === 92 ? enclosedEscape : enclosed; } /** * After `\`, at a special character. * * ```markdown * > | * ^ * ``` * * @type {State} */ function enclosedEscape(code) { if (code === 60 || code === 62 || code === 92) { effects.consume(code); return enclosed; } return enclosed(code); } /** * In raw destination. * * ```markdown * > | aa * ^ * ``` * * @type {State} */ function raw(code) { if (!balance && (code === null || code === 41 || markdownLineEndingOrSpace(code))) { effects.exit("chunkString"); effects.exit(stringType); effects.exit(rawType); effects.exit(type); return ok(code); } if (balance < limit && code === 40) { effects.consume(code); balance++; return raw; } if (code === 41) { effects.consume(code); balance--; return raw; } // ASCII control (but *not* `\0`) and space and `(`. // Note: in `markdown-rs`, `\0` exists in codes, in `micromark-js` it // doesn’t. if (code === null || code === 32 || code === 40 || asciiControl(code)) { return nok(code); } effects.consume(code); return code === 92 ? rawEscape : raw; } /** * After `\`, at special character. * * ```markdown * > | a\*a * ^ * ``` * * @type {State} */ function rawEscape(code) { if (code === 40 || code === 41 || code === 92) { effects.consume(code); return raw; } return raw(code); } }