mathpix-markdown-it
Version:
Mathpix-markdown-it is an open source implementation of the mathpix-markdown spec written in Typescript. It relies on the following open source libraries: MathJax v3 (to render math with SVGs), markdown-it (for standard Markdown parsing)
295 lines • 14.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Lists = void 0;
var latex_list_tokens_1 = require("./latex-list-tokens");
var latex_list_items_1 = require("./latex-list-items");
var re_level_1 = require("./re-level");
var latex_list_types_1 = require("./latex-list-types");
var latex_list_common_1 = require("./latex-list-common");
var consts_1 = require("../common/consts");
/**
* Try to handle an inline `\begin{lstlisting}` on the given line.
*
* Behavior:
* - If already inside a lstlisting environment (`envDepth > 0`), does nothing.
* - If a `\begin{lstlisting}` is found:
* - Text before the begin is appended either as a new list item (when it matches `itemTag`)
* or concatenated to the previous item.
* - Sets `envDepth` to 1 (entered lstlisting).
* - Appends the substring starting at `\begin{lstlisting}` to the end of the line
* as code content in the current item.
*
* The function does not mutate inputs; it returns the updated state.
*
* @param lineText The full text of the current line.
* @param envDepth Current lstlisting nesting depth.
* @param items Collected items so far (list builder state).
* @param nextLine The current (next) line index used for item position metadata.
* @param dStart Document start line offset to compute absolute positions.
* @param itemTag RegExp to detect list item prefixes (e.g., `^\s*\\item`).
* @returns Updated handling result with flags, depth, items, and original line text.
*/
var handleLstBeginInline = function (lineText, envDepth, items, nextLine, dStart, itemTag) {
// If already inside lstlisting, do nothing.
if (envDepth > 0) {
return { handled: false, envDepth: envDepth, items: items, lineText: lineText };
}
var mb = consts_1.BEGIN_LST_INLINE_RE.exec(lineText);
if (!mb) {
return { handled: false, envDepth: envDepth, items: items, lineText: lineText };
}
var beginIndex = mb.index;
// Is there text BEFORE \begin{lstlisting} ?
var before = lineText.slice(0, beginIndex).trimEnd();
var afterBegin = lineText.slice(beginIndex); // start from \begin...
// If there was something before begin, it was regular text/part of \item:
if (before.length > 0) {
if (itemTag.test(before)) {
items = (0, latex_list_items_1.ItemsListPush)(items, before, nextLine + dStart, nextLine + dStart);
}
else {
items = (0, latex_list_items_1.ItemsAddToPrev)(items, before, nextLine);
}
}
envDepth = 1; //entered lstlisting
items = (0, latex_list_items_1.ItemsAddToPrev)(items, afterBegin, nextLine); //The part from \begin{lstlisting} to the end of the line is considered a code string.
return { handled: true, envDepth: envDepth, items: items, lineText: lineText };
};
/**
* Try to handle an inline `\end{lstlisting}` on the current line.
*
* Behavior:
* - If not inside an lstlisting environment (`envDepth === 0`), does nothing.
* - If no end marker is found on this line, appends the full line (with original leading whitespace)
* to the current item and reports `handled: true` (still inside the env).
* - If an end marker is present:
* - Appends everything up to `\end{...}` (plus the end token itself) to the current item.
* - Resets `envDepth` to 0 (leaves lstlisting).
* - If there is trailing text after the end token, returns it in `lineText` so the caller
* can continue processing the remainder of the line; otherwise returns an empty `lineText`.
*
* The function does not mutate inputs; it returns the updated state.
*
* @param lineText Current line text (may contain `\end{lstlisting}`).
* @param envDepth Current lstlisting nesting depth (0 if outside).
* @param items Accumulated items list (list builder state).
* @param nextLine Line index used for item position metadata.
* @param state Markdown-It state (used to read the original line with indentation).
* @returns Updated result with flags, depth, items, and remaining line text (if any).
*/
var handleLstEndInline = function (lineText, envDepth, items, nextLine, state) {
var _a;
// If we are not inside lstlisting, we exit
if (envDepth === 0) {
return { handled: false, envDepth: envDepth, items: items, lineText: lineText };
}
var me = consts_1.END_LST_INLINE_RE.exec(lineText);
if (!me) {
// There is no end of environment - just add the line as is
lineText = state.src.slice(state.bMarks[nextLine], state.eMarks[nextLine]); // It is important to take into account the leading whitespace characters.
items = (0, latex_list_items_1.ItemsAddToPrev)(items, lineText, nextLine);
return { handled: true, envDepth: envDepth, items: items, lineText: lineText };
}
// There is an end of environment in this line
var endIndex = me.index;
var endToken = lineText.slice(endIndex, endIndex + me[0].length);
var beforeEnd = lineText.slice(0, endIndex);
var afterEnd = lineText.slice(endIndex + me[0].length);
// Everything up to \end{...} is a continuation of the code
if (beforeEnd.length > 0) {
items = (0, latex_list_items_1.ItemsAddToPrev)(items, beforeEnd + '\n' + endToken, nextLine);
}
else {
items = (0, latex_list_items_1.ItemsAddToPrev)(items, endToken, nextLine);
}
envDepth = 0; // Exit lstlisting
if (!((_a = afterEnd === null || afterEnd === void 0 ? void 0 : afterEnd.trim()) === null || _a === void 0 ? void 0 : _a.length)) {
return { handled: true, envDepth: envDepth, items: items, lineText: '' };
}
return { handled: false, envDepth: envDepth, items: items, lineText: afterEnd };
};
/**
* Block rule that parses LaTeX list environments:
* \begin{itemize} ... \end{itemize}
* \begin{enumerate} ... \end{enumerate}
*
* It:
* - detects list begin/end commands,
* - collects and splits \item content into logical items,
* - handles \setcounter and nested lists on the same line,
* - emits corresponding *_list_open, *_list_close, and list item tokens.
*/
var Lists = function (state, startLine, endLine, silent) {
var _a, _b;
var _c, _d, _e;
var pos = state.bMarks[startLine] + state.tShift[startLine];
var max = state.eMarks[startLine];
var lineText = state.src.slice(pos, max);
// Must start with backslash to be LaTeX command
if (lineText.charCodeAt(0) !== 0x5c /* '\' */) {
return false;
}
var match = lineText.match(consts_1.BEGIN_LIST_ENV_RE);
if (!match) {
return false;
}
var typeList = match[1].trim();
if (!(0, latex_list_types_1.isListType)(typeList)) {
return false;
}
// In silent mode: only report that this block can start; do not modify state or emit tokens.
if (silent) {
return true;
}
var renderStart = state.md.options.renderElement && state.md.options.renderElement.startLine
? Number(state.md.options.renderElement.startLine)
: 0;
var oldParentType = state.parentType;
var enumerateLevelTypes = (0, re_level_1.GetEnumerateLevel)();
var dataMarkers = (0, re_level_1.GetItemizeLevelTokensByState)(state);
var itemizeLevelTokens = dataMarkers.tokens;
var itemizeLevelContents = dataMarkers.contents;
var nextLine = startLine;
var li = null;
var openData = (0, latex_list_tokens_1.ListOpen)(state, startLine + renderStart, lineText, itemizeLevelTokens, enumerateLevelTypes, itemizeLevelContents);
var _f = openData.iOpen, iOpen = _f === void 0 ? 0 : _f, _g = openData.tokenStart, tokenStart = _g === void 0 ? null : _g;
li = (_c = openData.li) !== null && _c !== void 0 ? _c : null;
if (iOpen === 0) {
nextLine += 1;
state.line = nextLine;
state.startLine = startLine;
state.parentType = oldParentType;
state.level = state.prentLevel < 0 ? 0 : state.prentLevel;
return true;
}
else {
nextLine += 1;
}
var items = [];
var haveClose = false;
var envDepth = 0; // >0 — inside lstlisting environment
for (; nextLine < endLine; nextLine++) {
pos = state.bMarks[nextLine] + state.tShift[nextLine];
max = state.eMarks[nextLine];
lineText = state.src.slice(pos, max);
// 1) If you are NOT currently inside lstlisting, first search for \begin{lstlisting}
if (envDepth === 0) {
var beginRes = handleLstBeginInline(lineText, envDepth, items, nextLine, renderStart, consts_1.LATEX_ITEM_COMMAND_INLINE_RE);
envDepth = beginRes.envDepth;
if (beginRes.handled) {
continue; // this line is already fully processed
}
lineText = beginRes.lineText;
}
// 2) If inside lstlisting, look for \end{lstlisting}
if (envDepth > 0) {
var endRes = handleLstEndInline(lineText, envDepth, items, nextLine, state);
envDepth = endRes.envDepth;
items = endRes.items;
if (endRes.handled) {
continue;
}
lineText = endRes.lineText;
}
// Handle \setcounter lines
if (consts_1.reSetCounter.test(lineText)) {
match = lineText.match(consts_1.reSetCounter);
if (match && ((_d = state.md.options) === null || _d === void 0 ? void 0 : _d.forLatex)) {
var token = state.push("setcounter", "", 0);
token.latex = match[0].trim();
}
if (match && match[2]) {
var sE = match.index + match[0].length < lineText.length
? lineText.slice(match.index + match[0].length)
: "";
sE = sE.trim();
var startNumber = (_e = (0, latex_list_common_1.parseSetCounterNumber)(match)) !== null && _e !== void 0 ? _e : 1;
li = { value: startNumber };
if (sE.length > 0) {
items = (0, latex_list_items_1.ItemsAddToPrev)(items, sE, nextLine);
}
continue;
}
}
// Handle inline \end{itemize}/\end{enumerate}
if (consts_1.END_LIST_ENV_INLINE_RE.test(lineText)) {
var endMatch = lineText.match(consts_1.END_LIST_ENV_INLINE_RE);
if (endMatch) {
var raw = endMatch[1].trim();
if (!(0, latex_list_types_1.isListType)(raw)) {
return false;
}
var _h = (0, latex_list_items_1.splitInlineListEnv)(lineText, endMatch), sB = _h.sB, sE = _h.sE, isBacktickEscapedPair = _h.isBacktickEscapedPair;
if (isBacktickEscapedPair) {
items = (0, latex_list_items_1.ItemsListPush)(items, lineText, nextLine, nextLine);
continue;
}
if (sB.length > 0) {
items = (0, latex_list_items_1.ItemsAddToPrev)(items, sB, nextLine);
}
(_a = (0, latex_list_items_1.finalizeListItems)(state, items, itemizeLevelTokens, enumerateLevelTypes, li, iOpen, itemizeLevelContents, tokenStart), iOpen = _a.iOpen, items = _a.items, li = _a.li);
(0, latex_list_tokens_1.setTokenCloseList)(state, startLine + renderStart, nextLine + renderStart);
if (sE.length > 0) {
items = (0, latex_list_items_1.ItemsAddToPrev)(items, sE, nextLine);
}
iOpen--;
if (iOpen <= 0) {
haveClose = true;
nextLine += 1;
break;
}
}
continue;
}
// Handle inline \begin{itemize}/\begin{enumerate}
if (consts_1.BEGIN_LIST_ENV_INLINE_RE.test(lineText)) {
var beginMatch = lineText.match(consts_1.BEGIN_LIST_ENV_INLINE_RE);
if (beginMatch) {
var raw = beginMatch[1].trim();
if (!(0, latex_list_types_1.isListType)(raw)) {
return false;
}
var beginType = raw;
var _j = (0, latex_list_items_1.splitInlineListEnv)(lineText, beginMatch), sB = _j.sB, sE = _j.sE, isBacktickEscapedPair = _j.isBacktickEscapedPair;
if (isBacktickEscapedPair) {
items = (0, latex_list_items_1.ItemsListPush)(items, lineText, nextLine, nextLine);
continue;
}
if (sB.length > 0) {
items = (0, latex_list_items_1.ItemsAddToPrev)(items, sB, nextLine);
}
(_b = (0, latex_list_items_1.finalizeListItems)(state, items, itemizeLevelTokens, enumerateLevelTypes, li, iOpen, itemizeLevelContents, tokenStart), iOpen = _b.iOpen, items = _b.items, li = _b.li);
(0, latex_list_tokens_1.setTokenOpenList)(state, -1, -1, beginType, itemizeLevelTokens, enumerateLevelTypes, itemizeLevelContents);
if (sE.length > 0) {
items = (0, latex_list_items_1.ItemsAddToPrev)(items, sE, nextLine);
}
iOpen++;
}
}
else {
// Regular line inside list: either a new \item or continuation
if (consts_1.LATEX_ITEM_COMMAND_INLINE_RE.test(lineText)) {
items = (0, latex_list_items_1.ItemsListPush)(items, lineText, nextLine + renderStart, nextLine + renderStart);
}
else {
items = (0, latex_list_items_1.ItemsAddToPrev)(items, lineText, nextLine);
}
}
}
if (!haveClose) {
// No explicit \end{itemize}/\end{enumerate} found — flush remaining items
console.log("NOT CLOSE TAG.");
(0, latex_list_items_1.ListItemsBlock)(state, items);
li = null;
}
state.line = nextLine;
state.startLine = startLine;
state.parentType = oldParentType;
state.level = state.prentLevel < 0 ? 0 : state.prentLevel;
if (tokenStart) {
tokenStart.map[1] = nextLine + renderStart;
}
return true;
};
exports.Lists = Lists;
//# sourceMappingURL=latex-list-env-block.js.map