Files
est-frame/node_modules/micromark-extension-math/lib/math-flow.js
2025-10-22 05:38:27 +00:00

345 lines
6.7 KiB
JavaScript
Executable File

/**
* @import {Construct, State, TokenizeContext, Tokenizer} from 'micromark-util-types'
*/
import { factorySpace } from 'micromark-factory-space';
import { markdownLineEnding } from 'micromark-util-character';
/** @type {Construct} */
export const mathFlow = {
tokenize: tokenizeMathFenced,
concrete: true,
name: 'mathFlow'
};
/** @type {Construct} */
const nonLazyContinuation = {
tokenize: tokenizeNonLazyContinuation,
partial: true
};
/**
* @this {TokenizeContext}
* @type {Tokenizer}
*/
function tokenizeMathFenced(effects, ok, nok) {
const self = this;
const tail = self.events[self.events.length - 1];
const initialSize = tail && tail[1].type === "linePrefix" ? tail[2].sliceSerialize(tail[1], true).length : 0;
let sizeOpen = 0;
return start;
/**
* Start of math.
*
* ```markdown
* > | $$
* ^
* | \frac{1}{2}
* | $$
* ```
*
* @type {State}
*/
function start(code) {
effects.enter('mathFlow');
effects.enter('mathFlowFence');
effects.enter('mathFlowFenceSequence');
return sequenceOpen(code);
}
/**
* In opening fence sequence.
*
* ```markdown
* > | $$
* ^
* | \frac{1}{2}
* | $$
* ```
*
* @type {State}
*/
function sequenceOpen(code) {
if (code === 36) {
effects.consume(code);
sizeOpen++;
return sequenceOpen;
}
if (sizeOpen < 2) {
return nok(code);
}
effects.exit('mathFlowFenceSequence');
return factorySpace(effects, metaBefore, "whitespace")(code);
}
/**
* In opening fence, before meta.
*
* ```markdown
* > | $$asciimath
* ^
* | x < y
* | $$
* ```
*
* @type {State}
*/
function metaBefore(code) {
if (code === null || markdownLineEnding(code)) {
return metaAfter(code);
}
effects.enter('mathFlowFenceMeta');
effects.enter("chunkString", {
contentType: "string"
});
return meta(code);
}
/**
* In meta.
*
* ```markdown
* > | $$asciimath
* ^
* | x < y
* | $$
* ```
*
* @type {State}
*/
function meta(code) {
if (code === null || markdownLineEnding(code)) {
effects.exit("chunkString");
effects.exit('mathFlowFenceMeta');
return metaAfter(code);
}
if (code === 36) {
return nok(code);
}
effects.consume(code);
return meta;
}
/**
* After meta.
*
* ```markdown
* > | $$
* ^
* | \frac{1}{2}
* | $$
* ```
*
* @type {State}
*/
function metaAfter(code) {
// Guaranteed to be eol/eof.
effects.exit('mathFlowFence');
if (self.interrupt) {
return ok(code);
}
return effects.attempt(nonLazyContinuation, beforeNonLazyContinuation, after)(code);
}
/**
* After eol/eof in math, at a non-lazy closing fence or content.
*
* ```markdown
* | $$
* > | \frac{1}{2}
* ^
* > | $$
* ^
* ```
*
* @type {State}
*/
function beforeNonLazyContinuation(code) {
return effects.attempt({
tokenize: tokenizeClosingFence,
partial: true
}, after, contentStart)(code);
}
/**
* Before math content, definitely not before a closing fence.
*
* ```markdown
* | $$
* > | \frac{1}{2}
* ^
* | $$
* ```
*
* @type {State}
*/
function contentStart(code) {
return (initialSize ? factorySpace(effects, beforeContentChunk, "linePrefix", initialSize + 1) : beforeContentChunk)(code);
}
/**
* Before math content, after optional prefix.
*
* ```markdown
* | $$
* > | \frac{1}{2}
* ^
* | $$
* ```
*
* @type {State}
*/
function beforeContentChunk(code) {
if (code === null) {
return after(code);
}
if (markdownLineEnding(code)) {
return effects.attempt(nonLazyContinuation, beforeNonLazyContinuation, after)(code);
}
effects.enter('mathFlowValue');
return contentChunk(code);
}
/**
* In math content.
*
* ```markdown
* | $$
* > | \frac{1}{2}
* ^
* | $$
* ```
*
* @type {State}
*/
function contentChunk(code) {
if (code === null || markdownLineEnding(code)) {
effects.exit('mathFlowValue');
return beforeContentChunk(code);
}
effects.consume(code);
return contentChunk;
}
/**
* After math (ha!).
*
* ```markdown
* | $$
* | \frac{1}{2}
* > | $$
* ^
* ```
*
* @type {State}
*/
function after(code) {
effects.exit('mathFlow');
return ok(code);
}
/** @type {Tokenizer} */
function tokenizeClosingFence(effects, ok, nok) {
let size = 0;
/**
* Before closing fence, at optional whitespace.
*
* ```markdown
* | $$
* | \frac{1}{2}
* > | $$
* ^
* ```
*/
return factorySpace(effects, beforeSequenceClose, "linePrefix", self.parser.constructs.disable.null.includes('codeIndented') ? undefined : 4);
/**
* In closing fence, after optional whitespace, at sequence.
*
* ```markdown
* | $$
* | \frac{1}{2}
* > | $$
* ^
* ```
*
* @type {State}
*/
function beforeSequenceClose(code) {
effects.enter('mathFlowFence');
effects.enter('mathFlowFenceSequence');
return sequenceClose(code);
}
/**
* In closing fence sequence.
*
* ```markdown
* | $$
* | \frac{1}{2}
* > | $$
* ^
* ```
*
* @type {State}
*/
function sequenceClose(code) {
if (code === 36) {
size++;
effects.consume(code);
return sequenceClose;
}
if (size < sizeOpen) {
return nok(code);
}
effects.exit('mathFlowFenceSequence');
return factorySpace(effects, afterSequenceClose, "whitespace")(code);
}
/**
* After closing fence sequence, after optional whitespace.
*
* ```markdown
* | $$
* | \frac{1}{2}
* > | $$
* ^
* ```
*
* @type {State}
*/
function afterSequenceClose(code) {
if (code === null || markdownLineEnding(code)) {
effects.exit('mathFlowFence');
return ok(code);
}
return nok(code);
}
}
}
/**
* @this {TokenizeContext}
* @type {Tokenizer}
*/
function tokenizeNonLazyContinuation(effects, ok, nok) {
const self = this;
return start;
/** @type {State} */
function start(code) {
if (code === null) {
return ok(code);
}
effects.enter("lineEnding");
effects.consume(code);
effects.exit("lineEnding");
return lineStart;
}
/** @type {State} */
function lineStart(code) {
return self.parser.lazy[self.now().line] ? nok(code) : ok(code);
}
}