aboutsummaryrefslogtreecommitdiffstats
path: root/src/construct/mdx_jsx_flow.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/construct/mdx_jsx_flow.rs150
1 files changed, 150 insertions, 0 deletions
diff --git a/src/construct/mdx_jsx_flow.rs b/src/construct/mdx_jsx_flow.rs
new file mode 100644
index 0000000..4c3dd23
--- /dev/null
+++ b/src/construct/mdx_jsx_flow.rs
@@ -0,0 +1,150 @@
+//! MDX JSX (flow) occurs in the [flow][] content type.
+//!
+//! ## Grammar
+//!
+//! MDX JSX (flow) forms with the following BNF
+//! (<small>see [construct][crate::construct] for character groups</small>):
+//!
+//! ```bnf
+//! mdx_jsx_flow ::= mdx_jsx *space_or_tab [mdx_jsx *space_or_tab]
+//!
+//! ; See the `partial_mdx_jsx` construct for the BNF of that part.
+//! ```
+//!
+//! As this construct occurs in flow, like all flow constructs, it must be
+//! followed by an eol (line ending) or eof (end of file).
+//! It is allowed to use multiple tags after each other, optionally with only
+//! whitespace between them.
+//!
+//! See [`mdx_jsx`][mdx_jsx] for more info.
+//!
+//! ## Tokens
+//!
+//! * [`MdxJsxFlowTag`][Name::MdxJsxFlowTag]
+//! * [`SpaceOrTab`][Name::SpaceOrTab]
+//! * see [`mdx_jsx`][mdx_jsx] for more
+//!
+//! ## Recommendation
+//!
+//! See [`mdx_jsx`][mdx_jsx] for recommendations.
+//!
+//! ## References
+//!
+//! * [`jsx-flow.js` in `micromark-extension-mdx-jsx`](https://github.com/micromark/micromark-extension-mdx-jsx/blob/main/dev/lib/jsx-flow.js)
+//! * [`mdxjs.com`](https://mdxjs.com)
+//!
+//! [flow]: crate::construct::flow
+//! [mdx_jsx]: crate::construct::partial_mdx_jsx
+
+use crate::construct::partial_space_or_tab::{space_or_tab, space_or_tab_min_max};
+use crate::event::Name;
+use crate::state::{Name as StateName, State};
+use crate::tokenizer::Tokenizer;
+use crate::util::constant::TAB_SIZE;
+
+/// Start of MDX: JSX (flow).
+///
+/// ```markdown
+/// > | <A />
+/// ^
+/// ```
+pub fn start(tokenizer: &mut Tokenizer) -> State {
+ if tokenizer.parse_state.options.constructs.mdx_jsx_flow {
+ tokenizer.tokenize_state.token_1 = Name::MdxJsxFlowTag;
+ tokenizer.concrete = true;
+ if matches!(tokenizer.current, Some(b'\t' | b' ')) {
+ tokenizer.attempt(State::Next(StateName::MdxJsxFlowBefore), State::Nok);
+ State::Retry(space_or_tab_min_max(
+ tokenizer,
+ 0,
+ if tokenizer.parse_state.options.constructs.code_indented {
+ TAB_SIZE - 1
+ } else {
+ usize::MAX
+ },
+ ))
+ } else {
+ State::Retry(StateName::MdxJsxFlowBefore)
+ }
+ } else {
+ State::Nok
+ }
+}
+
+/// After optional whitespace, before of MDX JSX (flow).
+///
+/// ```markdown
+/// > | <A />
+/// ^
+/// ```
+pub fn before(tokenizer: &mut Tokenizer) -> State {
+ if Some(b'<') == tokenizer.current {
+ tokenizer.attempt(
+ State::Next(StateName::MdxJsxFlowAfter),
+ State::Next(StateName::MdxJsxFlowNok),
+ );
+ State::Retry(StateName::MdxJsxStart)
+ } else {
+ State::Retry(StateName::MdxJsxFlowNok)
+ }
+}
+
+/// After an MDX JSX (flow) tag.
+///
+/// ```markdown
+/// > | <A>
+/// ^
+/// ```
+pub fn after(tokenizer: &mut Tokenizer) -> State {
+ match tokenizer.current {
+ Some(b'\t' | b' ') => {
+ tokenizer.attempt(State::Next(StateName::MdxJsxFlowEnd), State::Nok);
+ State::Retry(space_or_tab(tokenizer))
+ }
+ _ => State::Retry(StateName::MdxJsxFlowEnd),
+ }
+}
+
+/// After an MDX JSX (flow) tag, after optional whitespace.
+///
+/// ```markdown
+/// > | <A> <B>
+/// ^
+/// ```
+pub fn end(tokenizer: &mut Tokenizer) -> State {
+ match tokenizer.current {
+ None | Some(b'\n') => {
+ reset(tokenizer);
+ State::Ok
+ }
+ // Another?
+ Some(b'<') => {
+ tokenizer.attempt(
+ State::Next(StateName::MdxJsxFlowAfter),
+ State::Next(StateName::MdxJsxFlowNok),
+ );
+ State::Retry(StateName::MdxJsxStart)
+ }
+ _ => {
+ reset(tokenizer);
+ State::Nok
+ }
+ }
+}
+
+/// At something that wasn’t an MDX JSX (flow) tag.
+///
+/// ```markdown
+/// > | <A> x
+/// ^
+/// ```
+pub fn nok(tokenizer: &mut Tokenizer) -> State {
+ reset(tokenizer);
+ State::Nok
+}
+
+/// Reset state.
+fn reset(tokenizer: &mut Tokenizer) {
+ tokenizer.concrete = false;
+ tokenizer.tokenize_state.token_1 = Name::Data;
+}