diff options
Diffstat (limited to '')
-rw-r--r-- | src/compiler.rs | 21 | ||||
-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 | ||||
-rw-r--r-- | src/event.rs | 5 | ||||
-rw-r--r-- | src/lib.rs | 18 | ||||
-rw-r--r-- | src/state.rs | 28 | ||||
-rw-r--r-- | tests/mdx_expression_flow.rs | 234 | ||||
-rw-r--r-- | tests/mdx_expression_text.rs | 273 | ||||
-rw-r--r-- | tests/mdx_jsx_flow.rs | 10 |
14 files changed, 818 insertions, 17 deletions
diff --git a/src/compiler.rs b/src/compiler.rs index e878c09..1f029f5 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -364,8 +364,10 @@ fn enter(context: &mut CompileContext) { | Name::HeadingAtxText | Name::HeadingSetextText | Name::Label - | Name::MdxJsxTextTag + | Name::MdxFlowExpression + | Name::MdxTextExpression | Name::MdxJsxFlowTag + | Name::MdxJsxTextTag | Name::ReferenceString | Name::ResourceTitleString => on_enter_buffer(context), @@ -406,9 +408,11 @@ fn exit(context: &mut CompileContext) { Name::CodeFencedFenceMeta | Name::MathFlowFenceMeta | Name::MdxJsxTextTag + | Name::MdxTextExpression | Name::Resource => { on_exit_drop(context); } + Name::MdxFlowExpression | Name::MdxJsxFlowTag => on_exit_drop_slurp(context), Name::CharacterEscapeValue | Name::CodeTextData | Name::Data | Name::MathTextData => { on_exit_data(context); } @@ -469,7 +473,6 @@ fn exit(context: &mut CompileContext) { Name::ListOrdered | Name::ListUnordered => on_exit_list(context), Name::ListItem => on_exit_list_item(context), Name::ListItemValue => on_exit_list_item_value(context), - Name::MdxJsxFlowTag => on_exit_mdx_jsx_flow_tag(context), Name::Paragraph => on_exit_paragraph(context), Name::ReferenceString => on_exit_reference_string(context), Name::ResourceDestinationString => on_exit_resource_destination_string(context), @@ -1093,6 +1096,14 @@ fn on_exit_drop(context: &mut CompileContext) { context.resume(); } +/// Handle [`Exit`][Kind::Exit]:*. +/// +/// Resumes, ignores what was resumed, and slurps the following line ending. +fn on_exit_drop_slurp(context: &mut CompileContext) { + context.resume(); + context.slurp_one_line_ending = true; +} + /// Handle [`Exit`][Kind::Exit]:{[`CodeTextData`][Name::CodeTextData],[`Data`][Name::Data],[`CharacterEscapeValue`][Name::CharacterEscapeValue]}. fn on_exit_data(context: &mut CompileContext) { context.push(&encode( @@ -1676,12 +1687,6 @@ fn on_exit_media(context: &mut CompileContext) { } } -/// Handle [`Exit`][Kind::Exit]:[`MdxJsxFlowTag`][Name::MdxJsxFlowTag]. -fn on_exit_mdx_jsx_flow_tag(context: &mut CompileContext) { - context.resume(); - context.slurp_one_line_ending = true; -} - /// Handle [`Exit`][Kind::Exit]:[`Paragraph`][Name::Paragraph]. fn on_exit_paragraph(context: &mut CompileContext) { let tight = context.tight_stack.last().unwrap_or(&false); 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), } } diff --git a/src/event.rs b/src/event.rs index 5827207..b18835e 100644 --- a/src/event.rs +++ b/src/event.rs @@ -3166,6 +3166,11 @@ pub enum Name { /// ^ ^ ^ /// ``` ThematicBreakSequence, + + MdxFlowExpression, + MdxTextExpression, + MdxExpressionMarker, // void + MdxExpressionData, // void } /// List of void events, used to make sure everything is working well. @@ -301,6 +301,20 @@ pub struct Constructs { /// ^^^ /// ``` pub math_text: bool, + /// MDX: expression (flow). + /// + /// ```markdown + /// > | {Math.PI} + /// ^^^^^^^^^ + /// ``` + pub mdx_expression_flow: bool, + /// MDX: expression (text). + /// + /// ```markdown + /// > | a {Math.PI} c + /// ^^^^^^^^^ + /// ``` + pub mdx_expression_text: bool, /// MDX: JSX (flow). /// /// ```markdown @@ -356,6 +370,8 @@ impl Default for Constructs { list_item: true, math_flow: false, math_text: false, + mdx_expression_flow: false, + mdx_expression_text: false, mdx_jsx_flow: false, mdx_jsx_text: false, thematic_break: true, @@ -396,6 +412,8 @@ impl Constructs { code_indented: false, html_flow: false, html_text: false, + mdx_expression_flow: true, + mdx_expression_text: true, mdx_jsx_flow: true, mdx_jsx_text: true, ..Self::default() diff --git a/src/state.rs b/src/state.rs index db03a90..2158966 100644 --- a/src/state.rs +++ b/src/state.rs @@ -150,6 +150,7 @@ pub enum Name { FlowBeforeCodeIndented, FlowBeforeRaw, FlowBeforeHtml, + FlowBeforeMdxExpression, FlowBeforeMdxJsx, FlowBeforeHeadingAtx, FlowBeforeHeadingSetext, @@ -434,12 +435,38 @@ pub enum Name { TitleAtBlankLine, TitleEscape, TitleInside, + + MdxExpressionTextStart, + MdxExpressionTextAfter, + + MdxExpressionFlowStart, + MdxExpressionFlowBefore, + MdxExpressionFlowAfter, + MdxExpressionFlowEnd, + + MdxExpressionStart, + MdxExpressionBefore, + MdxExpressionInside, + MdxExpressionEolAfter, } #[allow(clippy::too_many_lines)] /// Call the corresponding state for a state name. pub fn call(tokenizer: &mut Tokenizer, name: Name) -> State { let func = match name { + Name::MdxExpressionTextStart => construct::mdx_expression_text::start, + Name::MdxExpressionTextAfter => construct::mdx_expression_text::after, + + Name::MdxExpressionFlowStart => construct::mdx_expression_flow::start, + Name::MdxExpressionFlowBefore => construct::mdx_expression_flow::before, + Name::MdxExpressionFlowAfter => construct::mdx_expression_flow::after, + Name::MdxExpressionFlowEnd => construct::mdx_expression_flow::end, + + Name::MdxExpressionStart => construct::partial_mdx_expression::start, + Name::MdxExpressionBefore => construct::partial_mdx_expression::before, + Name::MdxExpressionInside => construct::partial_mdx_expression::inside, + Name::MdxExpressionEolAfter => construct::partial_mdx_expression::eol_after, + Name::AttentionStart => construct::attention::start, Name::AttentionInside => construct::attention::inside, @@ -555,6 +582,7 @@ pub fn call(tokenizer: &mut Tokenizer, name: Name) -> State { Name::FlowBeforeCodeIndented => construct::flow::before_code_indented, Name::FlowBeforeRaw => construct::flow::before_raw, Name::FlowBeforeHtml => construct::flow::before_html, + Name::FlowBeforeMdxExpression => construct::flow::before_mdx_expression, Name::FlowBeforeMdxJsx => construct::flow::before_mdx_jsx, Name::FlowBeforeHeadingAtx => construct::flow::before_heading_atx, Name::FlowBeforeHeadingSetext => construct::flow::before_heading_setext, diff --git a/tests/mdx_expression_flow.rs b/tests/mdx_expression_flow.rs new file mode 100644 index 0000000..2a66a9d --- /dev/null +++ b/tests/mdx_expression_flow.rs @@ -0,0 +1,234 @@ +extern crate micromark; +use micromark::{micromark_with_options, Constructs, Options}; +use pretty_assertions::assert_eq; + +#[test] +fn mdx_expression_flow_agnostic() -> Result<(), String> { + let mdx = Options { + constructs: Constructs::mdx(), + ..Options::default() + }; + + assert_eq!( + micromark_with_options("{a}", &mdx)?, + "", + "should support an expression" + ); + + assert_eq!( + micromark_with_options("{}", &mdx)?, + "", + "should support an empty expression" + ); + + assert_eq!( + micromark_with_options("{a", &mdx).err().unwrap(), + "1:3: Unexpected end of file in expression, expected a corresponding closing brace for `{`", + "should crash if no closing brace is found (1)" + ); + + assert_eq!( + micromark_with_options("{b { c }", &mdx).err().unwrap(), + "1:9: Unexpected end of file in expression, expected a corresponding closing brace for `{`", + "should crash if no closing brace is found (2)" + ); + + assert_eq!( + micromark_with_options("{\n}\na", &mdx)?, + "<p>a</p>", + "should support a line ending in an expression" + ); + + assert_eq!( + micromark_with_options("{ a } \t\nb", &mdx)?, + "<p>b</p>", + "should support expressions followed by spaces" + ); + + assert_eq!( + micromark_with_options(" { a }\nb", &mdx)?, + "<p>b</p>", + "should support expressions preceded by spaces" + ); + + assert_eq!( + micromark_with_options("> {a\nb}", &mdx) + .err() + .unwrap(), + "2:1: Unexpected lazy line in expression in container, expected line to be prefixed with `>` when in a block quote, whitespace when in a list, etc", + "should not support lazyness (1)" + ); + + assert_eq!( + micromark_with_options("> a\n{b}", &mdx)?, + "<blockquote>\n<p>a</p>\n</blockquote>\n", + "should not support lazyness (2)" + ); + + assert_eq!( + micromark_with_options("> {a}\nb", &mdx)?, + "<blockquote>\n</blockquote>\n<p>b</p>", + "should not support lazyness (3)" + ); + + assert_eq!( + micromark_with_options("> {\n> a\nb}", &mdx) + .err() + .unwrap(), + "3:1: Unexpected lazy line in expression in container, expected line to be prefixed with `>` when in a block quote, whitespace when in a list, etc", + "should not support lazyness (4)" + ); + + Ok(()) +} + +// To do: swc. +// #[test] +// fn mdx_expression_flow_gnostic() -> Result<(), String> { +// assert_eq!( +// micromark_with_options("{a}", &swc), +// "", +// "should support an expression" +// ); + +// assert_eq!( +// micromark_with_options("{}", &swc)?, +// "", +// "should support an empty expression" +// ); + +// // To do: errors. +// // t.throws( +// // () => { +// // micromark_with_options("{a", &swc); +// // }, +// // /Unexpected end of file in expression, expected a corresponding closing brace for `{`/, +// // "should crash if no closing brace is found (1)" +// // ); + +// // To do: errors. +// // t.throws( +// // () => { +// // micromark_with_options("{b { c }", &swc); +// // }, +// // /Could not parse expression with swc: Unexpected content after expression/, +// // "should crash if no closing brace is found (2)" +// // ); + +// assert_eq!( +// micromark_with_options("{\n}\na", &swc)?, +// "<p>a</p>", +// "should support a line ending in an expression" +// ); + +// assert_eq!( +// micromark_with_options("{ a } \t\nb", &swc)?, +// "<p>b</p>", +// "should support expressions followed by spaces" +// ); + +// assert_eq!( +// micromark_with_options(" { a }\nb", &swc)?, +// "<p>b</p>", +// "should support expressions preceded by spaces" +// ); + +// assert_eq!( +// micromark_with_options(" {`\n a\n `}", &swc)?, +// "", +// "should support indented expressions" +// ); + +// assert_eq!( +// micromark_with_options("a{(b)}c", &swc)?, +// "<p>ac</p>", +// "should support expressions padded w/ parens" +// ); + +// assert_eq!( +// micromark_with_options("a{/* b */ ( (c) /* d */ + (e) )}f", &swc)?, +// "<p>af</p>", +// "should support expressions padded w/ parens and comments" +// ); + +// Ok(()) +// } + +// To do: move to JSX, actually test spread in expressions? +// To do: swc. +// #[test] +// fn mdx_expression_spread() -> Result<(), String> { +// // To do: errors. +// // t.throws( +// // () => { +// // micromark_with_options("a {b} c", &swc); +// // }, +// // /Unexpected `Property` in code: only spread elements are supported/, +// // "should crash if not a spread" +// // ); + +// // To do: errors. +// // t.throws( +// // () => { +// // micromark_with_options("a {...?} c", &swc); +// // }, +// // /Could not parse expression with swc: Unexpected token/, +// // "should crash on an incorrect spread" +// // ); + +// // To do: errors. +// // t.throws( +// // () => { +// // micromark_with_options("a {...b,c} d", &swc); +// // }, +// // /Unexpected extra content in spread: only a single spread is supported/, +// // "should crash if a spread and other things" +// // ); + +// assert_eq!( +// micromark_with_options("a {} b", &swc)?, +// "<p>a b</p>", +// "should support an empty spread" +// ); + +// // To do: errors. +// // t.throws( +// // () => { +// // micromark_with_options("a {} b", &swc); +// // }, +// // /Unexpected empty expression/, +// // "should crash on an empty spread w/ `allowEmpty: false`" +// // ); + +// // To do: errors. +// // t.throws( +// // () => { +// // micromark_with_options("{a=b}", &swc); +// // }, +// // /Could not parse expression with swc: Shorthand property assignments are valid only in destructuring patterns/, +// // "should crash if not a spread w/ `allowEmpty`" +// // ); + +// assert_eq!( +// micromark_with_options("a {/* b */} c", &swc)?, +// "<p>a c</p>", +// "should support a comment spread" +// ); + +// // To do: errors. +// // t.throws( +// // () => { +// // micromark_with_options("a {/* b */} c", &swc); +// // }, +// // /Unexpected empty expression/, +// // "should crash on a comment spread w/ `allowEmpty: false`" +// // ); + +// assert_eq!( +// micromark_with_options("a {...b} c", &swc)?, +// "<p>a c</p>", +// "should support a spread" +// ); + +// Ok(()) +// } diff --git a/tests/mdx_expression_text.rs b/tests/mdx_expression_text.rs new file mode 100644 index 0000000..b42faf2 --- /dev/null +++ b/tests/mdx_expression_text.rs @@ -0,0 +1,273 @@ +extern crate micromark; +use micromark::{micromark_with_options, Constructs, Options}; +use pretty_assertions::assert_eq; + +// To do: swc. +// #[test] +// fn mdx_expression_text_gnostic_core() -> Result<(), String> { +// assert_eq!( +// micromark_with_options("a {} b", &swc)?, +// "<p>a b</p>", +// "should support an empty expression (1)" +// ); + +// assert_eq!( +// micromark_with_options("a { \t\r\n} b", &swc)?, +// "<p>a b</p>", +// "should support an empty expression (2)" +// ); + +// assert_eq!( +// micromark_with_options("a {/**/} b", &swc)?, +// "<p>a b</p>", +// "should support a multiline comment (1)" +// ); + +// assert_eq!( +// micromark_with_options("a { /*\n*/\t} b", &swc)?, +// "<p>a b</p>", +// "should support a multiline comment (2)" +// ); + +// assert_eq!( +// micromark_with_options("a {/*b*//*c*/} d", &swc)?, +// "<p>a d</p>", +// "should support a multiline comment (3)" +// ); + +// assert_eq!( +// micromark_with_options("a {b/*c*/} d", &swc)?, +// "<p>a d</p>", +// "should support a multiline comment (4)" +// ); + +// assert_eq!( +// micromark_with_options("a {/*b*/c} d", &swc)?, +// "<p>a d</p>", +// "should support a multiline comment (4)" +// ); + +// // To do: errors. +// // t.throws( +// // () => { +// // micromark_with_options("a {//} b", &swc); +// // }, +// // /Could not parse expression with swc: Unexpected token/, +// // "should crash on an incorrect line comment (1)" +// // ); + +// // To do: errors. +// // t.throws( +// // () => { +// // micromark_with_options("a { // b } c", &swc); +// // }, +// // /Could not parse expression with swc: Unexpected token/, +// // "should crash on an incorrect line comment (2)" +// // ); + +// assert_eq!( +// micromark_with_options("a {//\n} b", &swc)?, +// "<p>a b</p>", +// "should support a line comment followed by a line ending" +// ); + +// assert_eq!( +// micromark_with_options("a {// b\nd} d", &swc)?, +// "<p>a d</p>", +// "should support a line comment followed by a line ending and an expression" +// ); + +// assert_eq!( +// micromark_with_options("a {b// c\n} d", &swc)?, +// "<p>a d</p>", +// "should support an expression followed by a line comment and a line ending" +// ); + +// assert_eq!( +// micromark_with_options("a {/*b*/ // c\n} d", &swc)?, +// "<p>a d</p>", +// "should support comments (1)" +// ); + +// assert_eq!( +// micromark_with_options("a {b.c} d", &swc)?, +// "<p>a d</p>", +// "should support expression statements (1)" +// ); + +// assert_eq!( +// micromark_with_options("a {1 + 1} b", &swc)?, +// "<p>a b</p>", +// "should support expression statements (2)" +// ); + +// assert_eq!( +// micromark_with_options("a {function () {}} b", &swc)?, +// "<p>a b</p>", +// "should support expression statements (3)" +// ); + +// // To do: errors. +// // t.throws( +// // () => { +// // micromark_with_options("a {var b = \"c\"} d", &swc); +// // }, +// // /Could not parse expression with swc: Unexpected token/, +// // "should crash on non-expressions" +// // ); + +// assert_eq!( +// micromark_with_options("> a {\n> b} c", &swc)?, +// "<blockquote>\n<p>a c</p>\n</blockquote>", +// "should support expressions in containers" +// ); + +// // To do: errors. +// // t.throws( +// // () => { +// // micromark_with_options("> a {\n> b<} c", &swc); +// // }, +// // /Could not parse expression with swc: Unexpected token/, +// // "should crash on incorrect expressions in containers (1)" +// // ); + +// // To do: errors. +// // t.throws( +// // () => { +// // micromark_with_options("> a {\n> b\n> c} d", &swc); +// // }, +// // /Could not parse expression with swc: Unexpected content after expression/, +// // "should crash on incorrect expressions in containers (2)" +// // ); + +// Ok(()) +// } + +#[test] +fn mdx_expression_text_agnostic() -> Result<(), String> { + let mdx = Options { + constructs: Constructs::mdx(), + ..Options::default() + }; + + assert_eq!( + micromark_with_options("a {b} c", &mdx)?, + "<p>a c</p>", + "should support an expression" + ); + + assert_eq!( + micromark_with_options("a {} b", &mdx)?, + "<p>a b</p>", + "should support an empty expression" + ); + + assert_eq!( + micromark_with_options("a {b c", &mdx).err().unwrap(), + "1:7: Unexpected end of file in expression, expected a corresponding closing brace for `{`", + "should crash if no closing brace is found (1)" + ); + + assert_eq!( + micromark_with_options("a {b { c } d", &mdx) + .err() + .unwrap(), + "1:13: Unexpected end of file in expression, expected a corresponding closing brace for `{`", + "should crash if no closing brace is found (2)" + ); + + assert_eq!( + micromark_with_options("a {\n} b", &mdx)?, + "<p>a b</p>", + "should support a line ending in an expression" + ); + + assert_eq!( + micromark_with_options("a } b", &mdx)?, + "<p>a } b</p>", + "should support just a closing brace" + ); + + assert_eq!( + micromark_with_options("{ a } b", &mdx)?, + "<p> b</p>", + "should support expressions as the first thing when following by other things" + ); + + Ok(()) +} + +// // To do: swc. +// #[test] +// fn mdx_expression_text_gnostic() -> Result<(), String> { +// assert_eq!( +// micromark_with_options("a {b} c", &swc)?, +// "<p>a c</p>", +// "should support an expression" +// ); + +// // To do: errors. +// // t.throws( +// // () => { +// // micromark_with_options("a {??} b", &swc); +// // }, +// // /Could not parse expression with swc: Unexpected token/, +// // "should crash on an incorrect expression" +// // ); + +// assert_eq!( +// micromark_with_options("a {} b", &swc)?, +// "<p>a b</p>", +// "should support an empty expression" +// ); + +// // To do: errors. +// // t.throws( +// // () => { +// // micromark_with_options("a {b c", &swc); +// // }, +// // /Unexpected end of file in expression, expected a corresponding closing brace for `{`/, +// // "should crash if no closing brace is found (1)" +// // ); + +// // To do: errors. +// // t.throws( +// // () => { +// // micromark_with_options("a {b { c } d", &swc); +// // }, +// // /Could not parse expression with swc: Unexpected content after expression/, +// // "should crash if no closing brace is found (2)" +// // ); + +// assert_eq!( +// micromark_with_options("a {\n} b", &swc)?, +// "<p>a b</p>", +// "should support a line ending in an expression" +// ); + +// assert_eq!( +// micromark_with_options("a } b", &swc)?, +// "<p>a } b</p>", +// "should support just a closing brace" +// ); + +// assert_eq!( +// micromark_with_options("{ a } b", &swc)?, +// "<p> b</p>", +// "should support expressions as the first thing when following by other things" +// ); + +// assert_eq!( +// micromark_with_options("a { /* { */ } b", &swc)?, +// "<p>a b</p>", +// "should support an unbalanced opening brace (if JS permits)" +// ); + +// assert_eq!( +// micromark_with_options("a { /* } */ } b", &swc)?, +// "<p>a b</p>", +// "should support an unbalanced closing brace (if JS permits)" +// ); + +// Ok(()) +// } diff --git a/tests/mdx_jsx_flow.rs b/tests/mdx_jsx_flow.rs index c9cd18d..ff53dcb 100644 --- a/tests/mdx_jsx_flow.rs +++ b/tests/mdx_jsx_flow.rs @@ -84,7 +84,7 @@ fn mdx_jsx_flow_essence() -> Result<(), String> { assert_eq!( micromark_with_options("> <X\n/>", &mdx).err().unwrap(), - "2:1: Unexpected lazy line in container, expected line to be prefixed with `>` when in a block quote, whitespace when in a list, etc", + "2:1: Unexpected lazy line in jsx in container, expected line to be prefixed with `>` when in a block quote, whitespace when in a list, etc", "should not support lazy flow (1)" ); @@ -92,7 +92,7 @@ fn mdx_jsx_flow_essence() -> Result<(), String> { micromark_with_options("> a\n> <X\n/>", &mdx) .err() .unwrap(), - "3:1: Unexpected lazy line in container, expected line to be prefixed with `>` when in a block quote, whitespace when in a list, etc", + "3:1: Unexpected lazy line in jsx in container, expected line to be prefixed with `>` when in a block quote, whitespace when in a list, etc", "should not support lazy flow (2)" ); @@ -100,7 +100,7 @@ fn mdx_jsx_flow_essence() -> Result<(), String> { micromark_with_options("> <a b='\nc'/> d", &mdx) .err() .unwrap(), - "2:1: Unexpected lazy line in container, expected line to be prefixed with `>` when in a block quote, whitespace when in a list, etc", + "2:1: Unexpected lazy line in jsx in container, expected line to be prefixed with `>` when in a block quote, whitespace when in a list, etc", "should not support lazy flow (3)" ); @@ -108,7 +108,7 @@ fn mdx_jsx_flow_essence() -> Result<(), String> { micromark_with_options("> <a b='c\n'/> d", &mdx) .err() .unwrap(), - "2:1: Unexpected lazy line in container, expected line to be prefixed with `>` when in a block quote, whitespace when in a list, etc", + "2:1: Unexpected lazy line in jsx in container, expected line to be prefixed with `>` when in a block quote, whitespace when in a list, etc", "should not support lazy flow (4)" ); @@ -116,7 +116,7 @@ fn mdx_jsx_flow_essence() -> Result<(), String> { micromark_with_options("> <a b='c\nd'/> e", &mdx) .err() .unwrap(), - "2:1: Unexpected lazy line in container, expected line to be prefixed with `>` when in a block quote, whitespace when in a list, etc", + "2:1: Unexpected lazy line in jsx in container, expected line to be prefixed with `>` when in a block quote, whitespace when in a list, etc", "should not support lazy flow (4)" ); |