|
|
@@ -13,113 +13,134 @@ Latex.prototype.onParse = function (node, vm) {
|
|
|
// $...$、$$...$$、\(...\)、\[...\]包裹的内容为latex公式
|
|
|
if (!vm.options.editable && node.type === 'text' &&
|
|
|
(node.text.includes('$') || node.text.includes('\\(') || node.text.includes('\\['))) {
|
|
|
- console.log(node.text)
|
|
|
- // 同时匹配 $、$$、\(、\)、\[、\] 分隔符
|
|
|
- const part = node.text.split(/(\$\$|\$|\\\(|\\\)|\\\[|\\\])/)
|
|
|
+ console.log('原始文本:', node.text)
|
|
|
+
|
|
|
const children = []
|
|
|
- let status = 0 // 0-普通文本, 1-行内公式开始, 2-块公式开始
|
|
|
- let formulaType = '' // 记录公式的起始分隔符类型
|
|
|
- let formulaContent = '' // 临时存储公式内容
|
|
|
+ let remaining = node.text
|
|
|
|
|
|
- for (let i = 0; i < part.length; i++) {
|
|
|
- if (i % 2 === 0) {
|
|
|
- // 文本内容
|
|
|
- if (part[i]) {
|
|
|
- if (status === 0) {
|
|
|
- // 普通文本
|
|
|
- children.push({
|
|
|
- type: 'text',
|
|
|
- text: part[i]
|
|
|
- })
|
|
|
- } else {
|
|
|
- // 在公式内部,累积内容
|
|
|
- formulaContent += part[i]
|
|
|
- }
|
|
|
+ while (remaining.length > 0) {
|
|
|
+ // 查找所有可能的起始符位置
|
|
|
+ let nextDelimiters = [
|
|
|
+ { type: '$$', start: remaining.indexOf('$$'), isBlock: true, end: '$$' },
|
|
|
+ { type: '$', start: remaining.indexOf('$'), isBlock: false, end: '$' },
|
|
|
+ { type: '\\(', start: remaining.indexOf('\\('), isBlock: false, end: '\\)' },
|
|
|
+ { type: '\\[', start: remaining.indexOf('\\['), isBlock: true, end: '\\]' }
|
|
|
+ ].filter(d => d.start !== -1).sort((a, b) => {
|
|
|
+ // 先按位置排序
|
|
|
+ if (a.start !== b.start) return a.start - b.start
|
|
|
+ // 位置相同时,优先匹配更长的($$ 优先于 $)
|
|
|
+ return b.type.length - a.type.length
|
|
|
+ })
|
|
|
+
|
|
|
+ if (nextDelimiters.length === 0) {
|
|
|
+ // 没有找到任何公式标记,剩余部分都是普通文本
|
|
|
+ if (remaining) {
|
|
|
+ children.push({
|
|
|
+ type: 'text',
|
|
|
+ text: remaining
|
|
|
+ })
|
|
|
}
|
|
|
- } else {
|
|
|
- // 分隔符
|
|
|
- const delimiter = part[i]
|
|
|
-
|
|
|
- if (status === 0) {
|
|
|
- // 当前不在公式内,这是起始分隔符
|
|
|
- if (delimiter === '$') {
|
|
|
- status = 1
|
|
|
- formulaType = '$'
|
|
|
- formulaContent = ''
|
|
|
- } else if (delimiter === '$$') {
|
|
|
- status = 2
|
|
|
- formulaType = '$$'
|
|
|
- formulaContent = ''
|
|
|
- } else if (delimiter === '\\(') {
|
|
|
- status = 1
|
|
|
- formulaType = '\\('
|
|
|
- formulaContent = ''
|
|
|
- } else if (delimiter === '\\[') {
|
|
|
- status = 2
|
|
|
- formulaType = '\\['
|
|
|
- formulaContent = ''
|
|
|
- } else {
|
|
|
- // 没有起始符的结束符,当作普通文本
|
|
|
- children.push({
|
|
|
- type: 'text',
|
|
|
- text: delimiter
|
|
|
- })
|
|
|
- }
|
|
|
+ break
|
|
|
+ }
|
|
|
+
|
|
|
+ const delimiter = nextDelimiters[0]
|
|
|
+
|
|
|
+ // 添加起始符之前的文本
|
|
|
+ if (delimiter.start > 0) {
|
|
|
+ children.push({
|
|
|
+ type: 'text',
|
|
|
+ text: remaining.substring(0, delimiter.start)
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查找对应的结束符
|
|
|
+ const contentStart = delimiter.start + delimiter.type.length
|
|
|
+ let endPos = remaining.indexOf(delimiter.end, contentStart)
|
|
|
+
|
|
|
+ if (endPos === -1) {
|
|
|
+ // 没有找到结束符,将起始符当作普通文本
|
|
|
+ children.push({
|
|
|
+ type: 'text',
|
|
|
+ text: remaining.substring(delimiter.start, contentStart)
|
|
|
+ })
|
|
|
+ remaining = remaining.substring(contentStart)
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ // 提取公式内容
|
|
|
+ let formulaContent = remaining.substring(contentStart, endPos)
|
|
|
+ console.log('提取的原始公式内容:', formulaContent, '分隔符类型:', delimiter.type)
|
|
|
+
|
|
|
+ // 清理公式内容:移除内部嵌套的数学分隔符
|
|
|
+ // 如果外层用 \(...\) 或 \[...\],需要移除内部的 $...$ 和 $$...$$
|
|
|
+ // 如果外层用 $...$ 或 $$...$$,需要移除内部的 \(...\) 和 \[...\]
|
|
|
+ if (delimiter.type === '\\(' || delimiter.type === '\\[') {
|
|
|
+ // 外层是 LaTeX 标准格式,移除内部的 $ 分隔符
|
|
|
+ // 匹配 $...$ 和 $$...$$ 并只保留内容
|
|
|
+ formulaContent = formulaContent.replace(/\$\$(.*?)\$\$/g, '$1') // 先处理 $$
|
|
|
+ formulaContent = formulaContent.replace(/\$(.*?)\$/g, '$1') // 再处理 $
|
|
|
+ console.log('清理后的公式内容:', formulaContent)
|
|
|
+ } else if (delimiter.type === '$' || delimiter.type === '$$') {
|
|
|
+ // 外层是 $ 格式,移除内部的 \(...\) 和 \[...\]
|
|
|
+ formulaContent = formulaContent.replace(/\\\[(.*?)\\\]/g, '$1') // 先处理 \[...\]
|
|
|
+ formulaContent = formulaContent.replace(/\\\((.*?)\\\)/g, '$1') // 再处理 \(...\)
|
|
|
+ console.log('清理后的公式内容:', formulaContent)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 容错处理:修正常见的 LaTeX 语法错误
|
|
|
+ // \pi 后面直接跟字母的情况,添加空格
|
|
|
+ formulaContent = formulaContent.replace(/\\pi([a-zA-Z])/g, '\\pi $1')
|
|
|
+ // 其他常见的希腊字母也做类似处理
|
|
|
+ formulaContent = formulaContent.replace(/\\alpha([a-zA-Z])/g, '\\alpha $1')
|
|
|
+ formulaContent = formulaContent.replace(/\\beta([a-zA-Z])/g, '\\beta $1')
|
|
|
+ formulaContent = formulaContent.replace(/\\gamma([a-zA-Z])/g, '\\gamma $1')
|
|
|
+ formulaContent = formulaContent.replace(/\\delta([a-zA-Z])/g, '\\delta $1')
|
|
|
+ formulaContent = formulaContent.replace(/\\epsilon([a-zA-Z])/g, '\\epsilon $1')
|
|
|
+ formulaContent = formulaContent.replace(/\\theta([a-zA-Z])/g, '\\theta $1')
|
|
|
+ formulaContent = formulaContent.replace(/\\lambda([a-zA-Z])/g, '\\lambda $1')
|
|
|
+ formulaContent = formulaContent.replace(/\\mu([a-zA-Z])/g, '\\mu $1')
|
|
|
+ formulaContent = formulaContent.replace(/\\sigma([a-zA-Z])/g, '\\sigma $1')
|
|
|
+ formulaContent = formulaContent.replace(/\\omega([a-zA-Z])/g, '\\omega $1')
|
|
|
+ formulaContent = formulaContent.replace(/\\Omega([a-zA-Z])/g, '\\Omega $1')
|
|
|
+
|
|
|
+ // 渲染公式
|
|
|
+ try {
|
|
|
+ if (delimiter.isBlock) {
|
|
|
+ // 块公式
|
|
|
+ const nodes = parse.default(formulaContent, {
|
|
|
+ displayMode: true
|
|
|
+ })
|
|
|
+ children.push({
|
|
|
+ name: 'div',
|
|
|
+ attrs: {
|
|
|
+ style: 'text-align:center'
|
|
|
+ },
|
|
|
+ children: nodes
|
|
|
+ })
|
|
|
} else {
|
|
|
- // 当前在公式内,检查是否是对应的结束分隔符
|
|
|
- let isClosing = false
|
|
|
-
|
|
|
- if ((formulaType === '$' && delimiter === '$') ||
|
|
|
- (formulaType === '$$' && delimiter === '$$') ||
|
|
|
- (formulaType === '\\(' && delimiter === '\\)') ||
|
|
|
- (formulaType === '\\[' && delimiter === '\\]')) {
|
|
|
- isClosing = true
|
|
|
- }
|
|
|
-
|
|
|
- if (isClosing) {
|
|
|
- // 找到匹配的结束符,渲染公式
|
|
|
- if (status === 1) {
|
|
|
- // 行内公式
|
|
|
- const nodes = parse.default(formulaContent, {})
|
|
|
- children.push({
|
|
|
- name: 'span',
|
|
|
- attrs: {},
|
|
|
- l: 'T',
|
|
|
- f: 'display:inline-block',
|
|
|
- children: nodes
|
|
|
- })
|
|
|
- } else if (status === 2) {
|
|
|
- // 块公式
|
|
|
- const nodes = parse.default(formulaContent, {
|
|
|
- displayMode: true
|
|
|
- })
|
|
|
- children.push({
|
|
|
- name: 'div',
|
|
|
- attrs: {
|
|
|
- style: 'text-align:center'
|
|
|
- },
|
|
|
- children: nodes
|
|
|
- })
|
|
|
- }
|
|
|
- // 重置状态
|
|
|
- status = 0
|
|
|
- formulaType = ''
|
|
|
- formulaContent = ''
|
|
|
- } else {
|
|
|
- // 不是匹配的结束符,将分隔符作为公式内容的一部分
|
|
|
- formulaContent += delimiter
|
|
|
- }
|
|
|
+ // 行内公式
|
|
|
+ const nodes = parse.default(formulaContent, {})
|
|
|
+ children.push({
|
|
|
+ name: 'span',
|
|
|
+ attrs: {},
|
|
|
+ l: 'T',
|
|
|
+ f: 'display:inline-block',
|
|
|
+ children: nodes
|
|
|
+ })
|
|
|
}
|
|
|
+ } catch (e) {
|
|
|
+ console.error('KaTeX解析错误:', e, '公式内容:', formulaContent)
|
|
|
+ // 解析失败,当作普通文本
|
|
|
+ children.push({
|
|
|
+ type: 'text',
|
|
|
+ text: delimiter.type + remaining.substring(contentStart, endPos) + delimiter.end
|
|
|
+ })
|
|
|
}
|
|
|
+
|
|
|
+ // 继续处理剩余部分
|
|
|
+ remaining = remaining.substring(endPos + delimiter.end.length)
|
|
|
}
|
|
|
|
|
|
- // 如果还有未闭合的公式内容,作为普通文本处理
|
|
|
- if (status !== 0 && formulaContent) {
|
|
|
- children.push({
|
|
|
- type: 'text',
|
|
|
- text: (formulaType === '\\(' ? '\\(' : formulaType === '\\[' ? '\\[' : formulaType) + formulaContent
|
|
|
- })
|
|
|
- }
|
|
|
delete node.type
|
|
|
delete node.text
|
|
|
node.name = 'span'
|