Markdown parser fork with extended syntax for personal use.
at hack 168 lines 5.0 kB view raw
1//! MDX JSX (flow) occurs in the [flow][] content type. 2//! 3//! ## Grammar 4//! 5//! MDX JSX (flow) forms with the following BNF 6//! (<small>see [construct][crate::construct] for character groups</small>): 7//! 8//! ```bnf 9//! mdx_jsx_flow ::= mdx_jsx *space_or_tab [mdx_jsx *space_or_tab] 10//! 11//! ; See the `partial_mdx_jsx` construct for the BNF of that part. 12//! ``` 13//! 14//! As this construct occurs in flow, like all flow constructs, it must be 15//! followed by an eol (line ending) or eof (end of file). 16//! It is allowed to use multiple tags after each other, optionally with only 17//! whitespace between them. 18//! 19//! See [`mdx_jsx`][mdx_jsx] for more info. 20//! 21//! ## Tokens 22//! 23//! * [`MdxJsxFlowTag`][Name::MdxJsxFlowTag] 24//! * [`SpaceOrTab`][Name::SpaceOrTab] 25//! * see [`mdx_jsx`][mdx_jsx] for more 26//! 27//! ## Recommendation 28//! 29//! See [`mdx_jsx`][mdx_jsx] for recommendations. 30//! 31//! ## References 32//! 33//! * [`jsx-flow.js` in `micromark-extension-mdx-jsx`](https://github.com/micromark/micromark-extension-mdx-jsx/blob/main/dev/lib/jsx-flow.js) 34//! * [`mdxjs.com`](https://mdxjs.com) 35//! 36//! [flow]: crate::construct::flow 37//! [mdx_jsx]: crate::construct::partial_mdx_jsx 38 39use crate::construct::partial_space_or_tab::{space_or_tab, space_or_tab_min_max}; 40use crate::event::Name; 41use crate::state::{Name as StateName, State}; 42use crate::tokenizer::Tokenizer; 43use crate::util::constant::TAB_SIZE; 44 45/// Start of MDX: JSX (flow). 46/// 47/// ```markdown 48/// > | <A /> 49/// ^ 50/// ``` 51pub fn start(tokenizer: &mut Tokenizer) -> State { 52 if tokenizer.parse_state.options.constructs.mdx_jsx_flow { 53 tokenizer.tokenize_state.token_1 = Name::MdxJsxFlowTag; 54 tokenizer.concrete = true; 55 if matches!(tokenizer.current, Some(b'\t' | b' ')) { 56 tokenizer.attempt(State::Next(StateName::MdxJsxFlowBefore), State::Nok); 57 State::Retry(space_or_tab_min_max( 58 tokenizer, 59 0, 60 if tokenizer.parse_state.options.constructs.code_indented { 61 TAB_SIZE - 1 62 } else { 63 usize::MAX 64 }, 65 )) 66 } else { 67 State::Retry(StateName::MdxJsxFlowBefore) 68 } 69 } else { 70 State::Nok 71 } 72} 73 74/// After optional whitespace, before of MDX JSX (flow). 75/// 76/// ```markdown 77/// > | <A /> 78/// ^ 79/// ``` 80pub fn before(tokenizer: &mut Tokenizer) -> State { 81 if Some(b'<') == tokenizer.current { 82 tokenizer.attempt( 83 State::Next(StateName::MdxJsxFlowAfter), 84 State::Next(StateName::MdxJsxFlowNok), 85 ); 86 State::Retry(StateName::MdxJsxStart) 87 } else { 88 State::Retry(StateName::MdxJsxFlowNok) 89 } 90} 91 92/// After an MDX JSX (flow) tag. 93/// 94/// ```markdown 95/// > | <A> 96/// ^ 97/// ``` 98pub fn after(tokenizer: &mut Tokenizer) -> State { 99 match tokenizer.current { 100 Some(b'\t' | b' ') => { 101 tokenizer.attempt(State::Next(StateName::MdxJsxFlowEnd), State::Nok); 102 State::Retry(space_or_tab(tokenizer)) 103 } 104 _ => State::Retry(StateName::MdxJsxFlowEnd), 105 } 106} 107 108/// After an MDX JSX (flow) tag, after optional whitespace. 109/// 110/// ```markdown 111/// > | <A> <B> 112/// ^ 113/// ``` 114pub fn end(tokenizer: &mut Tokenizer) -> State { 115 // We want to allow expressions directly after tags. 116 // See <https://github.com/micromark/micromark-extension-mdx-expression/blob/d5d92b9/packages/micromark-extension-mdx-expression/dev/lib/syntax.js#L183> 117 // for more info. 118 // 119 // Note: in the JS version of micromark, arbitrary extensions could be 120 // loaded. 121 // Here we know that only our own construct `mdx_expression_flow` can be 122 // enabled. 123 match tokenizer.current { 124 None | Some(b'\n') => { 125 reset(tokenizer); 126 State::Ok 127 } 128 // Another tag. 129 Some(b'<') => { 130 // We can’t just say: fine. 131 // Lines of blocks have to be parsed until an eol/eof. 132 tokenizer.attempt( 133 State::Next(StateName::MdxJsxFlowAfter), 134 State::Next(StateName::MdxJsxFlowNok), 135 ); 136 State::Retry(StateName::MdxJsxStart) 137 } 138 // An expression. 139 Some(b'{') if tokenizer.parse_state.options.constructs.mdx_expression_flow => { 140 tokenizer.attempt( 141 State::Next(StateName::MdxJsxFlowAfter), 142 State::Next(StateName::MdxJsxFlowNok), 143 ); 144 State::Retry(StateName::MdxExpressionFlowStart) 145 } 146 _ => { 147 reset(tokenizer); 148 State::Nok 149 } 150 } 151} 152 153/// At something that wasn’t an MDX JSX (flow) tag. 154/// 155/// ```markdown 156/// > | <A> x 157/// ^ 158/// ``` 159pub fn nok(tokenizer: &mut Tokenizer) -> State { 160 reset(tokenizer); 161 State::Nok 162} 163 164/// Reset state. 165fn reset(tokenizer: &mut Tokenizer) { 166 tokenizer.concrete = false; 167 tokenizer.tokenize_state.token_1 = Name::Data; 168}