/** | |
* @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' | |
/** | |
* 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<Code>} */ | |
let marker | |
return start | |
/** | |
* Start of title. | |
* | |
* ```markdown | |
* > | "a" | |
* ^ | |
* ``` | |
* | |
* @type {State} | |
*/ | |
function start(code) { | |
if (code === 34 || code === 39 || code === 40) { | |
effects.enter(type) | |
effects.enter(markerType) | |
effects.consume(code) | |
effects.exit(markerType) | |
marker = code === 40 ? 41 : 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 === null) { | |
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('lineEnding') | |
effects.consume(code) | |
effects.exit('lineEnding') | |
return factorySpace(effects, atBreak, 'linePrefix') | |
} | |
effects.enter('chunkString', { | |
contentType: 'string' | |
}) | |
return inside(code) | |
} | |
/** | |
* | |
* | |
* @type {State} | |
*/ | |
function inside(code) { | |
if (code === marker || code === null || markdownLineEnding(code)) { | |
effects.exit('chunkString') | |
return atBreak(code) | |
} | |
effects.consume(code) | |
return code === 92 ? escape : inside | |
} | |
/** | |
* After `\`, at a special character. | |
* | |
* ```markdown | |
* > | "a\*b" | |
* ^ | |
* ``` | |
* | |
* @type {State} | |
*/ | |
function escape(code) { | |
if (code === marker || code === 92) { | |
effects.consume(code) | |
return inside | |
} | |
return inside(code) | |
} | |
} | |