/** * @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' import {codes, constants, types} from 'micromark-util-symbol' /** * 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 === codes.lessThan) { effects.enter(type) effects.enter(literalType) effects.enter(literalMarkerType) effects.consume(code) effects.exit(literalMarkerType) return enclosedBefore } // ASCII control, space, closing paren. if ( code === codes.eof || code === codes.space || code === codes.rightParenthesis || asciiControl(code) ) { return nok(code) } effects.enter(type) effects.enter(rawType) effects.enter(stringType) effects.enter(types.chunkString, {contentType: constants.contentTypeString}) return raw(code) } /** * After `<`, at an enclosed destination. * * ```markdown * > | * ^ * ``` * * @type {State} */ function enclosedBefore(code) { if (code === codes.greaterThan) { effects.enter(literalMarkerType) effects.consume(code) effects.exit(literalMarkerType) effects.exit(literalType) effects.exit(type) return ok } effects.enter(stringType) effects.enter(types.chunkString, {contentType: constants.contentTypeString}) return enclosed(code) } /** * In enclosed destination. * * ```markdown * > | * ^ * ``` * * @type {State} */ function enclosed(code) { if (code === codes.greaterThan) { effects.exit(types.chunkString) effects.exit(stringType) return enclosedBefore(code) } if ( code === codes.eof || code === codes.lessThan || markdownLineEnding(code) ) { return nok(code) } effects.consume(code) return code === codes.backslash ? enclosedEscape : enclosed } /** * After `\`, at a special character. * * ```markdown * > | * ^ * ``` * * @type {State} */ function enclosedEscape(code) { if ( code === codes.lessThan || code === codes.greaterThan || code === codes.backslash ) { effects.consume(code) return enclosed } return enclosed(code) } /** * In raw destination. * * ```markdown * > | aa * ^ * ``` * * @type {State} */ function raw(code) { if ( !balance && (code === codes.eof || code === codes.rightParenthesis || markdownLineEndingOrSpace(code)) ) { effects.exit(types.chunkString) effects.exit(stringType) effects.exit(rawType) effects.exit(type) return ok(code) } if (balance < limit && code === codes.leftParenthesis) { effects.consume(code) balance++ return raw } if (code === codes.rightParenthesis) { 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 === codes.eof || code === codes.space || code === codes.leftParenthesis || asciiControl(code) ) { return nok(code) } effects.consume(code) return code === codes.backslash ? rawEscape : raw } /** * After `\`, at special character. * * ```markdown * > | a\*a * ^ * ``` * * @type {State} */ function rawEscape(code) { if ( code === codes.leftParenthesis || code === codes.rightParenthesis || code === codes.backslash ) { effects.consume(code) return raw } return raw(code) } }