123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- /**
- * @typedef {import('micromark-util-types').Effects} Effects
- * @typedef {import('micromark-util-types').State} State
- * @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext
- * @typedef {import('micromark-util-types').TokenType} TokenType
- */
- import {markdownLineEnding, markdownSpace} from 'micromark-util-character'
- import {codes, constants, types} from 'micromark-util-symbol'
- import {ok as assert} from 'devlop'
- /**
- * Parse labels.
- *
- * > 👉 **Note**: labels in markdown are capped at 999 characters in the string.
- *
- * ###### Examples
- *
- * ```markdown
- * [a]
- * [a
- * b]
- * [a\]b]
- * ```
- *
- * @this {TokenizeContext}
- * Tokenize context.
- * @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 label (`[a]`).
- * @param {TokenType} markerType
- * Type for the markers (`[` and `]`).
- * @param {TokenType} stringType
- * Type for the identifier (`a`).
- * @returns {State}
- * Start state.
- */
- // eslint-disable-next-line max-params
- export function factoryLabel(effects, ok, nok, type, markerType, stringType) {
- const self = this
- let size = 0
- /** @type {boolean} */
- let seen
- return start
- /**
- * Start of label.
- *
- * ```markdown
- * > | [a]
- * ^
- * ```
- *
- * @type {State}
- */
- function start(code) {
- assert(code === codes.leftSquareBracket, 'expected `[`')
- effects.enter(type)
- effects.enter(markerType)
- effects.consume(code)
- effects.exit(markerType)
- effects.enter(stringType)
- return atBreak
- }
- /**
- * In label, at something, before something else.
- *
- * ```markdown
- * > | [a]
- * ^
- * ```
- *
- * @type {State}
- */
- function atBreak(code) {
- if (
- size > constants.linkReferenceSizeMax ||
- code === codes.eof ||
- code === codes.leftSquareBracket ||
- (code === codes.rightSquareBracket && !seen) ||
- // To do: remove in the future once we’ve switched from
- // `micromark-extension-footnote` to `micromark-extension-gfm-footnote`,
- // which doesn’t need this.
- // Hidden footnotes hook.
- /* c8 ignore next 3 */
- (code === codes.caret &&
- !size &&
- '_hiddenFootnoteSupport' in self.parser.constructs)
- ) {
- return nok(code)
- }
- if (code === codes.rightSquareBracket) {
- effects.exit(stringType)
- effects.enter(markerType)
- effects.consume(code)
- effects.exit(markerType)
- effects.exit(type)
- return ok
- }
- // To do: indent? Link chunks and EOLs together?
- if (markdownLineEnding(code)) {
- effects.enter(types.lineEnding)
- effects.consume(code)
- effects.exit(types.lineEnding)
- return atBreak
- }
- effects.enter(types.chunkString, {contentType: constants.contentTypeString})
- return labelInside(code)
- }
- /**
- * In label, in text.
- *
- * ```markdown
- * > | [a]
- * ^
- * ```
- *
- * @type {State}
- */
- function labelInside(code) {
- if (
- code === codes.eof ||
- code === codes.leftSquareBracket ||
- code === codes.rightSquareBracket ||
- markdownLineEnding(code) ||
- size++ > constants.linkReferenceSizeMax
- ) {
- effects.exit(types.chunkString)
- return atBreak(code)
- }
- effects.consume(code)
- if (!seen) seen = !markdownSpace(code)
- return code === codes.backslash ? labelEscape : labelInside
- }
- /**
- * After `\`, at a special character.
- *
- * ```markdown
- * > | [a\*a]
- * ^
- * ```
- *
- * @type {State}
- */
- function labelEscape(code) {
- if (
- code === codes.leftSquareBracket ||
- code === codes.backslash ||
- code === codes.rightSquareBracket
- ) {
- effects.consume(code)
- size++
- return labelInside
- }
- return labelInside(code)
- }
- }
|