/** * @typedef {import('micromark-util-types').Code} Code * @typedef {import('micromark-util-types').Effects} Effects * @typedef {import('micromark-util-types').State} State * @typedef {import('micromark-util-types').TokenType} TokenType */ import {factorySpace} from 'micromark-factory-space' import {markdownLineEnding} from 'micromark-util-character' import {codes, constants, types} from 'micromark-util-symbol' /** * Parse titles. * * ###### Examples * * ```markdown * "a" * 'b' * (c) * "a * b" * 'a * b' * (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 of the whole title (`"a"`, `'b'`, `(c)`). * @param {TokenType} markerType * Type for the markers (`"`, `'`, `(`, and `)`). * @param {TokenType} stringType * Type for the value (`a`). * @returns {State} * Start state. */ // eslint-disable-next-line max-params export function factoryTitle(effects, ok, nok, type, markerType, stringType) { /** @type {NonNullable} */ let marker return start /** * Start of title. * * ```markdown * > | "a" * ^ * ``` * * @type {State} */ function start(code) { if ( code === codes.quotationMark || code === codes.apostrophe || code === codes.leftParenthesis ) { effects.enter(type) effects.enter(markerType) effects.consume(code) effects.exit(markerType) marker = code === codes.leftParenthesis ? codes.rightParenthesis : code return begin } return nok(code) } /** * After opening marker. * * This is also used at the closing marker. * * ```markdown * > | "a" * ^ * ``` * * @type {State} */ function begin(code) { if (code === marker) { effects.enter(markerType) effects.consume(code) effects.exit(markerType) effects.exit(type) return ok } effects.enter(stringType) return atBreak(code) } /** * At something, before something else. * * ```markdown * > | "a" * ^ * ``` * * @type {State} */ function atBreak(code) { if (code === marker) { effects.exit(stringType) return begin(marker) } if (code === codes.eof) { return nok(code) } // Note: blank lines can’t exist in content. if (markdownLineEnding(code)) { // To do: use `space_or_tab_eol_with_options`, connect. effects.enter(types.lineEnding) effects.consume(code) effects.exit(types.lineEnding) return factorySpace(effects, atBreak, types.linePrefix) } effects.enter(types.chunkString, {contentType: constants.contentTypeString}) return inside(code) } /** * * * @type {State} */ function inside(code) { if (code === marker || code === codes.eof || markdownLineEnding(code)) { effects.exit(types.chunkString) return atBreak(code) } effects.consume(code) return code === codes.backslash ? escape : inside } /** * After `\`, at a special character. * * ```markdown * > | "a\*b" * ^ * ``` * * @type {State} */ function escape(code) { if (code === marker || code === codes.backslash) { effects.consume(code) return inside } return inside(code) } }