diff --git a/CHANGELOG.md b/CHANGELOG.md index 24acc59b6..d4555c307 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,7 @@ itself. - **#2122** On the Virtual Keyboard, the multiplication key now produces `\cdot` instead of `\times`. Use shift to produce `\times`. -- Improved serialization to ASCIIMath and MathML +- Improved serialization to ASCIIMath and MathML (**#2130** and others) - **#2121** For ASCIIMath and MathML serialization, including phantom closing delimiter in the output. - Pressing the Action keycap on the virtual keyboard with the shift key pressed diff --git a/src/editor/atom-to-ascii-math.ts b/src/editor/atom-to-ascii-math.ts index 32221efb4..d86586d74 100644 --- a/src/editor/atom-to-ascii-math.ts +++ b/src/editor/atom-to-ascii-math.ts @@ -128,6 +128,17 @@ const SPECIAL_OPERATORS = { // '\\hat': '^' }; +function joinAsciiMath(xs: string[]): string { + let result = ''; + for (const x of xs) { + const last = result[result.length - 1]; + if (last !== undefined && /\d/.test(last) && /^\d/.test(x)) result += ' '; + + result += x; + } + return result; +} + export function atomToAsciiMath(atom: Atom | Atom[] | undefined): string { if (!atom) return ''; if (isArray(atom)) { @@ -148,11 +159,16 @@ export function atomToAsciiMath(atom: Atom | Atom[] | undefined): string { } let i = 0; - let result = ''; - while (atom[i] && atom[i].mode === 'math') - result += atomToAsciiMath(atom[i++]); - - return result + atomToAsciiMath(atom.slice(i)); + const result: string[] = []; + while (atom[i] && atom[i].mode === 'math') { + let digits = ''; + while (atom[i] && atom[i].type === 'mord' && /\d/.test(atom[i].value)) + digits += atom[i++].value; + if (digits) result.push(digits); + else result.push(atomToAsciiMath(atom[i++])); + } + result.push(atomToAsciiMath(atom.slice(i))); + return joinAsciiMath(result); } if (atom.mode === 'text') return `"${atom.value}"`; @@ -178,7 +194,7 @@ export function atomToAsciiMath(atom: Atom | Atom[] | undefined): string { '\\check': 'check', // non-standard }[command]; - result += `${accent ?? ''} ${atomToAsciiMath(atom.body)} `; + result = `${accent ?? ''} ${atomToAsciiMath(atom.body)} `; break; case 'first': @@ -196,7 +212,7 @@ export function atomToAsciiMath(atom: Atom | Atom[] | undefined): string { { const genfracAtom = atom as GenfracAtom; if (genfracAtom.leftDelim || genfracAtom.rightDelim) { - result += + result = genfracAtom.leftDelim === '.' || !genfracAtom.leftDelim ? '{:' : genfracAtom.leftDelim; @@ -294,7 +310,7 @@ export function atomToAsciiMath(atom: Atom | Atom[] | undefined): string { case 'mopen': case 'mclose': - result += atom.value; + result = atom.value; break; case 'mpunct': diff --git a/test/math-ascii.test.ts b/test/math-ascii.test.ts index b44f61464..4f637d1b6 100644 --- a/test/math-ascii.test.ts +++ b/test/math-ascii.test.ts @@ -56,10 +56,13 @@ describe('ASCII MATH', function () { equalASCIIMath('\\Gamma +1', 'Gamma+1'); equalASCIIMath('\\frac{\\pi }{2\\pi }', '(pi)/(2pi)'); - equalASCIIMath('\\text{if }x>0', '"if "x>0'); + // Avoid collisions with digits + expect(convertLatexToAsciiMath('1^2 3^4')).toBe('1^2 3^4'); + + equalASCIIMath('\\text{if }x>0', '"if " x>0'); equalASCIIMath( '\\text{if }x>0\\text{ then }f\\left(x\\right)=x^{2}', - '"if "x>0" then "f(x)=x^2' + '"if " x>0" then " f(x)=x^2' ); // equalASCIIMath('\\begin{pmatrix}a & b & c\\end{pmatrix}', '((a),(b),(c))');