Markdown parser fork with extended syntax for personal use.
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}