Urara-Blog/node_modules/.pnpm-store/v3/files/b2/90e7608674735147d863e2bc8ff37d3bdc3535dc451797d8f45a77261c395aa9d5b35a6fc6665c4a1df8dd33ff2626272d19b79f90f6824f52ecf527b349ea
2022-08-14 01:14:53 +08:00

276 lines
7.1 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @typedef {import('micromark-util-types').Construct} Construct
* @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext
* @typedef {import('micromark-util-types').Exiter} Exiter
* @typedef {import('micromark-util-types').Tokenizer} Tokenizer
* @typedef {import('micromark-util-types').State} State
* @typedef {import('micromark-util-types').Code} Code
*/
/**
* @typedef {Record<string, unknown> & {marker: Code, type: string, size: number}} ListContainerState
* @typedef {TokenizeContext & {containerState: ListContainerState}} TokenizeContextWithState
*/
import {ok as assert} from 'uvu/assert'
import {factorySpace} from 'micromark-factory-space'
import {asciiDigit, markdownSpace} from 'micromark-util-character'
import {codes} from 'micromark-util-symbol/codes.js'
import {constants} from 'micromark-util-symbol/constants.js'
import {types} from 'micromark-util-symbol/types.js'
import {blankLine} from './blank-line.js'
import {thematicBreak} from './thematic-break.js'
/** @type {Construct} */
export const list = {
name: 'list',
tokenize: tokenizeListStart,
continuation: {tokenize: tokenizeListContinuation},
exit: tokenizeListEnd
}
/** @type {Construct} */
const listItemPrefixWhitespaceConstruct = {
tokenize: tokenizeListItemPrefixWhitespace,
partial: true
}
/** @type {Construct} */
const indentConstruct = {tokenize: tokenizeIndent, partial: true}
/**
* @type {Tokenizer}
* @this {TokenizeContextWithState}
*/
function tokenizeListStart(effects, ok, nok) {
const self = this
const tail = self.events[self.events.length - 1]
let initialSize =
tail && tail[1].type === types.linePrefix
? tail[2].sliceSerialize(tail[1], true).length
: 0
let size = 0
return start
/** @type {State} */
function start(code) {
const kind =
self.containerState.type ||
(code === codes.asterisk || code === codes.plusSign || code === codes.dash
? types.listUnordered
: types.listOrdered)
if (
kind === types.listUnordered
? !self.containerState.marker || code === self.containerState.marker
: asciiDigit(code)
) {
if (!self.containerState.type) {
self.containerState.type = kind
effects.enter(kind, {_container: true})
}
if (kind === types.listUnordered) {
effects.enter(types.listItemPrefix)
return code === codes.asterisk || code === codes.dash
? effects.check(thematicBreak, nok, atMarker)(code)
: atMarker(code)
}
if (!self.interrupt || code === codes.digit1) {
effects.enter(types.listItemPrefix)
effects.enter(types.listItemValue)
return inside(code)
}
}
return nok(code)
}
/** @type {State} */
function inside(code) {
if (asciiDigit(code) && ++size < constants.listItemValueSizeMax) {
effects.consume(code)
return inside
}
if (
(!self.interrupt || size < 2) &&
(self.containerState.marker
? code === self.containerState.marker
: code === codes.rightParenthesis || code === codes.dot)
) {
effects.exit(types.listItemValue)
return atMarker(code)
}
return nok(code)
}
/**
* @type {State}
**/
function atMarker(code) {
assert(code !== codes.eof, 'eof (`null`) is not a marker')
effects.enter(types.listItemMarker)
effects.consume(code)
effects.exit(types.listItemMarker)
self.containerState.marker = self.containerState.marker || code
return effects.check(
blankLine,
// Cant be empty when interrupting.
self.interrupt ? nok : onBlank,
effects.attempt(
listItemPrefixWhitespaceConstruct,
endOfPrefix,
otherPrefix
)
)
}
/** @type {State} */
function onBlank(code) {
self.containerState.initialBlankLine = true
initialSize++
return endOfPrefix(code)
}
/** @type {State} */
function otherPrefix(code) {
if (markdownSpace(code)) {
effects.enter(types.listItemPrefixWhitespace)
effects.consume(code)
effects.exit(types.listItemPrefixWhitespace)
return endOfPrefix
}
return nok(code)
}
/** @type {State} */
function endOfPrefix(code) {
self.containerState.size =
initialSize +
self.sliceSerialize(effects.exit(types.listItemPrefix), true).length
return ok(code)
}
}
/**
* @type {Tokenizer}
* @this {TokenizeContextWithState}
*/
function tokenizeListContinuation(effects, ok, nok) {
const self = this
self.containerState._closeFlow = undefined
return effects.check(blankLine, onBlank, notBlank)
/** @type {State} */
function onBlank(code) {
self.containerState.furtherBlankLines =
self.containerState.furtherBlankLines ||
self.containerState.initialBlankLine
// We have a blank line.
// Still, try to consume at most the items size.
return factorySpace(
effects,
ok,
types.listItemIndent,
self.containerState.size + 1
)(code)
}
/** @type {State} */
function notBlank(code) {
if (self.containerState.furtherBlankLines || !markdownSpace(code)) {
self.containerState.furtherBlankLines = undefined
self.containerState.initialBlankLine = undefined
return notInCurrentItem(code)
}
self.containerState.furtherBlankLines = undefined
self.containerState.initialBlankLine = undefined
return effects.attempt(indentConstruct, ok, notInCurrentItem)(code)
}
/** @type {State} */
function notInCurrentItem(code) {
// While we do continue, we signal that the flow should be closed.
self.containerState._closeFlow = true
// As were closing flow, were no longer interrupting.
self.interrupt = undefined
return factorySpace(
effects,
effects.attempt(list, ok, nok),
types.linePrefix,
self.parser.constructs.disable.null.includes('codeIndented')
? undefined
: constants.tabSize
)(code)
}
}
/**
* @type {Tokenizer}
* @this {TokenizeContextWithState}
*/
function tokenizeIndent(effects, ok, nok) {
const self = this
return factorySpace(
effects,
afterPrefix,
types.listItemIndent,
self.containerState.size + 1
)
/** @type {State} */
function afterPrefix(code) {
const tail = self.events[self.events.length - 1]
return tail &&
tail[1].type === types.listItemIndent &&
tail[2].sliceSerialize(tail[1], true).length === self.containerState.size
? ok(code)
: nok(code)
}
}
/**
* @type {Exiter}
* @this {TokenizeContextWithState}
*/
function tokenizeListEnd(effects) {
effects.exit(this.containerState.type)
}
/**
* @type {Tokenizer}
* @this {TokenizeContextWithState}
*/
function tokenizeListItemPrefixWhitespace(effects, ok, nok) {
const self = this
return factorySpace(
effects,
afterPrefix,
types.listItemPrefixWhitespace,
self.parser.constructs.disable.null.includes('codeIndented')
? undefined
: constants.tabSize + 1
)
/** @type {State} */
function afterPrefix(code) {
const tail = self.events[self.events.length - 1]
return !markdownSpace(code) &&
tail &&
tail[1].type === types.listItemPrefixWhitespace
? ok(code)
: nok(code)
}
}