aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Titus Wormer <tituswormer@gmail.com>2022-09-09 13:17:59 +0200
committerLibravatar Titus Wormer <tituswormer@gmail.com>2022-09-09 13:17:59 +0200
commit118cc91fd56a9b4c93bec5b1cb4c5f25924d353e (patch)
tree3a33911c5b3f7da33919dfb48f1a0b8f8b46bb1b
parentffef323d3c927f84e94cae21afeb541be7320f1c (diff)
downloadmarkdown-rs-118cc91fd56a9b4c93bec5b1cb4c5f25924d353e.tar.gz
markdown-rs-118cc91fd56a9b4c93bec5b1cb4c5f25924d353e.tar.bz2
markdown-rs-118cc91fd56a9b4c93bec5b1cb4c5f25924d353e.zip
Add mdx expression (flow, text)
Diffstat (limited to '')
-rw-r--r--src/compiler.rs21
-rw-r--r--src/construct/flow.rs24
-rw-r--r--src/construct/mdx_expression_flow.rs84
-rw-r--r--src/construct/mdx_expression_text.rs34
-rw-r--r--src/construct/mod.rs8
-rw-r--r--src/construct/partial_mdx_expression.rs83
-rw-r--r--src/construct/partial_mdx_jsx.rs2
-rw-r--r--src/construct/text.rs11
-rw-r--r--src/event.rs5
-rw-r--r--src/lib.rs18
-rw-r--r--src/state.rs28
-rw-r--r--tests/mdx_expression_flow.rs234
-rw-r--r--tests/mdx_expression_text.rs273
-rw-r--r--tests/mdx_jsx_flow.rs10
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.
diff --git a/src/lib.rs b/src/lib.rs
index 2d5b044..420b14d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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)"
);