site/node_modules/micromark-core-commonmark/dev/lib/code-indented.js

199 lines
4.4 KiB
JavaScript
Raw Permalink Normal View History

2024-10-14 06:09:33 +00:00
/**
* @typedef {import('micromark-util-types').Construct} Construct
* @typedef {import('micromark-util-types').State} State
* @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext
* @typedef {import('micromark-util-types').Tokenizer} Tokenizer
*/
import {factorySpace} from 'micromark-factory-space'
import {markdownLineEnding, markdownSpace} from 'micromark-util-character'
import {codes, constants, types} from 'micromark-util-symbol'
import {ok as assert} from 'devlop'
/** @type {Construct} */
export const codeIndented = {
name: 'codeIndented',
tokenize: tokenizeCodeIndented
}
/** @type {Construct} */
const furtherStart = {tokenize: tokenizeFurtherStart, partial: true}
/**
* @this {TokenizeContext}
* @type {Tokenizer}
*/
function tokenizeCodeIndented(effects, ok, nok) {
const self = this
return start
/**
* Start of code (indented).
*
* > **Parsing note**: it is not needed to check if this first line is a
* > filled line (that it has a non-whitespace character), because blank lines
* > are parsed already, so we never run into that.
*
* ```markdown
* > | aaa
* ^
* ```
*
* @type {State}
*/
function start(code) {
// To do: manually check if interrupting like `markdown-rs`.
assert(markdownSpace(code))
effects.enter(types.codeIndented)
// To do: use an improved `space_or_tab` function like `markdown-rs`,
// so that we can drop the next state.
return factorySpace(
effects,
afterPrefix,
types.linePrefix,
constants.tabSize + 1
)(code)
}
/**
* At start, after 1 or 4 spaces.
*
* ```markdown
* > | aaa
* ^
* ```
*
* @type {State}
*/
function afterPrefix(code) {
const tail = self.events[self.events.length - 1]
return tail &&
tail[1].type === types.linePrefix &&
tail[2].sliceSerialize(tail[1], true).length >= constants.tabSize
? atBreak(code)
: nok(code)
}
/**
* At a break.
*
* ```markdown
* > | aaa
* ^ ^
* ```
*
* @type {State}
*/
function atBreak(code) {
if (code === codes.eof) {
return after(code)
}
if (markdownLineEnding(code)) {
return effects.attempt(furtherStart, atBreak, after)(code)
}
effects.enter(types.codeFlowValue)
return inside(code)
}
/**
* In code content.
*
* ```markdown
* > | aaa
* ^^^^
* ```
*
* @type {State}
*/
function inside(code) {
if (code === codes.eof || markdownLineEnding(code)) {
effects.exit(types.codeFlowValue)
return atBreak(code)
}
effects.consume(code)
return inside
}
/** @type {State} */
function after(code) {
effects.exit(types.codeIndented)
// To do: allow interrupting like `markdown-rs`.
// Feel free to interrupt.
// tokenizer.interrupt = false
return ok(code)
}
}
/**
* @this {TokenizeContext}
* @type {Tokenizer}
*/
function tokenizeFurtherStart(effects, ok, nok) {
const self = this
return furtherStart
/**
* At eol, trying to parse another indent.
*
* ```markdown
* > | aaa
* ^
* | bbb
* ```
*
* @type {State}
*/
function furtherStart(code) {
// To do: improve `lazy` / `pierce` handling.
// If this is a lazy line, it cant be code.
if (self.parser.lazy[self.now().line]) {
return nok(code)
}
if (markdownLineEnding(code)) {
effects.enter(types.lineEnding)
effects.consume(code)
effects.exit(types.lineEnding)
return furtherStart
}
// To do: the code here in `micromark-js` is a bit different from
// `markdown-rs` because there it can attempt spaces.
// We cant yet.
//
// To do: use an improved `space_or_tab` function like `markdown-rs`,
// so that we can drop the next state.
return factorySpace(
effects,
afterPrefix,
types.linePrefix,
constants.tabSize + 1
)(code)
}
/**
* At start, after 1 or 4 spaces.
*
* ```markdown
* > | aaa
* ^
* ```
*
* @type {State}
*/
function afterPrefix(code) {
const tail = self.events[self.events.length - 1]
return tail &&
tail[1].type === types.linePrefix &&
tail[2].sliceSerialize(tail[1], true).length >= constants.tabSize
? ok(code)
: markdownLineEnding(code)
? furtherStart(code)
: nok(code)
}
}