Spaces:
Running
Running
import katex from 'katex'; | |
const DELIMITER_LIST = [ | |
{ left: '$$', right: '$$', display: true }, | |
{ left: '$', right: '$', display: false }, | |
{ left: '\\pu{', right: '}', display: false }, | |
{ left: '\\ce{', right: '}', display: false }, | |
{ left: '\\(', right: '\\)', display: false }, | |
{ left: '\\[', right: '\\]', display: true }, | |
{ left: '\\begin{equation}', right: '\\end{equation}', display: true } | |
]; | |
// const DELIMITER_LIST = [ | |
// { left: '$$', right: '$$', display: false }, | |
// { left: '$', right: '$', display: false }, | |
// ]; | |
// const inlineRule = /^(\${1,2})(?!\$)((?:\\.|[^\\\n])*?(?:\\.|[^\\\n\$]))\1(?=[\s?!\.,:?!。,:]|$)/; | |
// const blockRule = /^(\${1,2})\n((?:\\[^]|[^\\])+?)\n\1(?:\n|$)/; | |
let inlinePatterns = []; | |
let blockPatterns = []; | |
function escapeRegex(string) { | |
return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); | |
} | |
function generateRegexRules(delimiters) { | |
delimiters.forEach((delimiter) => { | |
const { left, right, display } = delimiter; | |
// Ensure regex-safe delimiters | |
const escapedLeft = escapeRegex(left); | |
const escapedRight = escapeRegex(right); | |
if (!display) { | |
// For inline delimiters, we match everything | |
inlinePatterns.push(`${escapedLeft}((?:\\\\[^]|[^\\\\])+?)${escapedRight}`); | |
} else { | |
// Block delimiters doubles as inline delimiters when not followed by a newline | |
inlinePatterns.push(`${escapedLeft}(?!\\n)((?:\\\\[^]|[^\\\\])+?)(?!\\n)${escapedRight}`); | |
blockPatterns.push(`${escapedLeft}\\n((?:\\\\[^]|[^\\\\])+?)\\n${escapedRight}`); | |
} | |
}); | |
// Math formulas can end in special characters | |
const inlineRule = new RegExp( | |
`^(${inlinePatterns.join('|')})(?=[\\s?。,!-\/:-@[-\`{-~]|$)`, | |
'u' | |
); | |
const blockRule = new RegExp(`^(${blockPatterns.join('|')})(?=[\\s?。,!-\/:-@[-\`{-~]|$)`, 'u'); | |
return { inlineRule, blockRule }; | |
} | |
const { inlineRule, blockRule } = generateRegexRules(DELIMITER_LIST); | |
export default function (options = {}) { | |
return { | |
extensions: [inlineKatex(options), blockKatex(options)] | |
}; | |
} | |
function katexStart(src, displayMode: boolean) { | |
let ruleReg = displayMode ? blockRule : inlineRule; | |
let indexSrc = src; | |
while (indexSrc) { | |
let index = -1; | |
let startIndex = -1; | |
let startDelimiter = ''; | |
let endDelimiter = ''; | |
for (let delimiter of DELIMITER_LIST) { | |
if (delimiter.display !== displayMode) { | |
continue; | |
} | |
startIndex = indexSrc.indexOf(delimiter.left); | |
if (startIndex === -1) { | |
continue; | |
} | |
index = startIndex; | |
startDelimiter = delimiter.left; | |
endDelimiter = delimiter.right; | |
} | |
if (index === -1) { | |
return; | |
} | |
// Check if the delimiter is preceded by a special character. | |
// If it does, then it's potentially a math formula. | |
const f = index === 0 || indexSrc.charAt(index - 1).match(/[\s?。,!-\/:-@[-`{-~]/); | |
if (f) { | |
const possibleKatex = indexSrc.substring(index); | |
if (possibleKatex.match(ruleReg)) { | |
return index; | |
} | |
} | |
indexSrc = indexSrc.substring(index + startDelimiter.length).replace(endDelimiter, ''); | |
} | |
} | |
function katexTokenizer(src, tokens, displayMode: boolean) { | |
let ruleReg = displayMode ? blockRule : inlineRule; | |
let type = displayMode ? 'blockKatex' : 'inlineKatex'; | |
const match = src.match(ruleReg); | |
if (match) { | |
const text = match | |
.slice(2) | |
.filter((item) => item) | |
.find((item) => item.trim()); | |
return { | |
type, | |
raw: match[0], | |
text: text, | |
displayMode | |
}; | |
} | |
} | |
function inlineKatex(options) { | |
return { | |
name: 'inlineKatex', | |
level: 'inline', | |
start(src) { | |
return katexStart(src, false); | |
}, | |
tokenizer(src, tokens) { | |
return katexTokenizer(src, tokens, false); | |
} | |
}; | |
} | |
function blockKatex(options) { | |
return { | |
name: 'blockKatex', | |
level: 'block', | |
start(src) { | |
return katexStart(src, true); | |
}, | |
tokenizer(src, tokens) { | |
return katexTokenizer(src, tokens, true); | |
} | |
}; | |
} | |