From 1fd81fb64cbdbc1348207a1e06298026c81d4dc0 Mon Sep 17 00:00:00 2001 From: Lukas Stancik Date: Sat, 4 Apr 2026 07:47:44 +0000 Subject: [PATCH] Implement abstract syntax tree compilation of the new syntax --- src/mdast.rs | 23 +++++++++++++++++++++++ src/to_html.rs | 19 ++++++++++++++++++- src/to_mdast.rs | 38 +++++++++++++++++++++++++++++++++++++- 3 files changed, 78 insertions(+), 2 deletions(-) diff --git a/src/mdast.rs b/src/mdast.rs index 341bb97..e1e513f 100644 --- a/src/mdast.rs +++ b/src/mdast.rs @@ -193,6 +193,7 @@ pub enum Node { Break(Break), /// Code (phrasing). InlineCode(InlineCode), + Task(Task), /// Math (phrasing). InlineMath(InlineMath), /// Delete. @@ -269,6 +270,7 @@ impl fmt::Debug for Node { Node::Yaml(x) => x.fmt(f), Node::Break(x) => x.fmt(f), Node::InlineCode(x) => x.fmt(f), + Node::Task(x) => x.fmt(f), Node::InlineMath(x) => x.fmt(f), Node::Delete(x) => x.fmt(f), Node::Emphasis(x) => x.fmt(f), @@ -330,6 +332,7 @@ impl ToString for Node { Node::Toml(x) => x.value.clone(), Node::Yaml(x) => x.value.clone(), Node::InlineCode(x) => x.value.clone(), + Node::Task(x) => x.value.clone(), Node::InlineMath(x) => x.value.clone(), Node::MdxTextExpression(x) => x.value.clone(), Node::Html(x) => x.value.clone(), @@ -414,6 +417,7 @@ impl Node { Node::Yaml(x) => x.position.as_ref(), Node::Break(x) => x.position.as_ref(), Node::InlineCode(x) => x.position.as_ref(), + Node::Task(x) => x.position.as_ref(), Node::InlineMath(x) => x.position.as_ref(), Node::Delete(x) => x.position.as_ref(), Node::Emphasis(x) => x.position.as_ref(), @@ -453,6 +457,7 @@ impl Node { Node::Yaml(x) => x.position.as_mut(), Node::Break(x) => x.position.as_mut(), Node::InlineCode(x) => x.position.as_mut(), + Node::Task(x) => x.position.as_mut(), Node::InlineMath(x) => x.position.as_mut(), Node::Delete(x) => x.position.as_mut(), Node::Emphasis(x) => x.position.as_mut(), @@ -492,6 +497,7 @@ impl Node { Node::Yaml(x) => x.position = position, Node::Break(x) => x.position = position, Node::InlineCode(x) => x.position = position, + Node::Task(x) => x.position = position, Node::InlineMath(x) => x.position = position, Node::Delete(x) => x.position = position, Node::Emphasis(x) => x.position = position, @@ -892,6 +898,23 @@ pub struct InlineCode { pub position: Option, } +/// Code (phrasing). +/// +/// ```markdown +/// > | `a` +/// ^^^ +/// ``` +#[derive(Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Task { + // Text. + /// Content model. + pub value: String, + /// Positional info. + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] + pub position: Option, +} + /// Math (phrasing). /// /// ```markdown diff --git a/src/to_html.rs b/src/to_html.rs index b50ec91..7eded21 100644 --- a/src/to_html.rs +++ b/src/to_html.rs @@ -333,6 +333,7 @@ fn enter(context: &mut CompileContext) { Name::CodeIndented => on_enter_code_indented(context), Name::CodeFenced | Name::MathFlow => on_enter_raw_flow(context), Name::CodeText | Name::MathText => on_enter_raw_text(context), + Name::Task => on_enter_task(context), Name::Definition => on_enter_definition(context), Name::DefinitionDestinationString => on_enter_definition_destination_string(context), Name::Emphasis => on_enter_emphasis(context), @@ -371,7 +372,7 @@ fn exit(context: &mut CompileContext) { on_exit_drop(context); } Name::MdxEsm | Name::MdxFlowExpression | Name::MdxJsxFlowTag => on_exit_drop_slurp(context), - Name::CharacterEscapeValue | Name::CodeTextData | Name::Data | Name::MathTextData => { + Name::CharacterEscapeValue | Name::CodeTextData | Name::TaskContent | Name::Data | Name::MathTextData => { on_exit_data(context); } Name::AutolinkEmail => on_exit_autolink_email(context), @@ -391,6 +392,7 @@ fn exit(context: &mut CompileContext) { Name::CodeFencedFenceInfo => on_exit_raw_flow_fence_info(context), Name::CodeFlowChunk | Name::MathFlowChunk => on_exit_raw_flow_chunk(context), Name::CodeText | Name::MathText => on_exit_raw_text(context), + Name::Task => on_exit_task(context), Name::Definition => on_exit_definition(context), Name::DefinitionDestinationString => on_exit_definition_destination_string(context), Name::DefinitionLabelString => on_exit_definition_label_string(context), @@ -455,6 +457,13 @@ fn on_enter_block_quote(context: &mut CompileContext) { context.push("
"); } +/// Handle [`Enter`][Kind::Enter]:[`Task`][Name::Task]. +fn on_enter_task(context: &mut CompileContext) { + context.tight_stack.push(false); + // context.line_ending_if_needed(); + context.push(""); +} + /// Handle [`Enter`][Kind::Enter]:[`CodeIndented`][Name::CodeIndented]. fn on_enter_code_indented(context: &mut CompileContext) { context.raw_flow_seen_data = Some(false); @@ -757,6 +766,14 @@ fn on_exit_block_quote(context: &mut CompileContext) { context.push("
"); } +/// Handle [`Exit`][Kind::Exit]:[`Task`][Name::Task]. +fn on_exit_task(context: &mut CompileContext) { + context.tight_stack.pop(); + // context.line_ending_if_needed(); + // context.slurp_one_line_ending = false; + context.push(""); +} + /// Handle [`Exit`][Kind::Exit]:[`CharacterReferenceMarker`][Name::CharacterReferenceMarker]. fn on_exit_character_reference_marker(context: &mut CompileContext) { context.character_reference_marker = Some(b'&'); diff --git a/src/to_mdast.rs b/src/to_mdast.rs index 059287c..75e5fe9 100644 --- a/src/to_mdast.rs +++ b/src/to_mdast.rs @@ -7,7 +7,7 @@ use crate::mdast::{ ImageReference, InlineCode, InlineMath, Link, LinkReference, List, ListItem, Math, MdxFlowExpression, MdxJsxAttribute, MdxJsxExpressionAttribute, MdxJsxFlowElement, MdxJsxTextElement, MdxTextExpression, MdxjsEsm, Node, Paragraph, ReferenceKind, Root, Strong, - Table, TableCell, TableRow, Text, ThematicBreak, Toml, Yaml, + Table, TableCell, TableRow, Text, Task, ThematicBreak, Toml, Yaml, }; use crate::message; use crate::unist::{Point, Position}; @@ -267,6 +267,7 @@ fn enter(context: &mut CompileContext) -> Result<(), message::Message> { | Name::CharacterReference | Name::CodeFlowChunk | Name::CodeTextData + | Name::TaskContent | Name::Data | Name::FrontmatterChunk | Name::HtmlFlowData @@ -291,6 +292,7 @@ fn enter(context: &mut CompileContext) -> Result<(), message::Message> { Name::CodeFenced => on_enter_code_fenced(context), Name::CodeIndented => on_enter_code_indented(context), Name::CodeText => on_enter_code_text(context), + Name::Task => on_enter_task(context), Name::Definition => on_enter_definition(context), Name::Emphasis => on_enter_emphasis(context), Name::Frontmatter => on_enter_frontmatter(context), @@ -359,6 +361,7 @@ fn exit(context: &mut CompileContext) -> Result<(), message::Message> { Name::CharacterEscapeValue | Name::CodeFlowChunk | Name::CodeTextData + | Name::TaskContent | Name::Data | Name::FrontmatterChunk | Name::HtmlFlowData @@ -387,6 +390,7 @@ fn exit(context: &mut CompileContext) -> Result<(), message::Message> { Name::CodeFenced | Name::MathFlow => on_exit_raw_flow(context)?, Name::CodeIndented => on_exit_code_indented(context)?, Name::CodeText | Name::MathText => on_exit_raw_text(context)?, + Name::Task => on_exit_task(context)?, Name::DefinitionDestinationString => on_exit_definition_destination_string(context), Name::DefinitionLabelString | Name::GfmFootnoteDefinitionLabelString => { on_exit_definition_id(context); @@ -501,6 +505,15 @@ fn on_enter_code_text(context: &mut CompileContext) { context.buffer(); } +/// Handle [`Enter`][Kind::Enter]:[`Task`][Name::Task]. +fn on_enter_task(context: &mut CompileContext) { + context.tail_push(Node::Task(Task { + value: String::new(), + position: None, + })); + context.buffer(); +} + /// Handle [`Enter`][Kind::Enter]:[`MathText`][Name::MathText]. fn on_enter_math_text(context: &mut CompileContext) { context.tail_push(Node::InlineMath(InlineMath { @@ -1103,6 +1116,29 @@ fn on_exit_raw_text(context: &mut CompileContext) -> Result<(), message::Message Ok(()) } +/// Handle [`Exit`][Kind::Exit]:{[`CodeText`][Name::CodeText],[`MathText`][Name::MathText]}. +fn on_exit_task(context: &mut CompileContext) -> Result<(), message::Message> { + let mut value = context.resume().to_string(); + + let value_bytes = value.as_bytes(); + if value.len() > 2 + && value_bytes[0] == b' ' + && value_bytes[value.len() - 1] == b' ' + && !value_bytes.iter().all(|b| *b == b' ') + { + value.remove(0); + value.pop(); + } + + match context.tail_mut() { + Node::Task(node) => node.value = value, + _ => unreachable!("expected task on stack for value"), + } + + on_exit(context)?; + Ok(()) +} + /// Handle [`Exit`][Kind::Exit]:[`Data`][Name::Data] (and many text things). fn on_exit_data(context: &mut CompileContext) -> Result<(), message::Message> { let value = Slice::from_position(