diff options
author | Titus Wormer <tituswormer@gmail.com> | 2022-09-09 15:42:07 +0200 |
---|---|---|
committer | Titus Wormer <tituswormer@gmail.com> | 2022-09-09 15:42:07 +0200 |
commit | 4711b1f0720eb54e458ca5a16cb655013693b628 (patch) | |
tree | de911aa4fee2a8e97104b30b3435c1f17af30140 /src/construct | |
parent | 118cc91fd56a9b4c93bec5b1cb4c5f25924d353e (diff) | |
download | markdown-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.rs | 30 | ||||
-rw-r--r-- | src/construct/partial_mdx_jsx.rs | 50 |
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 |