diff options
author | Titus Wormer <tituswormer@gmail.com> | 2022-09-19 17:29:59 +0200 |
---|---|---|
committer | Titus Wormer <tituswormer@gmail.com> | 2022-09-19 17:29:59 +0200 |
commit | 9cb9e37c33173c16cbafd345f43e43b5a550537d (patch) | |
tree | 6b02d2ea2c3ba904887b01f88aee369bd1a026d8 | |
parent | dfe1f2da522afa3e86680fa763b2f3c75e86b3ca (diff) | |
download | markdown-rs-9cb9e37c33173c16cbafd345f43e43b5a550537d.tar.gz markdown-rs-9cb9e37c33173c16cbafd345f43e43b5a550537d.tar.bz2 markdown-rs-9cb9e37c33173c16cbafd345f43e43b5a550537d.zip |
Add structs, enums for `mdast`
-rw-r--r-- | readme.md | 8 | ||||
-rw-r--r-- | src/lib.rs | 45 | ||||
-rw-r--r-- | src/mdast.rs | 1047 | ||||
-rw-r--r-- | src/to_html.rs (renamed from src/compiler.rs) | 10 | ||||
-rw-r--r-- | src/to_mdast.rs | 40 |
5 files changed, 1134 insertions, 16 deletions
@@ -180,12 +180,12 @@ The files in `src/` are as follows: — CommonMark, GFM, and other extension constructs used in micromark - `util/*.rs` — helpers often needed when parsing markdown -- `compiler.rs` - — turns events into a string of HTML - `event.rs` — things with meaning happening somewhere - `lib.rs` — core module +- `mdast.rs` + — syntax tree - `parser.rs` — turn a string of markdown into events - `resolve.rs` @@ -194,6 +194,10 @@ The files in `src/` are as follows: — steps of the state machine - `subtokenize.rs` — handle content in other content +- `to_html.rs` + — turns events into a string of HTML +- `to_mdast.rs` + — turns events into a syntax tree - `tokenizer.rs` — glue the states of the state machine together @@ -1,26 +1,37 @@ //! Public API of micromark. //! -//! This module exposes [`micromark`][] (and [`micromark_with_options`][]). -//! `micromark` is a safe way to transform (untrusted?) markdown into HTML. -//! `micromark_with_options` allows you to configure how markdown is turned into -//! HTML, such as by allowing dangerous HTML when you trust it. +//! This module exposes primarily [`micromark`][]. +//! It also exposes [`micromark_with_options`][] and [`micromark_to_mdast`][]. +//! +//! * [`micromark`][] +//! — safe way to transform (untrusted?) markdown into HTML +//! * [`micromark_with_options`][] +//! — like `micromark` but lets you configure how markdown is turned into +//! HTML, such as allowing dangerous HTML or turning on/off +//! different constructs (GFM, MDX, and the like) +//! * [`micromark_to_mdast`][] +//! — like `micromark_with_options` but compiles to a syntax tree #![no_std] extern crate alloc; -mod compiler; mod construct; mod event; +pub mod mdast; mod parser; mod resolve; mod state; mod subtokenize; +mod to_html; +mod to_mdast; mod tokenizer; mod util; -use crate::compiler::compile; -use crate::parser::parse; use alloc::{boxed::Box, fmt, string::String}; +use mdast::Root; +use parser::parse; +use to_html::compile as to_html; +use to_mdast::compile as to_mdast; /// Type of line endings in markdown. #[derive(Clone, Debug, Default, Eq, PartialEq)] @@ -1154,5 +1165,23 @@ pub fn micromark(value: &str) -> String { /// ``` pub fn micromark_with_options(value: &str, options: &Options) -> Result<String, String> { let (events, bytes) = parse(value, options)?; - Ok(compile(&events, bytes, options)) + Ok(to_html(&events, bytes, options)) +} + +/// Turn markdown into a syntax tree. +/// +/// ## Errors +/// +/// `to_mdast` never errors with normal markdown because markdown does not have +/// syntax errors, so feel free to `unwrap()`. +/// However, MDX does have syntax errors. +/// When MDX is turned on, there are several errors that can occur with how +/// JSX, expressions, or ESM are written. +/// +/// ## Examples +/// +/// To do. +pub fn micromark_to_mdast(value: &str, options: &Options) -> Result<Root, String> { + let (events, bytes) = parse(value, options)?; + Ok(to_mdast(&events, bytes, options)) } diff --git a/src/mdast.rs b/src/mdast.rs new file mode 100644 index 0000000..b60e891 --- /dev/null +++ b/src/mdast.rs @@ -0,0 +1,1047 @@ +//! [mdast][] syntax tree. +//! +//! [mdast]: https://github.com/syntax-tree/mdast + +// To do: example. +// To do: math. + +use alloc::{string::String, vec::Vec}; + +/// One place in a source file. +#[derive(Clone, Debug)] +pub struct Point { + /// 1-indexed integer representing a line in a source file. + pub line: usize, + /// 1-indexed integer representing a column in a source file. + pub column: usize, + /// 0-indexed integer representing a character in a source file. + pub offset: usize, +} + +/// Location of a node in a source file. +#[derive(Clone, Debug)] +pub struct Position { + /// Represents the place of the first character of the parsed source region. + pub start: Point, + /// Represents the place of the first character after the parsed source + /// region, whether it exists or not. + pub end: Point, +} + +/// Explicitness of a reference. +#[derive(Clone, Debug)] +pub enum ReferenceKind { + /// The reference is implicit, its identifier inferred from its content. + Shortcut, + /// The reference is explicit, its identifier inferred from its content. + Collapsed, + /// The reference is explicit, its identifier explicitly set. + Full, +} + +/// Represents how phrasing content is aligned. +#[derive(Clone, Debug)] +pub enum AlignKind { + /// See the `left` value of the `text-align` CSS property. + Left, + /// See the `right` value of the `text-align` CSS property. + Right, + /// See the `center` value of the `text-align` CSS property. + Center, + /// Phrasing content is aligned as defined by the host environment. + None, +} + +/// Node type. +#[derive(Clone, Debug)] +pub enum Kind { + /// Root node. + Root, + /// Paragraph node. + Paragraph, + /// Heading node. + Heading, + /// Thematic break node. + ThematicBreak, + /// Block quote node. + BlockQuote, + /// List node. + List, + /// List item node. + ListItem, + /// Html node. + Html, + /// Code node. + Code, + /// Definition node. + Definition, + /// Text node. + Text, + /// Emphasis node. + Emphasis, + /// Strong node. + Strong, + /// Code (inline) node. + InlineCode, + /// Break node. + Break, + /// Link node. + Link, + /// Image node. + Image, + /// Link reference node. + LinkReference, + /// Image reference node. + ImageReference, + /// Footnote definition node. + FootnoteDefinition, + /// Footnote reference node. + FootnoteReference, + /// Table node. + Table, + /// Table row node. + TableRow, + /// Table cell node. + TableCell, + /// Strong node. + Delete, + /// Yaml node. + Yaml, + /// Toml node. + Toml, + /// MDX: ESM node. + MdxjsEsm, + /// MDX: expression (flow). + MdxFlowExpression, + /// MDX: expression (phrasing). + MdxTextExpression, + /// MDX: JSX element (flow). + MdxJsxFlowElement, + /// MDX: JSX element (phrasing). + MdxJsxTextElement, + /// MDX: JSX attribute expression. + MdxJsxExpressionAttribute, + /// MDX: JSX attribute. + MdxJsxAttribute, + /// MDX: JSX attribute value expression. + MdxJsxAttributeValueExpression, +} + +/// Document content. +#[derive(Clone, Debug)] +pub enum DocumentContent { + /// Container content. + Container(ContainerContent), + /// Frontmatter content. + Frontmatter(FrontmatterContent), +} + +/// Container content. +#[derive(Clone, Debug)] +pub enum ContainerContent { + /// Block quote. + BlockQuote(BlockQuote), + /// Flow content. + Flow(FlowContent), + /// Footnote definition. + FootnoteDefinition(FootnoteDefinition), + /// MDX: JSX element (container). + JsxElement(MdxJsxFlowElement), + /// List. + List(List), +} + +/// Frontmatter content. +#[derive(Clone, Debug)] +pub enum FrontmatterContent { + /// MDX.js ESM. + Esm(MdxjsEsm), + /// Toml. + Toml(Toml), + /// Yaml. + Yaml(Yaml), +} + +/// Phrasing content. +#[derive(Clone, Debug)] +pub enum PhrasingContent { + /// Break. + Break(Break), + /// Code (phrasing). + Code(InlineCode), + /// Delete. + Delete(Delete), + /// Emphasis. + Emphasis(Emphasis), + // MDX: expression (text). + Expression(MdxTextExpression), + /// Footnote reference. + FootnoteReference(FootnoteReference), + /// Html (phrasing). + Html(Html), + /// Image. + Image(Image), + /// Image reference. + ImageReference(ImageReference), + // MDX: JSX element (text). + JsxElement(MdxJsxTextElement), + /// Link. + Link(Link), + /// Link reference. + LinkReference(LinkReference), + /// Strong + Strong(Strong), + /// Text. + Text(Text), +} + +/// Flow content. +#[derive(Clone, Debug)] +pub enum FlowContent { + /// Code (flow). + Code(Code), + /// Content. + Content(ContentContent), + // MDX: expression (flow). + Expression(MdxFlowExpression), + /// Heading. + Heading(Heading), + /// Html (flow). + Html(Html), + /// Table. + Table(Table), + /// Thematic break. + ThematicBreak(ThematicBreak), +} + +/// Table content. +#[derive(Clone, Debug)] +pub enum TableContent { + /// Table row. + Row(TableRow), +} + +/// Row content. +#[derive(Clone, Debug)] +pub enum RowContent { + /// Table cell. + Cell(TableCell), +} + +/// List content. +#[derive(Clone, Debug)] +pub enum ListContent { + /// List item. + Item(ListItem), +} + +/// Content. +#[derive(Clone, Debug)] +pub enum ContentContent { + /// Definition. + Definition(Definition), + /// Paragraph. + Paragraph(Paragraph), +} + +/// MDX: attribute content. +#[derive(Clone, Debug)] +pub enum AttributeContent { + /// MDX: JSX attribute expression. + Expression(MdxJsxExpressionAttribute), + /// MDX: JSX attribute. + Property(MdxJsxAttribute), +} + +/// MDX: attribute value. +#[derive(Clone, Debug)] +pub enum AttributeValue { + /// Expression value. + Expression(MdxJsxAttributeValueExpression), + /// Static value. + Literal(String), +} + +/// Document. +/// +/// ```markdown +/// > | a +/// ^ +/// ``` +#[derive(Clone, Debug)] +pub struct Root { + // Parent. + /// Node type. + pub kind: Kind, // `Kind::Root`. + /// Content model. + pub children: Vec<DocumentContent>, + /// Positional info. + pub position: Option<Position>, +} + +/// Paragraph. +/// +/// ```markdown +/// > | a +/// ^ +/// ``` +#[derive(Clone, Debug)] +pub struct Paragraph { + // Parent. + /// Node type. + pub kind: Kind, // `Kind::Paragraph`. + /// Content model. + pub children: Vec<PhrasingContent>, + /// Positional info. + pub position: Option<Position>, +} + +/// Heading. +/// +/// ```markdown +/// > | # a +/// ^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct Heading { + // Parent. + /// Node type. + pub kind: Kind, // `Kind::Heading`. + /// Content model. + pub children: Vec<PhrasingContent>, + /// Positional info. + pub position: Option<Position>, + // Extra. + /// Rank (between `1` and `6`, both including). + pub depth: u8, +} + +/// Thematic break. +/// +/// ```markdown +/// > | *** +/// ^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct ThematicBreak { + // Void. + /// Node type. + pub kind: Kind, // `Kind::ThematicBreak`. + /// Positional info. + pub position: Option<Position>, +} + +/// Block quote. +/// +/// ```markdown +/// > | > a +/// ^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct BlockQuote { + // Parent. + /// Node type. + pub kind: Kind, // `Kind::BlockQuote`. + /// Content model. + pub children: Vec<ContainerContent>, + /// Positional info. + pub position: Option<Position>, +} + +/// List. +/// +/// ```markdown +/// > | * a +/// ^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct List { + // Parent. + /// Node type. + pub kind: Kind, // `Kind::List`. + /// Content model. + pub children: Vec<ListContent>, + /// Positional info. + pub position: Option<Position>, + // Extra. + /// Ordered (`true`) or unordered (`false`). + pub ordered: bool, + /// Starting number of the list. + /// `None` when unordered. + pub start: Option<u8>, + /// One or more of its children are separated with a blank line from its + /// siblings (when `true`), or not (when `false`). + pub spread: bool, +} + +/// List item. +/// +/// ```markdown +/// > | * a +/// ^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct ListItem { + // Parent. + /// Node type. + pub kind: Kind, // `Kind::ListItem`. + /// Content model. + pub children: Vec<ContainerContent>, + /// Positional info. + pub position: Option<Position>, + // Extra. + /// The item contains two or more children separated by a blank line + /// (when `true`), or not (when `false`). + pub spread: bool, + /// GFM: whether the item is done (when `true`), not done (when `false`), + /// or indeterminate or not applicable (`None`). + pub checked: Option<bool>, +} + +/// Html (flow or phrasing). +/// +/// ```markdown +/// > | <a> +/// ^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct Html { + // Text. + /// Node type. + pub kind: Kind, // `Kind::Html`. + /// Content model. + pub value: String, + /// Positional info. + pub position: Option<Position>, +} + +/// Code (flow). +/// +/// ```markdown +/// > | ~~~ +/// ^^^ +/// > | a +/// ^^^ +/// > | ~~~ +/// ^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct Code { + // Text. + /// Node type. + pub kind: Kind, // `Kind::Code`. + /// Content model. + pub value: String, + /// Positional info. + pub position: Option<Position>, + // Extra. + /// The language of computer code being marked up. + pub lang: Option<String>, + /// Custom info relating to the node. + pub meta: Option<String>, +} + +/// Definition. +/// +/// ```markdown +/// > | [a]: b +/// ^^^^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct Definition { + // Void. + /// Node type. + pub kind: Kind, // `Kind::Definition`. + /// Positional info. + pub position: Option<Position>, + // Resource. + /// URL to the referenced resource. + pub url: String, + /// Advisory info for the resource, such as something that would be + /// appropriate for a tooltip. + pub title: Option<String>, + // Association. + /// Value that can match another node. + /// `identifier` is a source value: character escapes and character references + /// are *not* parsed. + /// Its value must be normalized. + pub identifier: String, + /// `label` is a string value: it works just like `title` on a link or a + /// `lang` on code: character escapes and character references are parsed. + /// + /// To normalize a value, collapse markdown whitespace (`[\t\n\r ]+`) to a + /// space, trim the optional initial and/or final space, and perform + /// case-folding. + pub label: Option<String>, +} + +/// Text. +/// +/// ```markdown +/// > | a +/// ^ +/// ``` +#[derive(Clone, Debug)] +pub struct Text { + // Text. + /// Node type. + pub kind: Kind, // `Kind::Text`. + /// Content model. + pub value: String, + /// Positional info. + pub position: Option<Position>, +} + +/// Emphasis. +/// +/// ```markdown +/// > | *a* +/// ^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct Emphasis { + // Parent. + /// Node type. + pub kind: Kind, // `Kind::Emphasis`. + /// Content model. + pub children: Vec<PhrasingContent>, + /// Positional info. + pub position: Option<Position>, +} + +/// Strong. +/// +/// ```markdown +/// > | **a** +/// ^^^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct Strong { + // Parent. + /// Node type. + pub kind: Kind, // `Kind::Strong`. + /// Content model. + pub children: Vec<PhrasingContent>, + /// Positional info. + pub position: Option<Position>, +} + +/// Code (phrasing). +/// +/// ```markdown +/// > | `a` +/// ^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct InlineCode { + // Text. + /// Node type. + pub kind: Kind, // `Kind::InlineCode`. + /// Content model. + pub value: String, + /// Positional info. + pub position: Option<Position>, +} + +/// Break. +/// +/// ```markdown +/// > | a\ +/// ^ +/// | b +/// ``` +#[derive(Clone, Debug)] +pub struct Break { + // Void. + /// Node type. + pub kind: Kind, // `Kind::Break`. + /// Positional info. + pub position: Option<Position>, +} + +/// Link. +/// +/// ```markdown +/// > | [a](b) +/// ^^^^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct Link { + // Parent. + /// Node type. + pub kind: Kind, // `Kind::Link`. + /// Content model. + pub children: Vec<PhrasingContent>, + /// Positional info. + pub position: Option<Position>, + // Resource. + /// URL to the referenced resource. + pub url: String, + /// Advisory info for the resource, such as something that would be + /// appropriate for a tooltip. + pub title: Option<String>, +} + +/// Image. +/// +/// ```markdown +/// > | ![a](b) +/// ^^^^^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct Image { + // Void. + /// Node type. + pub kind: Kind, // `Kind::Image`. + /// Positional info. + pub position: Option<Position>, + // Alternative. + /// Equivalent content for environments that cannot represent the node as + /// intended. + pub alt: String, + // Resource. + /// URL to the referenced resource. + pub url: String, + /// Advisory info for the resource, such as something that would be + /// appropriate for a tooltip. + pub title: Option<String>, +} + +/// Link reference. +/// +/// ```markdown +/// > | [a] +/// ^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct LinkReference { + // Parent. + /// Node type. + pub kind: Kind, // `Kind::LinkReference`. + /// Content model. + pub children: Vec<PhrasingContent>, + /// Positional info. + pub position: Option<Position>, + // Reference. + /// Explicitness of a reference. + pub reference_kind: ReferenceKind, + // Association. + /// Value that can match another node. + /// `identifier` is a source value: character escapes and character references + /// are *not* parsed. + /// Its value must be normalized. + pub identifier: String, + /// `label` is a string value: it works just like `title` on a link or a + /// `lang` on code: character escapes and character references are parsed. + /// + /// To normalize a value, collapse markdown whitespace (`[\t\n\r ]+`) to a + /// space, trim the optional initial and/or final space, and perform + /// case-folding. + pub label: Option<String>, +} + +/// Image reference. +/// +/// ```markdown +/// > | ![a] +/// ^^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct ImageReference { + // Void. + /// Node type. + pub kind: Kind, // `Kind::ImageReference`. + /// Positional info. + pub position: Option<Position>, + // Alternative. + /// Equivalent content for environments that cannot represent the node as + /// intended. + pub alt: String, + // Reference. + /// Explicitness of a reference. + pub reference_kind: ReferenceKind, + // Association. + /// Value that can match another node. + /// `identifier` is a source value: character escapes and character references + /// are *not* parsed. + /// Its value must be normalized. + pub identifier: String, + /// `label` is a string value: it works just like `title` on a link or a + /// `lang` on code: character escapes and character references are parsed. + /// + /// To normalize a value, collapse markdown whitespace (`[\t\n\r ]+`) to a + /// space, trim the optional initial and/or final space, and perform + /// case-folding. + pub label: Option<String>, +} + +/// Footnote definition (GFM). +/// +/// ```markdown +/// > | [^a]: b +/// ^^^^^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct FootnoteDefinition { + // Parent. + /// Node type. + pub kind: Kind, // `Kind::FootnoteDefinition`. + /// Content model. + pub children: Vec<ContainerContent>, + /// Positional info. + pub position: Option<Position>, + // Association. + /// Value that can match another node. + /// `identifier` is a source value: character escapes and character references + /// are *not* parsed. + /// Its value must be normalized. + pub identifier: String, + /// `label` is a string value: it works just like `title` on a link or a + /// `lang` on code: character escapes and character references are parsed. + /// + /// To normalize a value, collapse markdown whitespace (`[\t\n\r ]+`) to a + /// space, trim the optional initial and/or final space, and perform + /// case-folding. + pub label: Option<String>, +} + +/// Footnote reference (GFM). +/// +/// ```markdown +/// > | [^a] +/// ^^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct FootnoteReference { + // Void. + /// Node type. + pub kind: Kind, // `Kind::FootnoteReference`. + /// Positional info. + pub position: Option<Position>, + // Association. + /// Value that can match another node. + /// `identifier` is a source value: character escapes and character references + /// are *not* parsed. + /// Its value must be normalized. + pub identifier: String, + /// `label` is a string value: it works just like `title` on a link or a + /// `lang` on code: character escapes and character references are parsed. + /// + /// To normalize a value, collapse markdown whitespace (`[\t\n\r ]+`) to a + /// space, trim the optional initial and/or final space, and perform + /// case-folding. + pub label: Option<String>, +} + +/// Table (GFM). +/// +/// ```markdown +/// > | | a | +/// ^^^^^ +/// > | | - | +/// ^^^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct Table { + // Parent. + /// Node type. + pub kind: Kind, // `Kind::Table`. + /// Content model. + pub children: Vec<TableContent>, + /// Positional info. + pub position: Option<Position>, + // Extra. + /// Represents how cells in columns are aligned. + pub align: Vec<AlignKind>, +} + +/// Table row (GFM). +/// +/// ```markdown +/// > | | a | +/// ^^^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct TableRow { + // Parent. + /// Node type. + pub kind: Kind, // `Kind::TableRow`. + /// Content model. + pub children: Vec<RowContent>, + /// Positional info. + pub position: Option<Position>, +} + +/// Table cell (GFM). +/// +/// ```markdown +/// > | | a | +/// ^^^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct TableCell { + // Parent. + /// Node type. + pub kind: Kind, // `Kind::TableCell`. + /// Content model. + pub children: Vec<PhrasingContent>, + /// Positional info. + pub position: Option<Position>, +} + +/// Delete (GFM). +/// +/// ```markdown +/// > | ~~a~~ +/// ^^^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct Delete { + // Parent. + /// Node type. + pub kind: Kind, // `Kind::Delete`. + /// Content model. + pub children: Vec<PhrasingContent>, + /// Positional info. + pub position: Option<Position>, +} + +/// Yaml (frontmatter). +/// +/// ```markdown +/// > | --- +/// ^^^ +/// > | a: b +/// ^^^^ +/// > | --- +/// ^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct Yaml { + // Void. + /// Node type. + pub kind: Kind, // `Kind::Yaml`. + /// Content model. + pub value: String, + /// Positional info. + pub position: Option<Position>, +} + +/// Toml (frontmatter). +/// +/// ```markdown +/// > | +++ +/// ^^^ +/// > | a: b +/// ^^^^ +/// > | +++ +/// ^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct Toml { + // Void. + /// Node type. + pub kind: Kind, // `Kind::Toml`. + /// Content model. + pub value: String, + /// Positional info. + pub position: Option<Position>, +} + +/// MDX: ESM. +/// +/// ```markdown +/// > | import a from 'b' +/// ^^^^^^^^^^^^^^^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct MdxjsEsm { + // Literal. + /// Node type. + pub kind: Kind, // `Kind::MdxjsEsm`. + /// Content model. + pub value: String, + /// Positional info. + pub position: Option<Position>, +} + +/// MDX: expression (flow). +/// +/// ```markdown +/// > | {a} +/// ^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct MdxFlowExpression { + // Literal. + /// Node type. + pub kind: Kind, // `Kind::MdxFlowExpression`. + /// Content model. + pub value: String, + /// Positional info. + pub position: Option<Position>, +} + +/// MDX: expression (text). +/// +/// ```markdown +/// > | a {b} +/// ^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct MdxTextExpression { + // Literal. + /// Node type. + pub kind: Kind, // `Kind::MdxTextExpression`. + /// Content model. + pub value: String, + /// Positional info. + pub position: Option<Position>, +} + +/// MDX: JSX element (container). +/// +/// ```markdown +/// > | <a /> +/// ^^^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct MdxJsxFlowElement { + // Parent. + /// Node type. + pub kind: Kind, // `Kind::MdxJsxFlowElement`. + /// Content model. + pub children: Vec<ContainerContent>, + /// Positional info. + pub position: Option<Position>, + // JSX element. + /// Name. + /// + /// Fragments have no name. + pub name: Option<String>, + /// Attributes. + pub attributes: Vec<AttributeContent>, +} + +/// MDX: JSX element (text). +/// +/// ```markdown +/// > | <a />. +/// ^^^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct MdxJsxTextElement { + // Parent. + /// Node type. + pub kind: Kind, // `Kind::MdxJsxTextElement`. + /// Content model. + pub children: Vec<PhrasingContent>, + /// Positional info. + pub position: Option<Position>, + // JSX element. + /// Name. + /// + /// Fragments have no name. + pub name: Option<String>, + /// Attributes. + pub attributes: Vec<AttributeContent>, +} + +/// MDX: JSX attribute expression. +/// +/// ```markdown +/// > | <a {...b} /> +/// ^^^^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct MdxJsxExpressionAttribute { + // Literal. + /// Node type. + pub kind: Kind, // `Kind::MdxJsxExpressionAttribute`. + /// Content model. + pub value: String, + /// Positional info. + pub position: Option<Position>, +} + +/// MDX: JSX attribute. +/// +/// ```markdown +/// > | <a b /> +/// ^ +/// ``` +#[derive(Clone, Debug)] +pub struct MdxJsxAttribute { + // Void. + /// Node type. + pub kind: Kind, // `Kind::MdxJsxAttribute`. + /// Positional info. + pub position: Option<Position>, + /// Key. + pub name: String, + /// Value. + pub value: Option<AttributeValue>, +} + +/// MDX: JSX attribute value expression. +/// +/// ```markdown +/// > | <a b={c} /> +/// ^^^ +/// ``` +#[derive(Clone, Debug)] +pub struct MdxJsxAttributeValueExpression { + // Literal. + /// Node type. + pub kind: Kind, // `Kind::MdxJsxAttributeValueExpression`. + /// Content model. + pub value: String, + /// Positional info. + pub position: Option<Position>, +} + +#[cfg(test)] +mod tests { + use super::*; + use alloc::{string::ToString, vec}; + + #[test] + fn test() { + let text = Text { + kind: Kind::Text, + value: "a".to_string(), + position: Some(Position { + start: Point { + line: 1, + column: 1, + offset: 0, + }, + end: Point { + line: 1, + column: 2, + offset: 1, + }, + }), + }; + + let paragraph = Paragraph { + kind: Kind::Paragraph, + children: vec![PhrasingContent::Text(text)], + position: Some(Position { + start: Point { + line: 1, + column: 1, + offset: 0, + }, + end: Point { + line: 1, + column: 2, + offset: 1, + }, + }), + }; + + assert_eq!(paragraph.children.len(), 1); + assert!(matches!(¶graph.children[0], PhrasingContent::Text(_))); + } +} diff --git a/src/compiler.rs b/src/to_html.rs index eaa15ee..43be6a7 100644 --- a/src/compiler.rs +++ b/src/to_html.rs @@ -70,6 +70,7 @@ struct Definition { } /// GFM table: column alignment. +// To do: share with `mdast`. #[derive(Debug, PartialEq, Eq, Copy, Clone)] enum GfmTableAlign { /// No alignment. @@ -242,7 +243,7 @@ impl<'a> CompileContext<'a> { } } -/// Turn events and codes into a string of HTML. +/// Turn events and bytes into a string of HTML. pub fn compile(events: &[Event], bytes: &[u8], options: &Options) -> String { let mut index = 0; let mut line_ending_inferred = None; @@ -265,11 +266,8 @@ pub fn compile(events: &[Event], bytes: &[u8], options: &Options) -> String { } // Figure out which line ending style we’ll use. - let line_ending_default = if let Some(value) = line_ending_inferred { - value - } else { - options.default_line_ending.clone() - }; + let line_ending_default = + line_ending_inferred.unwrap_or_else(|| options.default_line_ending.clone()); let mut context = CompileContext::new(events, bytes, options, line_ending_default); let mut definition_indices = vec![]; diff --git a/src/to_mdast.rs b/src/to_mdast.rs new file mode 100644 index 0000000..d56134a --- /dev/null +++ b/src/to_mdast.rs @@ -0,0 +1,40 @@ +//! Turn events into a syntax tree. + +// To do: example. + +use crate::event::Event; +use crate::mdast; +use crate::Options; +use alloc::vec; + +/// Turn events and bytes into a syntax tree. +pub fn compile(events: &[Event], _bytes: &[u8], _options: &Options) -> mdast::Root { + mdast::Root { + kind: mdast::Kind::Root, + children: vec![], + position: Some(mdast::Position { + start: if events.is_empty() { + create_point(1, 1, 0) + } else { + point_from_event(&events[0]) + }, + end: if events.is_empty() { + create_point(1, 1, 0) + } else { + point_from_event(&events[events.len() - 1]) + }, + }), + } +} + +fn point_from_event(event: &Event) -> mdast::Point { + create_point(event.point.line, event.point.column, event.point.index) +} + +fn create_point(line: usize, column: usize, offset: usize) -> mdast::Point { + mdast::Point { + line, + column, + offset, + } +} |