site/node_modules/micromark-factory-title/index.js

157 lines
3.1 KiB
JavaScript
Raw Normal View History

2024-10-14 06:09:33 +00:00
/**
* @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 cant 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)
}
}