mirror of
https://github.com/youfoundjk/TeXcore.git
synced 2026-07-22 07:33:31 +00:00
- Replace naive $$ regexes and dollar counters with a state-machine parser `findDisplayMathBlocks`.
- Correctly handle adjacent single-dollar inline math delimiters (e.g. `$[\text{charge}]$$^2$`) without misinterpreting them as opening display math blocks.
- Update `findMathBlocks` in live preview, callout block equation extraction in `provider-equation`, and `checkAndFixCalloutMath` in fixer utils.
- Add unit test coverage for adjacent inline math blocks and display math boundary detection.
159 lines
5.4 KiB
TypeScript
159 lines
5.4 KiB
TypeScript
import {
|
|
toRomanUpper,
|
|
toRomanLower,
|
|
toAlphUpper,
|
|
toAlphLower,
|
|
getEqNumberPrefix,
|
|
CONVERTER
|
|
} from '../src/utils/format';
|
|
import {
|
|
trimMathText,
|
|
parseMarkdownComment,
|
|
parseYamlLike,
|
|
getCalloutPrefix,
|
|
isStructuralCalloutLine,
|
|
findDisplayMathBlocks
|
|
} from '../src/utils/parse';
|
|
|
|
import { splitIntoLines, insertAt } from '../src/utils/general';
|
|
import { App, TFile } from 'obsidian';
|
|
|
|
describe('format.ts tests', () => {
|
|
it('Roman numerals conversion', () => {
|
|
expect(toRomanUpper(1)).toBe('I');
|
|
expect(toRomanUpper(3)).toBe('III');
|
|
expect(toRomanUpper(4)).toBe('IV');
|
|
expect(toRomanUpper(9)).toBe('IX');
|
|
expect(toRomanUpper(10)).toBe('X');
|
|
expect(toRomanUpper(39)).toBe('XXXIX');
|
|
expect(toRomanUpper(40)).toBe('XL');
|
|
expect(toRomanUpper(50)).toBe('L');
|
|
expect(toRomanUpper(90)).toBe('XC');
|
|
expect(toRomanUpper(100)).toBe('C');
|
|
expect(toRomanUpper(400)).toBe('CD');
|
|
expect(toRomanUpper(500)).toBe('D');
|
|
expect(toRomanUpper(900)).toBe('CM');
|
|
expect(toRomanUpper(1000)).toBe('M');
|
|
expect(toRomanUpper(1994)).toBe('MCMXCIV');
|
|
|
|
expect(toRomanLower(1994)).toBe('mcmxciv');
|
|
});
|
|
|
|
it('Alphabetic numerals conversion', () => {
|
|
expect(toAlphUpper(1)).toBe('A');
|
|
expect(toAlphUpper(26)).toBe('Z');
|
|
expect(toAlphUpper(27)).toBe('BA');
|
|
expect(toAlphUpper(28)).toBe('BB');
|
|
expect(toAlphLower(1)).toBe('a');
|
|
expect(toAlphLower(27)).toBe('ba');
|
|
});
|
|
|
|
it('CONVERTER mapping', () => {
|
|
expect(CONVERTER.arabic(123)).toBe('123');
|
|
expect(CONVERTER.alph(1)).toBe('a');
|
|
expect(CONVERTER.Alph(1)).toBe('A');
|
|
expect(CONVERTER.roman(10)).toBe('x');
|
|
expect(CONVERTER.Roman(10)).toBe('X');
|
|
});
|
|
|
|
it('getEqNumberPrefix', () => {
|
|
const dummyApp = {} as App;
|
|
type TempTFile = TFile;
|
|
const dummyFile = {} as unknown as TempTFile;
|
|
const settings = {
|
|
eqNumberPrefix: 'Prefix-'
|
|
} as unknown;
|
|
expect(getEqNumberPrefix(dummyApp, dummyFile, settings)).toBe('Prefix-');
|
|
});
|
|
});
|
|
|
|
describe('parse.ts tests', () => {
|
|
it('trimMathText', () => {
|
|
expect(trimMathText('$$ E = mc^2 $$')).toBe('E = mc^2');
|
|
expect(trimMathText('$$\nE = mc^2\n$$')).toBe('E = mc^2');
|
|
expect(trimMathText('no math block')).toBe('');
|
|
});
|
|
|
|
it('findDisplayMathBlocks handles adjacent inline math like $[text]$$^2$', () => {
|
|
const content = `$[\\text{charge}]$$^2$ Refer Eq. [[#^eq-robp7m93]]
|
|
$$
|
|
\\mathbf{D}_0=\\begin{pmatrix}\\alpha_0^{\\,2}\\,Z_i Z_j & \\alpha_0\\alpha_2\\,Z_i\\\\-\\alpha_0\\alpha_2\\,Z_j & 0\\end{pmatrix}
|
|
% id: eq-robp7m93
|
|
$$`;
|
|
const blocks = findDisplayMathBlocks(content);
|
|
expect(blocks.length).toBe(1);
|
|
const matchedText = content.substring(blocks[0].from, blocks[0].to);
|
|
expect(matchedText).toContain('% id: eq-robp7m93');
|
|
expect(matchedText.startsWith('$$')).toBe(true);
|
|
expect(matchedText.endsWith('$$')).toBe(true);
|
|
});
|
|
|
|
it('findDisplayMathBlocks ignores math in code blocks', () => {
|
|
const content = `\`\`\`
|
|
$$ fake math $$
|
|
\`\`\`
|
|
$$ real math $$`;
|
|
const blocks = findDisplayMathBlocks(content);
|
|
expect(blocks.length).toBe(1);
|
|
expect(content.substring(blocks[0].from, blocks[0].to)).toBe('$$ real math $$');
|
|
});
|
|
|
|
it('parseMarkdownComment', () => {
|
|
const text = 'Some text\n%% comment line 1\ncomment line 2 %%\nOther text %% single comment %%';
|
|
const comments = parseMarkdownComment(text);
|
|
expect(comments).toEqual(['comment line 1', 'comment line 2', 'single comment']);
|
|
});
|
|
|
|
it('parseYamlLike', () => {
|
|
expect(parseYamlLike('key: value')).toEqual({ key: 'value' });
|
|
expect(parseYamlLike(' key : value ')).toEqual({ key: 'value' });
|
|
expect(parseYamlLike('no colon')).toBeNull();
|
|
expect(parseYamlLike('key: value: extra')).toEqual({ key: 'value: extra' });
|
|
});
|
|
|
|
it('getCalloutPrefix', () => {
|
|
expect(getCalloutPrefix('> hello')).toBe('> ');
|
|
expect(getCalloutPrefix('>> hello')).toBe('>> ');
|
|
expect(getCalloutPrefix(' > > hello')).toBe(' > > ');
|
|
expect(getCalloutPrefix('no callout')).toBe('');
|
|
});
|
|
|
|
it('isStructuralCalloutLine', () => {
|
|
expect(isStructuralCalloutLine('>')).toBe(true);
|
|
expect(isStructuralCalloutLine('> > ')).toBe(true);
|
|
expect(isStructuralCalloutLine(' > >')).toBe(true);
|
|
expect(isStructuralCalloutLine('> text')).toBe(false);
|
|
expect(isStructuralCalloutLine('text')).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('general.ts tests', () => {
|
|
it('splitIntoLines', () => {
|
|
expect(splitIntoLines('line1\nline2\r\nline3')).toEqual(['line1', 'line2', 'line3']);
|
|
});
|
|
|
|
it('insertAt', () => {
|
|
const arr = [1, 2, 3];
|
|
insertAt(arr, 99, 1);
|
|
expect(arr).toEqual([1, 99, 2, 3]);
|
|
});
|
|
});
|
|
|
|
describe('Sub-equation splitting regex tests', () => {
|
|
it('should split rows while keeping \\[dimen] intact', () => {
|
|
const regex = /(\\\\(?:\s*\[[^\]]*\])?)/;
|
|
const input = `\\beta \\frac{\\partial P}{\\partial \\rho} & = \\frac{\\partial}{\\partial \\eta} \\left[ \\eta \\frac{1 + \\eta + \\eta^2}{(1 - \\eta)^3} \\right] \\\\[0.6em]\n\\implies q(\\eta) & = \\frac{(1 + 2\\eta)^2}{(1 - \\eta)^4} \\end{align}`;
|
|
const parts = input.split(regex);
|
|
expect(parts.length).toBe(3);
|
|
expect(parts[0]).toContain('\\beta');
|
|
expect(parts[1]).toBe('\\\\[0.6em]');
|
|
expect(parts[2]).toContain('\\implies');
|
|
});
|
|
|
|
it('should split rows when there is no dimension parameter', () => {
|
|
const regex = /(\\\\(?:\s*\[[^\]]*\])?)/;
|
|
const input = `row1 \\\\ row2`;
|
|
const parts = input.split(regex);
|
|
expect(parts).toEqual(['row1 ', '\\\\', ' row2']);
|
|
});
|
|
});
|