-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
58 additions
and
79 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<typeof makeConstructTokenizer>) => ({ | ||
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; | ||
} |
112 changes: 47 additions & 65 deletions
112
packages/bundle/src/markdown/mathExtension/tokenizer.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,93 +1,75 @@ | ||
/* 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'; | ||
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<Effects, 'enter' | 'exit'> & { | ||
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); | ||
} | ||
} | ||
}; |