UNPKG

micromark-extension-directive

Version:

micromark extension to support generic directives (`:cite[smith04]`)

238 lines (220 loc) 7.55 kB
/** * @import {Code, Effects, State, TokenType} from 'micromark-util-types' */ import { factorySpace } from 'micromark-factory-space'; import { factoryWhitespace } from 'micromark-factory-whitespace'; import { markdownLineEnding, markdownLineEndingOrSpace, markdownSpace, unicodePunctuation, unicodeWhitespace } from 'micromark-util-character'; /** * @param {Effects} effects * @param {State} ok * @param {State} nok * @param {TokenType} attributesType * @param {TokenType} attributesMarkerType * @param {TokenType} attributeType * @param {TokenType} attributeIdType * @param {TokenType} attributeClassType * @param {TokenType} attributeNameType * @param {TokenType} attributeInitializerType * @param {TokenType} attributeValueLiteralType * @param {TokenType} attributeValueType * @param {TokenType} attributeValueMarker * @param {TokenType} attributeValueData * @param {boolean | undefined} [disallowEol=false] */ export function factoryAttributes(effects, ok, nok, attributesType, attributesMarkerType, attributeType, attributeIdType, attributeClassType, attributeNameType, attributeInitializerType, attributeValueLiteralType, attributeValueType, attributeValueMarker, attributeValueData, disallowEol) { /** @type {TokenType} */ let type; /** @type {Code | undefined} */ let marker; return start; /** @type {State} */ function start(code) { effects.enter(attributesType); effects.enter(attributesMarkerType); effects.consume(code); effects.exit(attributesMarkerType); return between; } /** @type {State} */ function between(code) { if (code === 35) { type = attributeIdType; return shortcutStart(code); } if (code === 46) { type = attributeClassType; return shortcutStart(code); } if (disallowEol && markdownSpace(code)) { return factorySpace(effects, between, "whitespace")(code); } if (!disallowEol && markdownLineEndingOrSpace(code)) { return factoryWhitespace(effects, between)(code); } if (code === null || markdownLineEnding(code) || unicodeWhitespace(code) || unicodePunctuation(code) && code !== 45 && code !== 95) { return end(code); } effects.enter(attributeType); effects.enter(attributeNameType); effects.consume(code); return name; } /** @type {State} */ function shortcutStart(code) { // Assume it’s registered. const markerType = /** @type {TokenType} */type + 'Marker'; effects.enter(attributeType); effects.enter(type); effects.enter(markerType); effects.consume(code); effects.exit(markerType); return shortcutStartAfter; } /** @type {State} */ function shortcutStartAfter(code) { if (code === null || code === 34 || code === 35 || code === 39 || code === 46 || code === 60 || code === 61 || code === 62 || code === 96 || code === 125 || markdownLineEndingOrSpace(code)) { return nok(code); } // Assume it’s registered. const valueType = /** @type {TokenType} */type + 'Value'; effects.enter(valueType); effects.consume(code); return shortcut; } /** @type {State} */ function shortcut(code) { if (code === null || code === 34 || code === 39 || code === 60 || code === 61 || code === 62 || code === 96) { return nok(code); } if (code === 35 || code === 46 || code === 125 || markdownLineEndingOrSpace(code)) { // Assume it’s registered. const valueType = /** @type {TokenType} */type + 'Value'; effects.exit(valueType); effects.exit(type); effects.exit(attributeType); return between(code); } effects.consume(code); return shortcut; } /** @type {State} */ function name(code) { if (code === null || markdownLineEnding(code) || unicodeWhitespace(code) || unicodePunctuation(code) && code !== 45 && code !== 46 && code !== 58 && code !== 95) { effects.exit(attributeNameType); if (disallowEol && markdownSpace(code)) { return factorySpace(effects, nameAfter, "whitespace")(code); } if (!disallowEol && markdownLineEndingOrSpace(code)) { return factoryWhitespace(effects, nameAfter)(code); } return nameAfter(code); } effects.consume(code); return name; } /** @type {State} */ function nameAfter(code) { if (code === 61) { effects.enter(attributeInitializerType); effects.consume(code); effects.exit(attributeInitializerType); return valueBefore; } // Attribute w/o value. effects.exit(attributeType); return between(code); } /** @type {State} */ function valueBefore(code) { if (code === null || code === 60 || code === 61 || code === 62 || code === 96 || code === 125 || disallowEol && markdownLineEnding(code)) { return nok(code); } if (code === 34 || code === 39) { effects.enter(attributeValueLiteralType); effects.enter(attributeValueMarker); effects.consume(code); effects.exit(attributeValueMarker); marker = code; return valueQuotedStart; } if (disallowEol && markdownSpace(code)) { return factorySpace(effects, valueBefore, "whitespace")(code); } if (!disallowEol && markdownLineEndingOrSpace(code)) { return factoryWhitespace(effects, valueBefore)(code); } effects.enter(attributeValueType); effects.enter(attributeValueData); effects.consume(code); marker = undefined; return valueUnquoted; } /** @type {State} */ function valueUnquoted(code) { if (code === null || code === 34 || code === 39 || code === 60 || code === 61 || code === 62 || code === 96) { return nok(code); } if (code === 125 || markdownLineEndingOrSpace(code)) { effects.exit(attributeValueData); effects.exit(attributeValueType); effects.exit(attributeType); return between(code); } effects.consume(code); return valueUnquoted; } /** @type {State} */ function valueQuotedStart(code) { if (code === marker) { effects.enter(attributeValueMarker); effects.consume(code); effects.exit(attributeValueMarker); effects.exit(attributeValueLiteralType); effects.exit(attributeType); return valueQuotedAfter; } effects.enter(attributeValueType); return valueQuotedBetween(code); } /** @type {State} */ function valueQuotedBetween(code) { if (code === marker) { effects.exit(attributeValueType); return valueQuotedStart(code); } if (code === null) { return nok(code); } // Note: blank lines can’t exist in content. if (markdownLineEnding(code)) { return disallowEol ? nok(code) : factoryWhitespace(effects, valueQuotedBetween)(code); } effects.enter(attributeValueData); effects.consume(code); return valueQuoted; } /** @type {State} */ function valueQuoted(code) { if (code === marker || code === null || markdownLineEnding(code)) { effects.exit(attributeValueData); return valueQuotedBetween(code); } effects.consume(code); return valueQuoted; } /** @type {State} */ function valueQuotedAfter(code) { return code === 125 || markdownLineEndingOrSpace(code) ? between(code) : end(code); } /** @type {State} */ function end(code) { if (code === 125) { effects.enter(attributesMarkerType); effects.consume(code); effects.exit(attributesMarkerType); effects.exit(attributesType); return ok; } return nok(code); } }