|
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 } |
|
]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let inlinePatterns = []; |
|
let 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('|')})(?=[\\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; |
|
} |
|
|
|
|
|
|
|
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); |
|
} |
|
}; |
|
} |
|
|