Verifying the KaTeX checklist: `$x^2$` in inline code was turned into math and the backticks were left as literal text, because the math split runs before markdown. The splitter now skips backtick code spans (N ticks close with N), so markdown's inline code wins: wire is <code>$y^2$</code>. Fenced blocks were already fine. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
99 lines
3.4 KiB
TypeScript
99 lines
3.4 KiB
TypeScript
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { splitMathSegments } from './mathParse';
|
|
|
|
test('plain text with no dollars is a single text segment', () => {
|
|
assert.deepEqual(splitMathSegments('hello world'), [{ type: 'text', value: 'hello world' }]);
|
|
});
|
|
|
|
test('empty string yields no segments', () => {
|
|
assert.deepEqual(splitMathSegments(''), []);
|
|
});
|
|
|
|
test('inline $…$ is extracted between surrounding text', () => {
|
|
assert.deepEqual(splitMathSegments('a $x^2$ b'), [
|
|
{ type: 'text', value: 'a ' },
|
|
{ type: 'inline', value: 'x^2' },
|
|
{ type: 'text', value: ' b' },
|
|
]);
|
|
});
|
|
|
|
test('block $$…$$ is extracted', () => {
|
|
assert.deepEqual(splitMathSegments('$$block$$'), [{ type: 'block', value: 'block' }]);
|
|
});
|
|
|
|
test('block math may span newlines', () => {
|
|
assert.deepEqual(splitMathSegments('$$\na=b\n$$'), [{ type: 'block', value: '\na=b\n' }]);
|
|
});
|
|
|
|
test('currency "$5 and $10" is NOT treated as math', () => {
|
|
assert.deepEqual(splitMathSegments('$5 and $10'), [{ type: 'text', value: '$5 and $10' }]);
|
|
});
|
|
|
|
test('escaped \\$ never opens or closes math', () => {
|
|
assert.deepEqual(splitMathSegments('cost \\$5 today'), [
|
|
{ type: 'text', value: 'cost $5 today' },
|
|
]);
|
|
assert.deepEqual(splitMathSegments('\\$x\\$'), [{ type: 'text', value: '$x$' }]);
|
|
});
|
|
|
|
test('unbalanced single $ stays as text', () => {
|
|
assert.deepEqual(splitMathSegments('price is $ here'), [
|
|
{ type: 'text', value: 'price is $ here' },
|
|
]);
|
|
});
|
|
|
|
test('unbalanced $$ stays as text', () => {
|
|
assert.deepEqual(splitMathSegments('$$x'), [{ type: 'text', value: '$$x' }]);
|
|
});
|
|
|
|
test('inline requires non-space adjacency on both delimiters', () => {
|
|
// Space right after opening $ -> not math.
|
|
assert.deepEqual(splitMathSegments('$ x$'), [{ type: 'text', value: '$ x$' }]);
|
|
// Space right before closing $ -> not math.
|
|
assert.deepEqual(splitMathSegments('$x $'), [{ type: 'text', value: '$x $' }]);
|
|
});
|
|
|
|
test('multiple inline spans on one line', () => {
|
|
assert.deepEqual(splitMathSegments('$a$ and $b$'), [
|
|
{ type: 'inline', value: 'a' },
|
|
{ type: 'text', value: ' and ' },
|
|
{ type: 'inline', value: 'b' },
|
|
]);
|
|
});
|
|
|
|
test('escaped dollar inside inline math is preserved in LaTeX', () => {
|
|
assert.deepEqual(splitMathSegments('$a\\$b$'), [{ type: 'inline', value: 'a\\$b' }]);
|
|
});
|
|
|
|
test('closing $ followed by a digit is skipped (currency guard) then recovers', () => {
|
|
// The first candidate closer is followed by `2` so it is skipped; the later
|
|
// `$` closes the span.
|
|
assert.deepEqual(splitMathSegments('$x$2 + y$'), [{ type: 'inline', value: 'x$2 + y' }]);
|
|
});
|
|
|
|
test('block and inline mixed with text', () => {
|
|
assert.deepEqual(splitMathSegments('see $$E=mc^2$$ and $a$ ok'), [
|
|
{ type: 'text', value: 'see ' },
|
|
{ type: 'block', value: 'E=mc^2' },
|
|
{ type: 'text', value: ' and ' },
|
|
{ type: 'inline', value: 'a' },
|
|
{ type: 'text', value: ' ok' },
|
|
]);
|
|
});
|
|
|
|
test('math inside a backtick code span stays literal text (#194 P4-4)', () => {
|
|
assert.deepEqual(splitMathSegments('inline `$y^2$` here'), [
|
|
{ type: 'text', value: 'inline `$y^2$` here' },
|
|
]);
|
|
assert.deepEqual(splitMathSegments('``$a$`` and $b$'), [
|
|
{ type: 'text', value: '``$a$`` and ' },
|
|
{ type: 'inline', value: 'b' },
|
|
]);
|
|
// An unclosed backtick is just a character.
|
|
assert.deepEqual(splitMathSegments('tick ` then $c$'), [
|
|
{ type: 'text', value: 'tick ` then ' },
|
|
{ type: 'inline', value: 'c' },
|
|
]);
|
|
});
|