/** * @typedef {import('micromark-util-types').Effects} Effects * @typedef {import('micromark-util-types').State} State * @typedef {import('micromark-util-types').TokenType} TokenType */ 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. */ // eslint-disable-next-line max-params 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) } }