/** * @typedef {import('micromark-util-types').Code} Code * @typedef {import('micromark-util-types').Construct} Construct * @typedef {import('micromark-util-types').State} State * @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext * @typedef {import('micromark-util-types').Tokenizer} Tokenizer */ import {factorySpace} from 'micromark-factory-space' import {markdownLineEnding, markdownSpace} from 'micromark-util-character' import {codes, constants, types} from 'micromark-util-symbol' import {ok as assert} from 'devlop' /** @type {Construct} */ export const thematicBreak = { name: 'thematicBreak', tokenize: tokenizeThematicBreak } /** * @this {TokenizeContext} * @type {Tokenizer} */ function tokenizeThematicBreak(effects, ok, nok) { let size = 0 /** @type {NonNullable} */ let marker return start /** * Start of thematic break. * * ```markdown * > | *** * ^ * ``` * * @type {State} */ function start(code) { effects.enter(types.thematicBreak) // To do: parse indent like `markdown-rs`. return before(code) } /** * After optional whitespace, at marker. * * ```markdown * > | *** * ^ * ``` * * @type {State} */ function before(code) { assert( code === codes.asterisk || code === codes.dash || code === codes.underscore, 'expected `*`, `-`, or `_`' ) marker = code return atBreak(code) } /** * After something, before something else. * * ```markdown * > | *** * ^ * ``` * * @type {State} */ function atBreak(code) { if (code === marker) { effects.enter(types.thematicBreakSequence) return sequence(code) } if ( size >= constants.thematicBreakMarkerCountMin && (code === codes.eof || markdownLineEnding(code)) ) { effects.exit(types.thematicBreak) return ok(code) } return nok(code) } /** * In sequence. * * ```markdown * > | *** * ^ * ``` * * @type {State} */ function sequence(code) { if (code === marker) { effects.consume(code) size++ return sequence } effects.exit(types.thematicBreakSequence) return markdownSpace(code) ? factorySpace(effects, atBreak, types.whitespace)(code) : atBreak(code) } }