aboutsummaryrefslogtreecommitdiffstats
path: root/src/construct
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 /src/construct
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/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
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),
}
}