diff options
Diffstat (limited to 'src/construct')
-rw-r--r-- | src/construct/flow.rs | 24 | ||||
-rw-r--r-- | src/construct/mdx_expression_flow.rs | 84 | ||||
-rw-r--r-- | src/construct/mdx_expression_text.rs | 34 | ||||
-rw-r--r-- | src/construct/mod.rs | 8 | ||||
-rw-r--r-- | src/construct/partial_mdx_expression.rs | 83 | ||||
-rw-r--r-- | src/construct/partial_mdx_jsx.rs | 2 | ||||
-rw-r--r-- | src/construct/text.rs | 11 |
7 files changed, 242 insertions, 4 deletions
diff --git a/src/construct/flow.rs b/src/construct/flow.rs index 5b2cbfe..e97ee63 100644 --- a/src/construct/flow.rs +++ b/src/construct/flow.rs @@ -16,6 +16,7 @@ //! * [Heading (atx)][crate::construct::heading_atx] //! * [Heading (setext)][crate::construct::heading_setext] //! * [HTML (flow)][crate::construct::html_flow] +//! * [MDX expression (flow)][crate::construct::mdx_expression_flow] //! * [MDX JSX (flow)][crate::construct::mdx_jsx_flow] //! * [Raw (flow)][crate::construct::raw_flow] (code (fenced), math (flow)) //! * [Thematic break][crate::construct::thematic_break] @@ -66,6 +67,13 @@ pub fn start(tokenizer: &mut Tokenizer) -> State { ); State::Retry(StateName::HtmlFlowStart) } + Some(b'{') => { + tokenizer.attempt( + State::Next(StateName::FlowAfter), + State::Next(StateName::FlowBeforeParagraph), + ); + State::Retry(StateName::MdxExpressionFlowStart) + } // Actual parsing: blank line? Indented code? Indented anything? // Tables, setext heading underlines, definitions, and paragraphs are // particularly weird. @@ -181,11 +189,25 @@ pub fn before_heading_setext(tokenizer: &mut Tokenizer) -> State { pub fn before_thematic_break(tokenizer: &mut Tokenizer) -> State { tokenizer.attempt( State::Next(StateName::FlowAfter), - State::Next(StateName::FlowBeforeGfmTable), + State::Next(StateName::FlowBeforeMdxExpression), ); State::Retry(StateName::ThematicBreakStart) } +/// At MDX expression (flow). +/// +/// ```markdown +/// > | {Math.PI} +/// ^ +/// ``` +pub fn before_mdx_expression(tokenizer: &mut Tokenizer) -> State { + tokenizer.attempt( + State::Next(StateName::FlowAfter), + State::Next(StateName::FlowBeforeGfmTable), + ); + State::Retry(StateName::MdxExpressionFlowStart) +} + /// At GFM table. /// /// ```markdown diff --git a/src/construct/mdx_expression_flow.rs b/src/construct/mdx_expression_flow.rs new file mode 100644 index 0000000..0faea31 --- /dev/null +++ b/src/construct/mdx_expression_flow.rs @@ -0,0 +1,84 @@ +//! To do. + +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: expression (flow). +/// +/// ```markdown +/// > | {Math.PI} +/// ^ +/// ``` +pub fn start(tokenizer: &mut Tokenizer) -> State { + if tokenizer.parse_state.options.constructs.mdx_expression_flow { + tokenizer.tokenize_state.token_1 = Name::MdxFlowExpression; + tokenizer.concrete = true; + if matches!(tokenizer.current, Some(b'\t' | b' ')) { + tokenizer.attempt(State::Next(StateName::MdxExpressionFlowBefore), 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::MdxExpressionFlowBefore) + } + } else { + State::Nok + } +} + +/// After optional whitespace, before of MDX expression (flow). +/// +/// ```markdown +/// > | {Math.PI} +/// ^ +/// ``` +pub fn before(tokenizer: &mut Tokenizer) -> State { + if Some(b'{') == tokenizer.current { + tokenizer.attempt(State::Next(StateName::MdxExpressionFlowAfter), State::Nok); + State::Retry(StateName::MdxExpressionStart) + } else { + State::Nok + } +} + +/// After an MDX expression (flow). +/// +/// ```markdown +/// > | {Math.PI} +/// ^ +/// ``` +pub fn after(tokenizer: &mut Tokenizer) -> State { + match tokenizer.current { + Some(b'\t' | b' ') => { + tokenizer.attempt(State::Next(StateName::MdxExpressionFlowEnd), State::Nok); + State::Retry(space_or_tab(tokenizer)) + } + _ => State::Retry(StateName::MdxExpressionFlowEnd), + } +} + +/// After an MDX expression (flow), after optional whitespace. +/// +/// ```markdown +/// > | {Math.PI}␠␊ +/// ^ +/// ``` +pub fn end(tokenizer: &mut Tokenizer) -> State { + tokenizer.concrete = false; + tokenizer.tokenize_state.token_1 = Name::Data; + + if matches!(tokenizer.current, None | Some(b'\n')) { + State::Ok + } else { + State::Nok + } +} diff --git a/src/construct/mdx_expression_text.rs b/src/construct/mdx_expression_text.rs new file mode 100644 index 0000000..8d061eb --- /dev/null +++ b/src/construct/mdx_expression_text.rs @@ -0,0 +1,34 @@ +//! To do. + +use crate::event::Name; +use crate::state::{Name as StateName, State}; +use crate::tokenizer::Tokenizer; + +/// Start of MDX: expression (text). +/// +/// ```markdown +/// > | a {Math.PI} c +/// ^ +/// ``` +pub fn start(tokenizer: &mut Tokenizer) -> State { + if Some(b'{') == tokenizer.current + && tokenizer.parse_state.options.constructs.mdx_expression_text + { + tokenizer.tokenize_state.token_1 = Name::MdxTextExpression; + tokenizer.attempt(State::Next(StateName::MdxExpressionTextAfter), State::Nok); + State::Retry(StateName::MdxExpressionStart) + } else { + State::Nok + } +} + +/// After an MDX expression (text) tag. +/// +/// ```markdown +/// > | a {Math.PI} c +/// ^ +/// ``` +pub fn after(tokenizer: &mut Tokenizer) -> State { + tokenizer.tokenize_state.token_1 = Name::Data; + State::Ok +} diff --git a/src/construct/mod.rs b/src/construct/mod.rs index 09ec976..1afa105 100644 --- a/src/construct/mod.rs +++ b/src/construct/mod.rs @@ -63,8 +63,10 @@ //! * [gfm label start footnote][gfm_label_start_footnote] //! * [gfm table][gfm_table] //! * [gfm task list item check][gfm_task_list_item_check] -//! * [mdx jsx (text)][mdx_jsx_text] +//! * [mdx expression (flow)][mdx_expression_flow] +//! * [mdx expression (text)][mdx_expression_text] //! * [mdx jsx (flow)][mdx_jsx_flow] +//! * [mdx jsx (text)][mdx_jsx_text] //! //! There are also several small subroutines typically used in different places: //! @@ -72,6 +74,7 @@ //! * [data][partial_data] //! * [destination][partial_destination] //! * [label][partial_label] +//! * [mdx expression][partial_mdx_expression] //! * [mdx jsx][partial_mdx_jsx] //! * [non lazy continuation][partial_non_lazy_continuation] //! * [space or tab][partial_space_or_tab] @@ -164,6 +167,8 @@ pub mod label_end; pub mod label_start_image; pub mod label_start_link; pub mod list_item; +pub mod mdx_expression_flow; +pub mod mdx_expression_text; pub mod mdx_jsx_flow; pub mod mdx_jsx_text; pub mod paragraph; @@ -171,6 +176,7 @@ pub mod partial_bom; pub mod partial_data; pub mod partial_destination; pub mod partial_label; +pub mod partial_mdx_expression; pub mod partial_mdx_jsx; pub mod partial_non_lazy_continuation; pub mod partial_space_or_tab; diff --git a/src/construct/partial_mdx_expression.rs b/src/construct/partial_mdx_expression.rs new file mode 100644 index 0000000..a949347 --- /dev/null +++ b/src/construct/partial_mdx_expression.rs @@ -0,0 +1,83 @@ +//! To do. + +use crate::construct::partial_space_or_tab::space_or_tab_min_max; +use crate::event::Name; +use crate::state::{Name as StateName, State}; +use crate::tokenizer::Tokenizer; +use alloc::format; + +/// Start of MDX: expression. +/// +/// ```markdown +/// > | a {Math.PI} c +/// ^ +/// ``` +pub fn start(tokenizer: &mut Tokenizer) -> State { + debug_assert_eq!(tokenizer.current, Some(b'{')); + tokenizer.enter(tokenizer.tokenize_state.token_1.clone()); + tokenizer.enter(Name::MdxExpressionMarker); + tokenizer.consume(); + tokenizer.exit(Name::MdxExpressionMarker); + State::Next(StateName::MdxExpressionBefore) +} + +pub fn before(tokenizer: &mut Tokenizer) -> State { + match tokenizer.current { + None => { + State::Error(format!( + "{}:{}: Unexpected end of file in expression, expected a corresponding closing brace for `{{`", + tokenizer.point.line, tokenizer.point.column + )) + } + Some(b'\n') => { + tokenizer.enter(Name::LineEnding); + tokenizer.consume(); + tokenizer.exit(Name::LineEnding); + State::Next(StateName::MdxExpressionEolAfter) + }, + Some(b'}') if tokenizer.tokenize_state.size == 0 => { + tokenizer.enter(Name::MdxExpressionMarker); + tokenizer.consume(); + tokenizer.exit(Name::MdxExpressionMarker); + tokenizer.exit(tokenizer.tokenize_state.token_1.clone()); + State::Ok + }, + Some(_) => { + tokenizer.enter(Name::MdxExpressionData); + State::Retry(StateName::MdxExpressionInside) + } + } +} + +pub fn inside(tokenizer: &mut Tokenizer) -> State { + if matches!(tokenizer.current, None | Some(b'\n')) + || (tokenizer.current == Some(b'}') && tokenizer.tokenize_state.size == 0) + { + tokenizer.exit(Name::MdxExpressionData); + State::Retry(StateName::MdxExpressionBefore) + } else { + // To do: only count if agnostic. + if tokenizer.current == Some(b'{') { + tokenizer.tokenize_state.size += 1; + } + + tokenizer.consume(); + State::Next(StateName::MdxExpressionInside) + } +} + +pub fn eol_after(tokenizer: &mut Tokenizer) -> State { + // Lazy continuation in a flow expression is a syntax error. + if tokenizer.tokenize_state.token_1 == Name::MdxFlowExpression && tokenizer.lazy { + State::Error(format!( + "{}:{}: Unexpected lazy line in expression in container, expected line to be prefixed with `>` when in a block quote, whitespace when in a list, etc", + tokenizer.point.line, tokenizer.point.column + )) + } else if matches!(tokenizer.current, Some(b'\t' | b' ')) { + tokenizer.attempt(State::Next(StateName::MdxExpressionBefore), State::Nok); + // To do: use `start_column` + constants.tabSize for max space to eat. + State::Next(space_or_tab_min_max(tokenizer, 0, usize::MAX)) + } else { + State::Retry(StateName::MdxExpressionBefore) + } +} diff --git a/src/construct/partial_mdx_jsx.rs b/src/construct/partial_mdx_jsx.rs index 583241a..05a2c0c 100644 --- a/src/construct/partial_mdx_jsx.rs +++ b/src/construct/partial_mdx_jsx.rs @@ -1049,7 +1049,7 @@ pub fn es_whitespace_eol_after(tokenizer: &mut Tokenizer) -> State { // Lazy continuation in a flow tag is a syntax error. if tokenizer.tokenize_state.token_1 == Name::MdxJsxFlowTag && tokenizer.lazy { State::Error(format!( - "{}:{}: Unexpected lazy line in container, expected line to be prefixed with `>` when in a block quote, whitespace when in a list, etc", + "{}:{}: Unexpected lazy line in jsx in container, expected line to be prefixed with `>` when in a block quote, whitespace when in a list, etc", tokenizer.point.line, tokenizer.point.column )) } else { diff --git a/src/construct/text.rs b/src/construct/text.rs index b59fe65..34ea071 100644 --- a/src/construct/text.rs +++ b/src/construct/text.rs @@ -18,6 +18,7 @@ //! * [Label start (image)][crate::construct::label_start_image] //! * [Label start (link)][crate::construct::label_start_link] //! * [Label end][crate::construct::label_end] +//! * [MDX: expression (text)][crate::construct::mdx_expression_text] //! * [MDX: JSX (text)][crate::construct::mdx_jsx_text] //! //! > 👉 **Note**: for performance reasons, hard break (trailing) is formed by @@ -30,7 +31,7 @@ use crate::state::{Name as StateName, State}; use crate::tokenizer::Tokenizer; /// Characters that can start something in text. -const MARKERS: [u8; 15] = [ +const MARKERS: [u8; 16] = [ b'!', // `label_start_image` b'$', // `raw_text` (math (text)) b'&', // `character_reference` @@ -45,6 +46,7 @@ const MARKERS: [u8; 15] = [ b'`', // `raw_text` (code (text)) b'h', // `gfm_autolink_literal` (`protocol` kind) b'w', // `gfm_autolink_literal` (`www.` kind) + b'{', // `mdx_expression_text` b'~', // `attention` (gfm strikethrough) ]; @@ -153,6 +155,13 @@ pub fn before(tokenizer: &mut Tokenizer) -> State { ); State::Retry(StateName::LabelEndStart) } + Some(b'{') => { + tokenizer.attempt( + State::Next(StateName::TextBefore), + State::Next(StateName::TextBeforeData), + ); + State::Retry(StateName::MdxExpressionTextStart) + } _ => State::Retry(StateName::TextBeforeData), } } |