aboutsummaryrefslogtreecommitdiffstats
path: root/src/construct
diff options
context:
space:
mode:
authorLibravatar Titus Wormer <tituswormer@gmail.com>2022-09-09 15:42:07 +0200
committerLibravatar Titus Wormer <tituswormer@gmail.com>2022-09-09 15:42:07 +0200
commit4711b1f0720eb54e458ca5a16cb655013693b628 (patch)
treede911aa4fee2a8e97104b30b3435c1f17af30140 /src/construct
parent118cc91fd56a9b4c93bec5b1cb4c5f25924d353e (diff)
downloadmarkdown-rs-4711b1f0720eb54e458ca5a16cb655013693b628.tar.gz
markdown-rs-4711b1f0720eb54e458ca5a16cb655013693b628.tar.bz2
markdown-rs-4711b1f0720eb54e458ca5a16cb655013693b628.zip
Add support for mdx attribute (value) expressions
Diffstat (limited to 'src/construct')
-rw-r--r--src/construct/partial_mdx_expression.rs30
-rw-r--r--src/construct/partial_mdx_jsx.rs50
2 files changed, 70 insertions, 10 deletions
diff --git a/src/construct/partial_mdx_expression.rs b/src/construct/partial_mdx_expression.rs
index a949347..aeea52e 100644
--- a/src/construct/partial_mdx_expression.rs
+++ b/src/construct/partial_mdx_expression.rs
@@ -36,13 +36,22 @@ pub fn before(tokenizer: &mut Tokenizer) -> State {
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
+ if tokenizer.tokenize_state.token_1 == Name::MdxJsxTagAttributeValueExpression && !tokenizer.tokenize_state.seen {
+ State::Error(format!(
+ "{}:{}: Unexpected empty in expression, expected a value between braces",
+ tokenizer.point.line, tokenizer.point.column
+ ))
+ } else {
+ tokenizer.tokenize_state.seen = false;
+ tokenizer.enter(Name::MdxExpressionMarker);
+ tokenizer.consume();
+ tokenizer.exit(Name::MdxExpressionMarker);
+ tokenizer.exit(tokenizer.tokenize_state.token_1.clone());
+ State::Ok
+ }
},
Some(_) => {
+ tokenizer.tokenize_state.seen = true;
tokenizer.enter(Name::MdxExpressionData);
State::Retry(StateName::MdxExpressionInside)
}
@@ -56,9 +65,11 @@ pub fn inside(tokenizer: &mut Tokenizer) -> State {
tokenizer.exit(Name::MdxExpressionData);
State::Retry(StateName::MdxExpressionBefore)
} else {
- // To do: only count if agnostic.
+ // To do: don’t count if gnostic.
if tokenizer.current == Some(b'{') {
tokenizer.tokenize_state.size += 1;
+ } else if tokenizer.current == Some(b'}') {
+ tokenizer.tokenize_state.size -= 1;
}
tokenizer.consume();
@@ -67,8 +78,11 @@ pub fn inside(tokenizer: &mut Tokenizer) -> State {
}
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 {
+ // Lazy continuation in a flow expression (or in a flow tag) is a syntax error.
+ if (tokenizer.tokenize_state.token_1 == Name::MdxFlowExpression
+ || tokenizer.tokenize_state.token_2 == Name::MdxJsxFlowTag)
+ && 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
diff --git a/src/construct/partial_mdx_jsx.rs b/src/construct/partial_mdx_jsx.rs
index 05a2c0c..9177b5b 100644
--- a/src/construct/partial_mdx_jsx.rs
+++ b/src/construct/partial_mdx_jsx.rs
@@ -610,7 +610,17 @@ pub fn attribute_before(tokenizer: &mut Tokenizer) -> State {
// End of tag.
Some(b'>') => State::Retry(StateName::MdxJsxTagEnd),
// Attribute expression.
- Some(b'{') => unreachable!("to do: attribute expression"),
+ Some(b'{') => {
+ // To do: force `spread: true` if gnostic.
+ // To do: pass `start_point` if gnostic.
+ tokenizer.tokenize_state.token_2 = tokenizer.tokenize_state.token_1.clone();
+ tokenizer.tokenize_state.token_1 = Name::MdxJsxTagAttributeExpression;
+ tokenizer.attempt(
+ State::Next(StateName::MdxJsxAttributeExpressionAfter),
+ State::Nok,
+ );
+ State::Retry(StateName::MdxExpressionStart)
+ }
_ => {
// Start of an attribute name.
if id_start(char_after_index(
@@ -633,6 +643,19 @@ pub fn attribute_before(tokenizer: &mut Tokenizer) -> State {
}
}
+/// After attribute expression.
+///
+/// ```markdown
+/// > | a <b {c} d/> e
+/// ^
+/// ```
+pub fn attribute_expression_after(tokenizer: &mut Tokenizer) -> State {
+ tokenizer.tokenize_state.token_1 = tokenizer.tokenize_state.token_2.clone();
+ tokenizer.tokenize_state.token_2 = Name::Data;
+ tokenizer.attempt(State::Next(StateName::MdxJsxAttributeBefore), State::Nok);
+ State::Retry(StateName::MdxJsxEsWhitespaceStart)
+}
+
/// In primary attribute name.
///
/// ```markdown
@@ -862,7 +885,16 @@ pub fn attribute_value_before(tokenizer: &mut Tokenizer) -> State {
State::Next(StateName::MdxJsxAttributeValueQuotedStart)
}
// Attribute value expression.
- Some(b'{') => unreachable!("to do: attribute value expression"),
+ Some(b'{') => {
+ // To do: pass `start_point` if gnostic.
+ tokenizer.tokenize_state.token_2 = tokenizer.tokenize_state.token_1.clone();
+ tokenizer.tokenize_state.token_1 = Name::MdxJsxTagAttributeValueExpression;
+ tokenizer.attempt(
+ State::Next(StateName::MdxJsxAttributeValueExpressionAfter),
+ State::Nok,
+ );
+ State::Retry(StateName::MdxExpressionStart)
+ }
_ => crash(
tokenizer,
"before attribute value",
@@ -878,6 +910,20 @@ pub fn attribute_value_before(tokenizer: &mut Tokenizer) -> State {
}
}
+/// After attribute value expression.
+///
+/// ```markdown
+/// > | a <b c={d} e/> f
+/// ^
+/// ```
+pub fn attribute_value_expression_after(tokenizer: &mut Tokenizer) -> State {
+ tokenizer.tokenize_state.token_1 = tokenizer.tokenize_state.token_2.clone();
+ tokenizer.tokenize_state.token_2 = Name::Data;
+ tokenizer.exit(Name::MdxJsxTagAttribute);
+ tokenizer.attempt(State::Next(StateName::MdxJsxAttributeBefore), State::Nok);
+ State::Retry(StateName::MdxJsxEsWhitespaceStart)
+}
+
/// Before quoted literal attribute value.
///
/// ```markdown