diff --git a/__tests__/html2/markdown/math3.html b/__tests__/html2/markdown/math3.html index cae136727d..cad76722ba 100644 --- a/__tests__/html2/markdown/math3.html +++ b/__tests__/html2/markdown/math3.html @@ -22,8 +22,7 @@ ## Basic Math -1. Simple arithmetic: -\(2 + 2 = 4\) +1. Simple arithmetic: \(2 + 2 = 4\) 2. Fractions: \[\frac{1}{2} + \frac{1}{3} = \frac{5}{6}\] @@ -83,11 +82,9 @@ ## Invalid input examples -12. Wrong expression is rendered: -\(2++2\) +12. Wrong expression is rendered: \(2++2\) -13. Inline closing delimeter is required: -\(x^2 +13. Inline closing delimeter is required: \(x^2 14. Katex syntax error: \[\int_0^\infty e^{-x} dx = 1 +}\] diff --git a/__tests__/html2/markdown/math3.html.snap-1.png b/__tests__/html2/markdown/math3.html.snap-1.png index 31e802f8e3..502cff1203 100644 Binary files a/__tests__/html2/markdown/math3.html.snap-1.png and b/__tests__/html2/markdown/math3.html.snap-1.png differ diff --git a/__tests__/html2/markdown/math3.html.snap-2.png b/__tests__/html2/markdown/math3.html.snap-2.png index bf7c5b62ec..618d83f6da 100644 Binary files a/__tests__/html2/markdown/math3.html.snap-2.png and b/__tests__/html2/markdown/math3.html.snap-2.png differ diff --git a/__tests__/html2/markdown/math3.html.snap-3.png b/__tests__/html2/markdown/math3.html.snap-3.png index 2af56c96b0..c5819cc0bd 100644 Binary files a/__tests__/html2/markdown/math3.html.snap-3.png and b/__tests__/html2/markdown/math3.html.snap-3.png differ diff --git a/packages/bundle/src/markdown/mathExtension/math.ts b/packages/bundle/src/markdown/mathExtension/math.ts index 764f7c1535..01d0479d3d 100644 --- a/packages/bundle/src/markdown/mathExtension/math.ts +++ b/packages/bundle/src/markdown/mathExtension/math.ts @@ -1,20 +1,20 @@ import type { Extension } from 'micromark-util-types'; -import { BACKSLASH, DOLLAR } from './constants'; -import { createTokenizer } from './tokenizer'; +import { BACKSLASH, CLOSE_BRACKET, CLOSE_PAREN, DOLLAR, OPEN_BRACKET, OPEN_PAREN } from './constants'; +import makeConstructTokenizer from './tokenizer'; export default function math(): Extension { - const construct = { + const makeConstruct = (...args: Parameters) => ({ name: 'math', - tokenize: createTokenizer - }; + tokenize: makeConstructTokenizer(...args) + }); return { text: { - [BACKSLASH]: construct + [BACKSLASH]: makeConstruct({ OPEN_CODE: OPEN_PAREN, CLOSE_CODE: CLOSE_PAREN }) }, flow: { - [BACKSLASH]: construct, - [DOLLAR]: construct + [BACKSLASH]: makeConstruct({ OPEN_CODE: OPEN_BRACKET, CLOSE_CODE: CLOSE_BRACKET }), + [DOLLAR]: makeConstruct({ OPEN_CODE: DOLLAR, CLOSE_CODE: DOLLAR }) } } as any; } diff --git a/packages/bundle/src/markdown/mathExtension/tokenizer.ts b/packages/bundle/src/markdown/mathExtension/tokenizer.ts index 90c9441353..a38431e565 100644 --- a/packages/bundle/src/markdown/mathExtension/tokenizer.ts +++ b/packages/bundle/src/markdown/mathExtension/tokenizer.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-magic-numbers */ /* eslint-disable @typescript-eslint/no-use-before-define */ import { BACKSLASH, OPEN_PAREN, CLOSE_PAREN, OPEN_BRACKET, CLOSE_BRACKET, DOLLAR } from './constants'; import { markdownLineEnding } from 'micromark-util-character'; @@ -6,88 +5,71 @@ import { type Code, type Effects, type State } from 'micromark-util-types'; type MathTokenTypes = 'math' | 'mathChunk'; +type OpenCode = typeof OPEN_BRACKET | typeof OPEN_PAREN | typeof DOLLAR; +type CloseCode = typeof CLOSE_BRACKET | typeof CLOSE_PAREN | typeof DOLLAR; + type MathEffects = Omit & { enter(type: MathTokenTypes): void; exit(type: MathTokenTypes): void; }; -export function createTokenizer(effects: MathEffects, ok: State, nok: State) { - let expectedCloseDelimiter: number; - let dollarDelimiterCount = 0; +/** + * Creates a math tokenizer for specified delimiter pair + * @param OPEN_CODE - Opening delimiter code + * @param CLOSE_CODE - Closing delimiter code + */ +export default ({ OPEN_CODE, CLOSE_CODE }: { OPEN_CODE: OpenCode; CLOSE_CODE: CloseCode }) => + function createTokenizer(effects: MathEffects, ok: State, nok: State) { + return start; + + function start(code: Code): State { + if (code === BACKSLASH || (code === DOLLAR && OPEN_CODE === DOLLAR)) { + effects.enter('math'); + effects.enter('mathChunk'); + effects.consume(code); + return openDelimiter; + } + + return nok(code); + } - return start; + function openDelimiter(code: Code): State { + if (code !== OPEN_CODE) { + return nok(code); + } - function start(code: Code): State { - if (code === BACKSLASH || code === DOLLAR) { - effects.enter('math'); - effects.enter('mathChunk'); effects.consume(code); - dollarDelimiterCount = code === DOLLAR ? 1 : 0; - return openDelimiter; + return content; } - return nok(code); - } - - function openDelimiter(code: Code): State { - switch (code) { - case OPEN_PAREN: - expectedCloseDelimiter = CLOSE_PAREN; - break; - case OPEN_BRACKET: - expectedCloseDelimiter = CLOSE_BRACKET; - break; - case DOLLAR: - expectedCloseDelimiter = DOLLAR; - dollarDelimiterCount++; - if (dollarDelimiterCount !== 2) { - return nok(code); - } - break; - default: + function content(code: Code): State { + if (code === null) { return nok(code); - } - effects.consume(code); - return content; - } + } - function content(code: Code): State { - if (code === null) { - return nok(code); - } + if (code === BACKSLASH || (CLOSE_CODE === DOLLAR && code === DOLLAR)) { + effects.consume(code); + return maybeCloseDelimiter; + } - if (code === BACKSLASH || (dollarDelimiterCount && code === DOLLAR)) { effects.consume(code); - code === DOLLAR && dollarDelimiterCount--; - return maybeCloseDelimiter; - } - effects.consume(code); + if (markdownLineEnding(code)) { + effects.exit('mathChunk'); + effects.enter('mathChunk'); + } - if (markdownLineEnding(code)) { - effects.exit('mathChunk'); - effects.enter('mathChunk'); + return content; } - return content; - } - - function maybeCloseDelimiter(code: Code): State { - if (code === expectedCloseDelimiter) { - code === DOLLAR && dollarDelimiterCount--; - if (dollarDelimiterCount !== 0) { - return nok(code); + function maybeCloseDelimiter(code: Code): State { + if (code === CLOSE_CODE) { + effects.consume(code); + effects.exit('mathChunk'); + effects.exit('math'); + return ok; } - effects.consume(code); - effects.exit('mathChunk'); - effects.exit('math'); - - dollarDelimiterCount = 0; - expectedCloseDelimiter = undefined; - return ok; + return content(code); } - - return content(code); - } -} + };