| | 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 ALLOWED_SURROUNDING_CHARS = |
| | '\\s。,、、;;„“‘’“”()「」『』[]《》【】‹›«»…⋯::?!~⇒?!-\\/:-@\\[-`{-~\\p{Script=Han}\\p{Script=Hiragana}\\p{Script=Katakana}\\p{Script=Hangul}'; |
| | |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | |
| | |
| |
|
| | const inlinePatterns = []; |
| | const blockPatterns = []; |
| |
|
| | function escapeRegex(string) { |
| | return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); |
| | } |
| |
|
| | function generateRegexRules(delimiters) { |
| | delimiters.forEach((delimiter) => { |
| | const { left, right, display } = delimiter; |
| | |
| | const escapedLeft = escapeRegex(left); |
| | const escapedRight = escapeRegex(right); |
| |
|
| | if (!display) { |
| | |
| | inlinePatterns.push(`${escapedLeft}((?:\\\\[^]|[^\\\\])+?)${escapedRight}`); |
| | } else { |
| | |
| | inlinePatterns.push(`${escapedLeft}(?!\\n)((?:\\\\[^]|[^\\\\])+?)(?!\\n)${escapedRight}`); |
| | blockPatterns.push(`${escapedLeft}\\n((?:\\\\[^]|[^\\\\])+?)\\n${escapedRight}`); |
| | } |
| | }); |
| |
|
| | |
| | const inlineRule = new RegExp( |
| | `^(${inlinePatterns.join('|')})(?=[${ALLOWED_SURROUNDING_CHARS}]|$)`, |
| | 'u' |
| | ); |
| | const blockRule = new RegExp( |
| | `^(${blockPatterns.join('|')})(?=[${ALLOWED_SURROUNDING_CHARS}]|$)`, |
| | '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) { |
| | const ruleReg = displayMode ? blockRule : inlineRule; |
| |
|
| | let indexSrc = src; |
| |
|
| | while (indexSrc) { |
| | let index = -1; |
| | let startIndex = -1; |
| | let startDelimiter = ''; |
| | let endDelimiter = ''; |
| | for (const 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; |
| | } |
| |
|
| | |
| | |
| | const f = |
| | index === 0 || |
| | indexSrc.charAt(index - 1).match(new RegExp(`[${ALLOWED_SURROUNDING_CHARS}]`, 'u')); |
| | 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) { |
| | const ruleReg = displayMode ? blockRule : inlineRule; |
| | const 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); |
| | }, |
| | renderer(token) { |
| | return `${token?.text ?? ''}`; |
| | } |
| | }; |
| | } |
| |
|
| | function blockKatex(options) { |
| | return { |
| | name: 'blockKatex', |
| | level: 'block', |
| | start(src) { |
| | return katexStart(src, true); |
| | }, |
| | tokenizer(src, tokens) { |
| | return katexTokenizer(src, tokens, true); |
| | }, |
| | renderer(token) { |
| | return `${token?.text ?? ''}`; |
| | } |
| | }; |
| | } |
| |
|