diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f7a8d91..1897ccf 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,26 +1,25 @@ -name: main -on: - - pull_request - - push jobs: - main: + coverage: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@v1 with: toolchain: stable - components: rustfmt, clippy - - run: cargo fmt --check && cargo clippy --examples --tests --benches --all-features - - run: cargo test --all-features - - run: cargo clippy -p mdast_util_to_markdown - - run: cargo test -p mdast_util_to_markdown - coverage: + - run: cargo install cargo-tarpaulin + - run: cargo tarpaulin --features json --out xml + - uses: codecov/codecov-action@v4 + main: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@v1 with: + components: clippy, rustfmt toolchain: stable - - run: cargo install cargo-tarpaulin && cargo tarpaulin --features json --out xml - - uses: codecov/codecov-action@v4 + - run: cargo fmt --check && cargo clippy --all-features --all-targets --workspace + - run: cargo test --all-features --workspace +name: main +on: + - pull_request + - push diff --git a/.gitignore b/.gitignore index 822c322..151575c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ *.log *.lock coverage/ -target +target/ commonmark-data.txt unicode-data.txt fuzz/target diff --git a/Cargo.toml b/Cargo.toml index 9c6f39f..a5630d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,47 +1,47 @@ -[package] -name = "markdown" -version = "1.0.0-alpha.21" -authors = ["Titus Wormer "] -edition = "2018" -rust-version = "1.56" -description = "CommonMark compliant markdown parser in Rust with ASTs and extensions" -homepage = "https://github.com/wooorm/markdown-rs" -repository = "https://github.com/wooorm/markdown-rs" -license = "MIT" -keywords = ["commonmark", "markdown", "parse", "render", "tokenize"] -categories = ["compilers", "encoding", "parser-implementations", "parsing", "text-processing"] -include = ["src/", "license"] - -[workspace] -members = ["generate", "mdast_util_to_markdown"] - -[workspace.dependencies] -pretty_assertions = "1" - [[bench]] +harness = false name = "bench" path = "benches/bench.rs" -harness = false - -[features] -default = [] -json = ["serde"] -serde = ["dep:serde"] -log = ["dep:log"] [dependencies] -log = { version = "0.4", optional = true } -unicode-id = { version = "0.3", features = ["no_std"] } -serde = { version = "1", features = ["derive"], optional = true } +log = { optional = true, version = "0.4" } +serde = { features = ["derive"], optional = true, version = "1" } +unicode-id = { features = ["no_std"], version = "0.3" } [dev-dependencies] -env_logger = "0.11" criterion = "0.5" +env_logger = "0.11" pretty_assertions = { workspace = true } serde_json = { version = "1" } swc_core = { version = "0.100", features = [ + "common", "ecma_ast", - "ecma_visit", "ecma_parser", - "common", + "ecma_visit", ] } + +[features] +default = [] +json = ["serde"] +log = ["dep:log"] +serde = ["dep:serde"] + +[package] +authors = ["Titus Wormer "] +categories = ["compilers", "encoding", "parser-implementations", "parsing", "text-processing"] +description = "CommonMark compliant markdown parser in Rust with ASTs and extensions" +edition = "2018" +homepage = "https://github.com/wooorm/markdown-rs" +include = ["src/", "license"] +keywords = ["commonmark", "markdown", "parse", "render", "tokenize"] +license = "MIT" +name = "markdown" +repository = "https://github.com/wooorm/markdown-rs" +rust-version = "1.56" +version = "1.0.0-alpha.21" + +[workspace] +members = ["generate", "mdast_util_to_markdown"] + +[workspace.dependencies] +pretty_assertions = "1" diff --git a/generate/Cargo.toml b/generate/Cargo.toml index d001b8b..0a48f11 100644 --- a/generate/Cargo.toml +++ b/generate/Cargo.toml @@ -1,11 +1,11 @@ +[dependencies] +regex = "1" +reqwest = "0.12" +tokio = { features = ["full"], version = "1" } + [package] -name = "markdown-generate" -version = "0.0.0" authors = ["Titus Wormer "] edition = "2018" +name = "markdown-generate" publish = false - -[dependencies] -regex = "1" -reqwest = "0.12" -tokio = { version = "1", features = ["full"] } +version = "0.0.0" diff --git a/mdast_util_to_markdown/Cargo.toml b/mdast_util_to_markdown/Cargo.toml index 2684686..0ac26ed 100644 --- a/mdast_util_to_markdown/Cargo.toml +++ b/mdast_util_to_markdown/Cargo.toml @@ -1,12 +1,12 @@ -[package] -name = "mdast_util_to_markdown" -version = "0.0.0" -edition = "2018" -license = "MIT" - [dependencies] markdown = { path = "../" } regex = { version = "1" } [dev-dependencies] pretty_assertions = { workspace = true } + +[package] +edition = "2018" +license = "MIT" +name = "mdast_util_to_markdown" +version = "0.0.0" diff --git a/mdast_util_to_markdown/src/association.rs b/mdast_util_to_markdown/src/association.rs index a5e9d16..b9fb67c 100644 --- a/mdast_util_to_markdown/src/association.rs +++ b/mdast_util_to_markdown/src/association.rs @@ -1,3 +1,7 @@ +//! Traits for . +//! +//! JS equivalent: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/70e1a4f/types/mdast/index.d.ts#L48. + use alloc::string::String; use markdown::mdast::{Definition, ImageReference, LinkReference}; diff --git a/mdast_util_to_markdown/src/configure.rs b/mdast_util_to_markdown/src/configure.rs index 3419d35..4f13a07 100644 --- a/mdast_util_to_markdown/src/configure.rs +++ b/mdast_util_to_markdown/src/configure.rs @@ -1,76 +1,96 @@ +//! Configuration. +//! +//! JS equivalent: https://github.com/syntax-tree/mdast-util-to-markdown/blob/fd6a508/lib/types.js#L307. + +#[derive(Clone, Copy)] +/// Configuration for indent of lists. +pub enum IndentOptions { + /// Depends on the item and its parent list: uses `IndentOptions::One` if + /// the item and list are tight and `IndentOptions::Tab` otherwise. + Mixed, + /// The size of the bullet plus one space. + One, + /// Tab stop. + Tab, +} + +/// Configuration. pub struct Options { - /// Marker to use for bullets of items in unordered lists ('*', '+', or '-', default: '*'). + /// Marker to use for bullets of items in unordered lists (`'*'`, `'+'`, or + /// `'-'`, default: `'*'`). pub bullet: char, - // Marker to use in certain cases where the primary bullet doesn’t work - // ('*', '+', or '-', default: '-' when bullet is '*', '*' otherwise). - pub bullet_other: char, - /// Marker to use for bullets of items in ordered lists ('.' or ')', default: '.'). + /// Marker to use for bullets of items in ordered lists (`'.'` or `')'`, + /// default: `'.'`). pub bullet_ordered: char, - /// Marker to use for emphasis ('*' or '_', default: '*'). + /// Marker to use in certain cases where the primary bullet doesn’t work + /// (`'*'`, `'+'`, or `'-'`, default: `'-'` when bullet is `'*'`, `'*'` + /// otherwise). + pub bullet_other: char, + /// Whether to add the same number of number signs (`#`) at the end of an + /// ATX heading as the opening sequence (`bool`, default: `false`). + pub close_atx: bool, + /// Marker to use for emphasis (`'*'` or `'_'`, default: `'*'`). pub emphasis: char, - // Marker to use for fenced code ('`' or '~', default: '`'). + /// Marker to use for fenced code (``'`'`` or `'~'`, default: ``'`'``). pub fence: char, - /// Whether to use fenced code always (bool, default: true). The default is to use fenced code - /// if there is a language defined, if the code is empty, or if it starts or ends in blank lines. + /// Whether to use fenced code always (`bool`, default: `true`). + /// The default is to use fenced code if there is a language defined, + /// if the code is empty, + /// or if it starts or ends in blank lines. pub fences: bool, - // How to indent the content of list items (default: 'IndentOptions::One'). + /// Whether to increment the counter of ordered lists items (`bool`, + /// default: `true`). + pub increment_list_marker: bool, + /// How to indent the content of list items (default: `IndentOptions::One`). pub list_item_indent: IndentOptions, - /// Marker to use for titles ('"' or "'", default: '"'). + /// Marker to use for titles (`'"'` or `"'"`, default: `'"'`). pub quote: char, - /// Marker to use for thematic breaks ('*', '-', or '_', default: '*'). - pub rule: char, - // Marker to use for strong ('*' or '_', default: '*'). - pub strong: char, - // Whether to increment the counter of ordered lists items (bool, default: true). - pub increment_list_marker: bool, - /// Whether to add the same number of number signs (#) at the end of an ATX heading as the - /// opening sequence (bool, default: false). - pub close_atx: bool, - /// Whether to always use resource links (bool, default: false). The default is to use autolinks - /// () when possible and resource links ([text](url)) otherwise. + /// Whether to always use resource links (`bool`, default: `false`). + /// The default is to use autolinks (``) when possible + /// and resource links (`[text](url)`) otherwise. pub resource_link: bool, - /// Whether to add spaces between markers in thematic breaks (bool, default: false). + /// Marker to use for thematic breaks (`'*'`, `'-'`, or `'_'`, default: + /// `'*'`). + pub rule: char, + /// Number of markers to use for thematic breaks (`u32`, default: `3`, min: + /// `3`). + pub rule_repetition: u32, + /// Whether to add spaces between markers in thematic breaks (`bool`, + /// default: `false`). pub rule_spaces: bool, - /// Whether to use setext headings when possible (bool, default: false). The default is to always - /// use ATX headings (# heading) instead of setext headings (heading\n=======). Setext headings - /// cannot be used for empty headings or headings with a rank of three or more. + /// Whether to use setext headings when possible (`bool`, default: + /// `false`). + /// The default is to always use ATX headings (`# heading`) instead of + /// setext headings (`heading\n=======`). + /// Setext headings cannot be used for empty headings or headings with a + /// rank of three or more. pub setext: bool, - /// Whether to join definitions without a blank line (bool, default: false). + /// Marker to use for strong (`'*'` or `'_'`, default: `'*'`). + pub strong: char, + /// Whether to join definitions without a blank line (`bool`, default: + /// `false`). pub tight_definitions: bool, - // Number of markers to use for thematic breaks (u32, default: 3, min: 3). - pub rule_repetition: u32, -} - -#[derive(Copy, Clone)] -pub enum IndentOptions { - // Depends on the item and its parent list uses 'One' if the item and list are tight and 'Tab' - // otherwise. - Mixed, - // The size of the bullet plus one space. - One, - /// Tab stop. - Tab, } impl Default for Options { fn default() -> Self { Self { bullet: '*', - bullet_other: '-', bullet_ordered: '.', + bullet_other: '-', + close_atx: false, emphasis: '*', fence: '`', fences: true, increment_list_marker: true, - rule_repetition: 3, list_item_indent: IndentOptions::One, quote: '"', + resource_link: false, rule: '*', - strong: '*', - close_atx: false, + rule_repetition: 3, rule_spaces: false, - resource_link: false, setext: false, + strong: '*', tight_definitions: false, } } diff --git a/mdast_util_to_markdown/src/construct_name.rs b/mdast_util_to_markdown/src/construct_name.rs index ac1b5dd..6bac427 100644 --- a/mdast_util_to_markdown/src/construct_name.rs +++ b/mdast_util_to_markdown/src/construct_name.rs @@ -1,14 +1,16 @@ +//! Names of the things being serialized. +//! +//! JS equivalent: https://github.com/syntax-tree/mdast-util-to-markdown/blob/fd6a508/index.d.ts#L18. + #[derive(Clone, PartialEq)] pub enum ConstructName { + /// Whole autolink. /// - //// Whole autolink. - //// - //// ```markdown - //// > | and - //// ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^ - //// ``` + /// ```markdown + /// > | and + /// ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^ + /// ``` Autolink, - /// /// Whole block quote. /// /// ```markdown @@ -18,7 +20,6 @@ pub enum ConstructName { /// ^ /// ``` Blockquote, - /// /// Whole code (indented). /// /// ```markdown @@ -26,7 +27,6 @@ pub enum ConstructName { /// ^^^^^^^^^^^^^^^^^^ /// ``` CodeIndented, - /// /// Whole code (fenced). /// /// ````markdown @@ -38,7 +38,6 @@ pub enum ConstructName { /// ^^^ /// ```` CodeFenced, - /// /// Code (fenced) language, when fenced with grave accents. /// /// ````markdown @@ -48,7 +47,6 @@ pub enum ConstructName { /// | ``` /// ```` CodeFencedLangGraveAccent, - /// /// Code (fenced) language, when fenced with tildes. /// /// ````markdown @@ -58,7 +56,6 @@ pub enum ConstructName { /// | ~~~ /// ```` CodeFencedLangTilde, - /// /// Code (fenced) meta string, when fenced with grave accents. /// /// ````markdown @@ -68,7 +65,6 @@ pub enum ConstructName { /// | ``` /// ```` CodeFencedMetaGraveAccent, - /// /// Code (fenced) meta string, when fenced with tildes. /// /// ````markdown @@ -78,7 +74,6 @@ pub enum ConstructName { /// | ~~~ /// ```` CodeFencedMetaTilde, - /// /// Whole definition. /// /// ```markdown @@ -86,7 +81,6 @@ pub enum ConstructName { /// ^^^^^^^^^^ /// ``` Definition, - /// /// Destination (literal) (occurs in definition, image, link). /// /// ```markdown @@ -96,7 +90,6 @@ pub enum ConstructName { /// ^^^ /// ``` DestinationLiteral, - /// /// Destination (raw) (occurs in definition, image, link). /// /// ```markdown @@ -106,7 +99,6 @@ pub enum ConstructName { /// ^ /// ``` DestinationRaw, - /// /// Emphasis. /// /// ```markdown @@ -114,7 +106,6 @@ pub enum ConstructName { /// ^^^ /// ``` Emphasis, - /// /// Whole heading (atx). /// /// ```markdown @@ -122,7 +113,6 @@ pub enum ConstructName { /// ^^^^^^^ /// ``` HeadingAtx, - /// /// Whole heading (setext). /// /// ```markdown @@ -132,7 +122,6 @@ pub enum ConstructName { /// ^^^^^ /// ``` HeadingSetext, - /// /// Whole image. /// /// ```markdown @@ -142,7 +131,6 @@ pub enum ConstructName { /// ^^^^ /// ``` Image, - /// /// Whole image reference. /// /// ```markdown @@ -150,7 +138,6 @@ pub enum ConstructName { /// ^^^^ /// ``` ImageReference, - /// /// Label (occurs in definitions, image reference, image, link reference, /// link). /// @@ -165,7 +152,6 @@ pub enum ConstructName { /// ^^^ /// ``` Label, - /// /// Whole link. /// /// ```markdown @@ -175,7 +161,6 @@ pub enum ConstructName { /// ^^^ /// ``` Link, - /// /// Whole link reference. /// /// ```markdown @@ -183,7 +168,6 @@ pub enum ConstructName { /// ^^^ /// ``` LinkReference, - /// /// List. /// /// ```markdown @@ -193,7 +177,6 @@ pub enum ConstructName { /// ^^^^ /// ``` List, - /// /// List item. /// /// ```markdown @@ -203,7 +186,6 @@ pub enum ConstructName { /// ^^^^ /// ``` ListItem, - /// /// Paragraph. /// /// ```markdown @@ -213,7 +195,6 @@ pub enum ConstructName { /// ^^ /// ``` Paragraph, - /// /// Phrasing (occurs in headings, paragraphs, etc). /// /// ```markdown @@ -221,7 +202,6 @@ pub enum ConstructName { /// ^ /// ``` Phrasing, - /// /// Reference (occurs in image, link). /// /// ```markdown @@ -229,7 +209,6 @@ pub enum ConstructName { /// ^^ /// ``` Reference, - /// /// Strong. /// /// ```markdown @@ -237,7 +216,6 @@ pub enum ConstructName { /// ^^^^^ /// ``` Strong, - /// /// Title using single quotes (occurs in definition, image, link). /// /// ```markdown @@ -245,7 +223,6 @@ pub enum ConstructName { /// ^^^ /// ``` TitleApostrophe, - /// /// Title using double quotes (occurs in definition, image, link). /// /// ```markdown diff --git a/mdast_util_to_markdown/src/handle/blockquote.rs b/mdast_util_to_markdown/src/handle/blockquote.rs index 1aa9603..c7ed952 100644 --- a/mdast_util_to_markdown/src/handle/blockquote.rs +++ b/mdast_util_to_markdown/src/handle/blockquote.rs @@ -1,15 +1,15 @@ -use alloc::string::String; -use markdown::{ - mdast::{Blockquote, Node}, - message::Message, -}; +//! JS equivalent: https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/handle/blockquote.js +use super::Handle; use crate::{ construct_name::ConstructName, state::{Info, State}, }; - -use super::Handle; +use alloc::string::String; +use markdown::{ + mdast::{Blockquote, Node}, + message::Message, +}; impl Handle for Blockquote { fn handle( diff --git a/mdast_util_to_markdown/src/handle/break.rs b/mdast_util_to_markdown/src/handle/break.rs index 95cef5a..e63a3eb 100644 --- a/mdast_util_to_markdown/src/handle/break.rs +++ b/mdast_util_to_markdown/src/handle/break.rs @@ -1,15 +1,15 @@ -use alloc::string::ToString; -use markdown::{ - mdast::{Break, Node}, - message::Message, -}; +//! JS equivalent: https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/handle/break.js +use super::Handle; use crate::{ state::{Info, State}, util::pattern_in_scope::pattern_in_scope, }; - -use super::Handle; +use alloc::string::ToString; +use markdown::{ + mdast::{Break, Node}, + message::Message, +}; impl Handle for Break { fn handle( @@ -20,9 +20,12 @@ impl Handle for Break { _node: &Node, ) -> Result { for pattern in state.r#unsafe.iter() { + // If we can’t put eols in this construct (setext headings, tables), use a + // space instead. if pattern.character == '\n' && pattern_in_scope(&state.stack, pattern) { - let is_whitespace_or_tab = info.before.chars().any(|c| c == ' ' || c == '\t'); - if is_whitespace_or_tab { + let space_or_tab = info.before.chars().any(|c| c == '\t' || c == ' '); + + if space_or_tab { return Ok("".to_string()); } diff --git a/mdast_util_to_markdown/src/handle/code.rs b/mdast_util_to_markdown/src/handle/code.rs index 4bb9caf..d92a143 100644 --- a/mdast_util_to_markdown/src/handle/code.rs +++ b/mdast_util_to_markdown/src/handle/code.rs @@ -1,12 +1,6 @@ -use alloc::{ - format, - string::{String, ToString}, -}; -use markdown::{ - mdast::{Code, Node}, - message::Message, -}; +//! JS equivalent: https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/handle/code.js +use super::Handle; use crate::{ construct_name::ConstructName, state::{Info, State}, @@ -15,8 +9,14 @@ use crate::{ longest_char_streak::longest_char_streak, safe::SafeConfig, }, }; - -use super::Handle; +use alloc::{ + format, + string::{String, ToString}, +}; +use markdown::{ + mdast::{Code, Node}, + message::Message, +}; impl Handle for Code { fn handle( diff --git a/mdast_util_to_markdown/src/handle/definition.rs b/mdast_util_to_markdown/src/handle/definition.rs index 17d7c97..3338027 100644 --- a/mdast_util_to_markdown/src/handle/definition.rs +++ b/mdast_util_to_markdown/src/handle/definition.rs @@ -1,16 +1,19 @@ -use alloc::string::String; -use markdown::{ - mdast::{Definition, Node}, - message::Message, -}; +//! JS equivalent: https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/handle/definition.js +use super::Handle; use crate::{ construct_name::ConstructName, state::{Info, State}, - util::{check_quote::check_quote, safe::SafeConfig}, + util::{ + check_quote::check_quote, contains_control_or_whitespace::contains_control_or_whitespace, + safe::SafeConfig, + }, +}; +use alloc::string::String; +use markdown::{ + mdast::{Definition, Node}, + message::Message, }; - -use super::Handle; impl Handle for Definition { fn handle( @@ -36,7 +39,7 @@ impl Handle for Definition { state.exit(); - if self.url.is_empty() || contain_control_char_or_whitespace(&self.url) { + if self.url.is_empty() || contains_control_or_whitespace(&self.url) { state.enter(ConstructName::DestinationLiteral); value.push('<'); value.push_str(&state.safe(&self.url, &SafeConfig::new(&value, ">", None))); @@ -73,7 +76,3 @@ impl Handle for Definition { Ok(value) } } - -fn contain_control_char_or_whitespace(value: &str) -> bool { - value.chars().any(|c| c.is_whitespace() || c.is_control()) -} diff --git a/mdast_util_to_markdown/src/handle/emphasis.rs b/mdast_util_to_markdown/src/handle/emphasis.rs index 56c0184..85f75f4 100644 --- a/mdast_util_to_markdown/src/handle/emphasis.rs +++ b/mdast_util_to_markdown/src/handle/emphasis.rs @@ -1,16 +1,16 @@ -use alloc::format; -use markdown::{ - mdast::{Emphasis, Node}, - message::Message, -}; +//! JS equivalent: https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/handle/emphasis.js +use super::Handle; use crate::{ construct_name::ConstructName, state::{Info, State}, util::check_emphasis::check_emphasis, }; - -use super::Handle; +use alloc::format; +use markdown::{ + mdast::{Emphasis, Node}, + message::Message, +}; impl Handle for Emphasis { fn handle( diff --git a/mdast_util_to_markdown/src/handle/heading.rs b/mdast_util_to_markdown/src/handle/heading.rs index e78553c..7dd6738 100644 --- a/mdast_util_to_markdown/src/handle/heading.rs +++ b/mdast_util_to_markdown/src/handle/heading.rs @@ -1,16 +1,16 @@ -use alloc::format; -use markdown::{ - mdast::{Heading, Node}, - message::Message, -}; +//! JS equivalent: https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/handle/heading.js +use super::Handle; use crate::{ construct_name::ConstructName, state::{Info, State}, util::format_heading_as_setext::format_heading_as_setext, }; - -use super::Handle; +use alloc::format; +use markdown::{ + mdast::{Heading, Node}, + message::Message, +}; impl Handle for Heading { fn handle( diff --git a/mdast_util_to_markdown/src/handle/html.rs b/mdast_util_to_markdown/src/handle/html.rs index 35dceee..383e838 100644 --- a/mdast_util_to_markdown/src/handle/html.rs +++ b/mdast_util_to_markdown/src/handle/html.rs @@ -1,12 +1,12 @@ +//! JS equivalent: https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/handle/html.js + +use super::Handle; +use crate::state::{Info, State}; use markdown::{ mdast::{Html, Node}, message::Message, }; -use crate::state::{Info, State}; - -use super::Handle; - impl Handle for Html { fn handle( &self, diff --git a/mdast_util_to_markdown/src/handle/image.rs b/mdast_util_to_markdown/src/handle/image.rs index c7a4874..6eae7dc 100644 --- a/mdast_util_to_markdown/src/handle/image.rs +++ b/mdast_util_to_markdown/src/handle/image.rs @@ -1,16 +1,19 @@ -use alloc::string::String; -use markdown::{ - mdast::{Image, Node}, - message::Message, -}; +//! JS equivalent: https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/handle/image.js +use super::Handle; use crate::{ construct_name::ConstructName, state::{Info, State}, - util::{check_quote::check_quote, safe::SafeConfig}, + util::{ + check_quote::check_quote, contains_control_or_whitespace::contains_control_or_whitespace, + safe::SafeConfig, + }, +}; +use alloc::string::String; +use markdown::{ + mdast::{Image, Node}, + message::Message, }; - -use super::Handle; impl Handle for Image { fn handle( @@ -33,8 +36,7 @@ impl Handle for Image { value.push_str("]("); state.exit(); - if self.url.is_empty() && self.title.is_some() - || contain_control_char_or_whitespace(&self.url) + if self.url.is_empty() && self.title.is_some() || contains_control_or_whitespace(&self.url) { state.enter(ConstructName::DestinationLiteral); value.push('<'); @@ -74,10 +76,6 @@ impl Handle for Image { } } -fn contain_control_char_or_whitespace(value: &str) -> bool { - value.chars().any(|c| c.is_whitespace() || c.is_control()) -} - pub fn peek_image() -> char { '!' } diff --git a/mdast_util_to_markdown/src/handle/image_reference.rs b/mdast_util_to_markdown/src/handle/image_reference.rs index 6de5d0c..02d205c 100644 --- a/mdast_util_to_markdown/src/handle/image_reference.rs +++ b/mdast_util_to_markdown/src/handle/image_reference.rs @@ -1,18 +1,17 @@ -use core::mem; - -use alloc::string::String; -use markdown::{ - mdast::{ImageReference, Node, ReferenceKind}, - message::Message, -}; +//! JS equivalent: https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/handle/image-reference.js +use super::Handle; use crate::{ construct_name::ConstructName, state::{Info, State}, util::safe::SafeConfig, }; - -use super::Handle; +use alloc::string::String; +use core::mem; +use markdown::{ + mdast::{ImageReference, Node, ReferenceKind}, + message::Message, +}; impl Handle for ImageReference { fn handle( diff --git a/mdast_util_to_markdown/src/handle/inline_code.rs b/mdast_util_to_markdown/src/handle/inline_code.rs index 3631e1a..ccd2439 100644 --- a/mdast_util_to_markdown/src/handle/inline_code.rs +++ b/mdast_util_to_markdown/src/handle/inline_code.rs @@ -1,3 +1,7 @@ +//! JS equivalent: https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/handle/inline-code.js + +use super::Handle; +use crate::state::{Info, State}; use alloc::{format, string::String}; use markdown::{ mdast::{InlineCode, Node}, @@ -5,10 +9,6 @@ use markdown::{ }; use regex::Regex; -use crate::state::{Info, State}; - -use super::Handle; - impl Handle for InlineCode { fn handle( &self, @@ -30,6 +30,7 @@ impl Handle for InlineCode { let ends_with_whitespace = value.ends_with(char::is_whitespace); let starts_with_tick = value.starts_with('`'); let ends_with_tick = value.ends_with('`'); + if no_whitespaces && ((starts_with_whitespace && ends_with_whitespace) || starts_with_tick diff --git a/mdast_util_to_markdown/src/handle/link.rs b/mdast_util_to_markdown/src/handle/link.rs index bd576b9..6cb24c3 100644 --- a/mdast_util_to_markdown/src/handle/link.rs +++ b/mdast_util_to_markdown/src/handle/link.rs @@ -1,21 +1,20 @@ -use core::mem; - -use alloc::string::String; -use markdown::{ - mdast::{Link, Node}, - message::Message, -}; +//! JS equivalent: https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/handle/link.js +use super::Handle; use crate::{ construct_name::ConstructName, state::{Info, State}, util::{ - check_quote::check_quote, format_link_as_auto_link::format_link_as_auto_link, - safe::SafeConfig, + check_quote::check_quote, contains_control_or_whitespace::contains_control_or_whitespace, + format_link_as_auto_link::format_link_as_auto_link, safe::SafeConfig, }, }; - -use super::Handle; +use alloc::string::String; +use core::mem; +use markdown::{ + mdast::{Link, Node}, + message::Message, +}; impl Handle for Link { fn handle( @@ -45,8 +44,7 @@ impl Handle for Link { value.push_str("]("); state.exit(); - if self.url.is_empty() && self.title.is_some() - || contain_control_char_or_whitespace(&self.url) + if self.url.is_empty() && self.title.is_some() || contains_control_or_whitespace(&self.url) { state.enter(ConstructName::DestinationLiteral); value.push('<'); @@ -86,10 +84,6 @@ impl Handle for Link { } } -fn contain_control_char_or_whitespace(value: &str) -> bool { - value.chars().any(|c| c.is_whitespace() || c.is_control()) -} - pub fn peek_link(link: &Link, node: &Node, state: &State) -> char { if format_link_as_auto_link(link, node, state) { '>' diff --git a/mdast_util_to_markdown/src/handle/link_reference.rs b/mdast_util_to_markdown/src/handle/link_reference.rs index becc53b..2adda03 100644 --- a/mdast_util_to_markdown/src/handle/link_reference.rs +++ b/mdast_util_to_markdown/src/handle/link_reference.rs @@ -1,18 +1,17 @@ -use core::mem; - -use alloc::string::String; -use markdown::{ - mdast::{LinkReference, Node, ReferenceKind}, - message::Message, -}; +//! JS equivalent: https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/handle/link-reference.js +use super::Handle; use crate::{ construct_name::ConstructName, state::{Info, State}, util::safe::SafeConfig, }; - -use super::Handle; +use alloc::string::String; +use core::mem; +use markdown::{ + mdast::{LinkReference, Node, ReferenceKind}, + message::Message, +}; impl Handle for LinkReference { fn handle( diff --git a/mdast_util_to_markdown/src/handle/list.rs b/mdast_util_to_markdown/src/handle/list.rs index ffe420c..ee3ad3a 100644 --- a/mdast_util_to_markdown/src/handle/list.rs +++ b/mdast_util_to_markdown/src/handle/list.rs @@ -1,8 +1,6 @@ -use markdown::{ - mdast::{List, Node}, - message::Message, -}; +//! JS equivalent: https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/handle/list.js +use super::Handle; use crate::{ construct_name::ConstructName, state::{Info, State}, @@ -11,8 +9,10 @@ use crate::{ check_bullet_other::check_bullet_other, check_rule::check_rule, }, }; - -use super::Handle; +use markdown::{ + mdast::{List, Node}, + message::Message, +}; impl Handle for List { fn handle( diff --git a/mdast_util_to_markdown/src/handle/list_item.rs b/mdast_util_to_markdown/src/handle/list_item.rs index 9bbb3d1..67dafcd 100644 --- a/mdast_util_to_markdown/src/handle/list_item.rs +++ b/mdast_util_to_markdown/src/handle/list_item.rs @@ -1,3 +1,12 @@ +//! JS equivalent: https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/handle/list-item.js + +use super::Handle; +use crate::{ + configure::IndentOptions, + construct_name::ConstructName, + state::{Info, State}, + util::check_bullet::check_bullet, +}; use alloc::{ format, string::{String, ToString}, @@ -7,15 +16,6 @@ use markdown::{ message::Message, }; -use crate::{ - configure::IndentOptions, - construct_name::ConstructName, - state::{Info, State}, - util::check_bullet::check_bullet, -}; - -use super::Handle; - impl Handle for ListItem { fn handle( &self, @@ -51,7 +51,6 @@ impl Handle for ListItem { let mut size = bullet.len() + 1; let should_compute_size = match list_item_indent { - IndentOptions::Tab => true, IndentOptions::Mixed => { if let Some(Node::List(list)) = parent { list.spread || self.spread @@ -59,6 +58,7 @@ impl Handle for ListItem { self.spread } } + IndentOptions::Tab => true, _ => false, }; diff --git a/mdast_util_to_markdown/src/handle/paragraph.rs b/mdast_util_to_markdown/src/handle/paragraph.rs index 47a0966..429e6ca 100644 --- a/mdast_util_to_markdown/src/handle/paragraph.rs +++ b/mdast_util_to_markdown/src/handle/paragraph.rs @@ -1,14 +1,14 @@ -use markdown::{ - mdast::{Node, Paragraph}, - message::Message, -}; +//! JS equivalent: https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/handle/paragraph.js +use super::Handle; use crate::{ construct_name::ConstructName, state::{Info, State}, }; - -use super::Handle; +use markdown::{ + mdast::{Node, Paragraph}, + message::Message, +}; impl Handle for Paragraph { fn handle( @@ -19,12 +19,9 @@ impl Handle for Paragraph { node: &Node, ) -> Result { state.enter(ConstructName::Paragraph); - state.enter(ConstructName::Phrasing); let value = state.container_phrasing(node, info)?; - // exit phrasing state.exit(); - // exit paragarph state.exit(); Ok(value) } diff --git a/mdast_util_to_markdown/src/handle/root.rs b/mdast_util_to_markdown/src/handle/root.rs index 4addfaf..bcd8464 100644 --- a/mdast_util_to_markdown/src/handle/root.rs +++ b/mdast_util_to_markdown/src/handle/root.rs @@ -1,13 +1,13 @@ +//! JS equivalent: https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/handle/root.js + +use super::Handle; +use crate::state::{Info, State}; use alloc::string::String; use markdown::{ mdast::{Node, Root}, message::Message, }; -use crate::state::{Info, State}; - -use super::Handle; - impl Handle for Root { fn handle( &self, @@ -17,6 +17,7 @@ impl Handle for Root { node: &Node, ) -> Result { let has_phrasing = self.children.iter().any(phrasing); + if has_phrasing { state.container_phrasing(node, info) } else { @@ -25,16 +26,18 @@ impl Handle for Root { } } +// JS: . fn phrasing(child: &Node) -> bool { + // Note: `html` nodes are ambiguous. matches!( *child, Node::Break(_) | Node::Emphasis(_) - | Node::Image(_) | Node::ImageReference(_) + | Node::Image(_) | Node::InlineCode(_) - | Node::Link(_) | Node::LinkReference(_) + | Node::Link(_) | Node::Strong(_) | Node::Text(_) ) diff --git a/mdast_util_to_markdown/src/handle/strong.rs b/mdast_util_to_markdown/src/handle/strong.rs index c765715..b4f503b 100644 --- a/mdast_util_to_markdown/src/handle/strong.rs +++ b/mdast_util_to_markdown/src/handle/strong.rs @@ -1,16 +1,16 @@ -use alloc::format; -use markdown::{ - mdast::{Node, Strong}, - message::Message, -}; +//! JS equivalent: https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/handle/strong.js +use super::Handle; use crate::{ construct_name::ConstructName, state::{Info, State}, util::check_strong::check_strong, }; - -use super::Handle; +use alloc::format; +use markdown::{ + mdast::{Node, Strong}, + message::Message, +}; impl Handle for Strong { fn handle( diff --git a/mdast_util_to_markdown/src/handle/text.rs b/mdast_util_to_markdown/src/handle/text.rs index 61b04fb..b3b116b 100644 --- a/mdast_util_to_markdown/src/handle/text.rs +++ b/mdast_util_to_markdown/src/handle/text.rs @@ -1,14 +1,14 @@ -use markdown::{ - mdast::{Node, Text}, - message::Message, -}; +//! JS equivalent: https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/handle/text.js +use super::Handle; use crate::{ state::{Info, State}, util::safe::SafeConfig, }; - -use super::Handle; +use markdown::{ + mdast::{Node, Text}, + message::Message, +}; impl Handle for Text { fn handle( diff --git a/mdast_util_to_markdown/src/handle/thematic_break.rs b/mdast_util_to_markdown/src/handle/thematic_break.rs index a0f1c4e..98da006 100644 --- a/mdast_util_to_markdown/src/handle/thematic_break.rs +++ b/mdast_util_to_markdown/src/handle/thematic_break.rs @@ -1,15 +1,15 @@ -use alloc::format; -use markdown::{ - mdast::{Node, ThematicBreak}, - message::Message, -}; +//! JS equivalent: https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/handle/thematic-break.js +use super::Handle; use crate::{ state::{Info, State}, util::{check_rule::check_rule, check_rule_repetition::check_rule_repetition}, }; - -use super::Handle; +use alloc::format; +use markdown::{ + mdast::{Node, ThematicBreak}, + message::Message, +}; impl Handle for ThematicBreak { fn handle( @@ -25,7 +25,8 @@ impl Handle for ThematicBreak { format!("{}{}", marker, space).repeat(check_rule_repetition(state)? as usize); if state.options.rule_spaces { - value.pop(); // remove the last space + // Remove the last space. + value.pop(); Ok(value) } else { Ok(value) diff --git a/mdast_util_to_markdown/src/lib.rs b/mdast_util_to_markdown/src/lib.rs index 7be88a4..8ce47b5 100644 --- a/mdast_util_to_markdown/src/lib.rs +++ b/mdast_util_to_markdown/src/lib.rs @@ -1,3 +1,7 @@ +//! API. +//! +//! JS equivalent: https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/index.js. + #![no_std] use alloc::string::String; @@ -14,18 +18,22 @@ mod state; mod r#unsafe; mod util; +/// Turn an mdast syntax tree into markdown. pub fn to_markdown(tree: &Node) -> Result { to_markdown_with_options(tree, &Options::default()) } +/// Turn an mdast syntax tree, with options, into markdown. pub fn to_markdown_with_options(tree: &Node, options: &Options) -> Result { let mut state = State::new(options); let mut result = state.handle(tree, &Info::new("\n", "\n"), None)?; + if !result.is_empty() { let last_char = result.chars().last().unwrap(); if last_char != '\n' && last_char != '\r' { result.push('\n'); } } + Ok(result) } diff --git a/mdast_util_to_markdown/src/state.rs b/mdast_util_to_markdown/src/state.rs index 4a08d72..8d00b67 100644 --- a/mdast_util_to_markdown/src/state.rs +++ b/mdast_util_to_markdown/src/state.rs @@ -1,16 +1,15 @@ -use crate::association::Association; -use crate::construct_name::ConstructName; -use crate::handle::emphasis::peek_emphasis; -use crate::handle::html::peek_html; -use crate::handle::image::peek_image; -use crate::handle::image_reference::peek_image_reference; -use crate::handle::inline_code::peek_inline_code; -use crate::handle::link::peek_link; -use crate::handle::link_reference::peek_link_reference; -use crate::handle::strong::peek_strong; -use crate::handle::Handle; -use crate::Options; +//! State. +//! +//! JS equivalent: https://github.com/syntax-tree/mdast-util-to-markdown/blob/fd6a508/lib/types.js#L195. + use crate::{ + association::Association, + construct_name::ConstructName, + handle::{ + emphasis::peek_emphasis, html::peek_html, image::peek_image, + image_reference::peek_image_reference, inline_code::peek_inline_code, link::peek_link, + link_reference::peek_link_reference, strong::peek_strong, Handle, + }, r#unsafe::Unsafe, util::{ format_code_as_indented::format_code_as_indented, @@ -18,209 +17,77 @@ use crate::{ pattern_in_scope::pattern_in_scope, safe::{escape_backslashes, EscapeInfos, SafeConfig}, }, + Options, }; -use alloc::boxed::Box; -use alloc::string::ToString; -use alloc::{collections::BTreeMap, format, string::String, vec::Vec}; -use markdown::mdast::Node; -use markdown::message::Message; +use alloc::{ + boxed::Box, + collections::BTreeMap, + format, + string::{String, ToString}, + vec::Vec, +}; +use markdown::{mdast::Node, message::Message}; use regex::{Captures, Regex, RegexBuilder}; +pub struct Info<'a> { + pub after: &'a str, + pub before: &'a str, +} + #[derive(Debug)] +/// Different ways to join two (container, flow) nodes. enum Join { + /// Join the two nodes with `1` blank line. Break, - HTMLComment, + /// Join the two nodes with an HTML comment. + HtmlComment, + /// Join the two nodes with `d` blank lines. Lines(usize), } pub struct State<'a> { - pub stack: Vec, - pub index_stack: Vec, - pub bullet_last_used: Option, pub bullet_current: Option, - pub r#unsafe: Vec>, + pub bullet_last_used: Option, + pub index_stack: Vec, pub options: &'a Options, -} - -pub struct Info<'a> { - pub before: &'a str, - pub after: &'a str, + pub stack: Vec, + pub r#unsafe: Vec>, } impl<'a> Info<'a> { pub fn new(before: &'a str, after: &'a str) -> Self { - Info { before, after } + Info { after, before } } } impl<'a> State<'a> { - pub fn new(options: &'a Options) -> Self { - State { - stack: Vec::new(), - index_stack: Vec::new(), - bullet_last_used: None, - bullet_current: None, - r#unsafe: Unsafe::get_default_unsafe(), - options, + /// JS equivalent: . + pub fn association(&self, node: &impl Association) -> String { + if node.label().is_some() || node.identifier().is_empty() { + return node.label().clone().unwrap_or_default(); } - } - - pub fn enter(&mut self, name: ConstructName) { - self.stack.push(name); - } - pub fn exit(&mut self) { - self.stack.pop(); - } + let character_escape_or_reference = + RegexBuilder::new(r"\\([!-/:-@\[-`{-~])|&(#(?:\d{1,7}|x[\da-f]{1,6})|[\da-z]{1,31});") + .case_insensitive(true) + .build() + .unwrap(); - pub fn handle( - &mut self, - node: &Node, - info: &Info, - parent: Option<&Node>, - ) -> Result { - match node { - Node::Root(root) => root.handle(self, info, parent, node), - Node::Paragraph(paragraph) => paragraph.handle(self, info, parent, node), - Node::Text(text) => text.handle(self, info, parent, node), - Node::Strong(strong) => strong.handle(self, info, parent, node), - Node::Emphasis(emphasis) => emphasis.handle(self, info, parent, node), - Node::Heading(heading) => heading.handle(self, info, parent, node), - Node::Break(r#break) => r#break.handle(self, info, parent, node), - Node::Html(html) => html.handle(self, info, parent, node), - Node::ThematicBreak(thematic_break) => thematic_break.handle(self, info, parent, node), - Node::Code(code) => code.handle(self, info, parent, node), - Node::Blockquote(block_quote) => block_quote.handle(self, info, parent, node), - Node::List(list) => list.handle(self, info, parent, node), - Node::ListItem(list_item) => list_item.handle(self, info, parent, node), - Node::Image(image) => image.handle(self, info, parent, node), - Node::Link(link) => link.handle(self, info, parent, node), - Node::InlineCode(inline_code) => inline_code.handle(self, info, parent, node), - Node::Definition(definition) => definition.handle(self, info, parent, node), - Node::ImageReference(image_reference) => { - image_reference.handle(self, info, parent, node) - } - Node::LinkReference(link_reference) => link_reference.handle(self, info, parent, node), - _ => Err(Message { - reason: format!("Unexpected node type {:?}", node), - rule_id: Box::new("unexpected-node".into()), - source: Box::new("mdast-util-to-markdown".into()), - place: None, - }), - } + character_escape_or_reference + .replace_all(node.identifier(), Self::decode) + .into_owned() } - pub fn safe(&mut self, input: &str, config: &SafeConfig) -> String { - let value = format!("{}{}{}", config.before, input, config.after); - let mut positions: Vec = Vec::new(); - let mut result: String = String::new(); - let mut infos: BTreeMap = BTreeMap::new(); - - for pattern in &mut self.r#unsafe { - if !pattern_in_scope(&self.stack, pattern) { - continue; - } - - Self::compile_pattern(pattern); - - if let Some(regex) = &pattern.compiled { - for m in regex.captures_iter(&value) { - let full_match = m.get(0).expect("Guaranteed to have a match"); - let captured_group_len = m - .get(1) - .map(|captured_group| captured_group.len()) - .unwrap_or(0); - let before = pattern.before.is_some() || pattern.at_break; - let after = pattern.after.is_some(); - let position = full_match.start() + if before { captured_group_len } else { 0 }; - - if positions.contains(&position) { - if let Some(entry) = infos.get_mut(&position) { - if entry.before && !before { - entry.before = false; - } - if entry.after && !after { - entry.after = false; - } - } - } else { - infos.insert(position, EscapeInfos { before, after }); - positions.push(position); - } - } - } - } - - positions.sort_unstable(); - - let mut start = config.before.len(); - let end = value.len() - config.after.len(); - for (index, position) in positions.iter().enumerate() { - if *position < start || *position >= end { - continue; - } - - // If this character is supposed to be escaped because it has a condition on - // the next character, and the next character is definitly being escaped, - // then skip this escape. - // This will never panic because the bounds are properly checked, and we - // guarantee that the positions are already keys in the `infos` map before this - // point in execution. - if index + 1 < positions.len() - && position + 1 < end - && positions[index + 1] == position + 1 - && infos[position].after - && !infos[&(position + 1)].before - && !infos[&(position + 1)].after - || index > 0 - && positions[index - 1] == position - 1 - && infos[position].before - && !infos[&(position - 1)].before - && !infos[&(position - 1)].after - { - continue; - } - - if start != *position { - result.push_str(&escape_backslashes(&value[start..*position], r"\")); - } - start = *position; - - let char_at_pos = value.chars().nth(*position); - match char_at_pos { - Some('!'..='/') | Some(':'..='@') | Some('['..='`') | Some('{'..='~') => { - if let Some(encode) = &config.encode { - let character = char_at_pos.expect("To be a valid char"); - if *encode != character { - result.push('\\'); - } else { - let encoded_char = Self::encode_char(character); - result.push_str(&encoded_char); - start += character.len_utf8(); - } - } else { - result.push('\\'); - } - } - Some(character) => { - let encoded_char = Self::encode_char(character); - result.push_str(&encoded_char); - start += character.len_utf8(); - } - _ => (), - }; + /// JS equivalent: . + fn between(&self, left: &Node, right: &Node, parent: &Node, results: &mut String) { + if self.options.tight_definitions { + Self::set_between(&self.tight_definition(left, right), results) + } else { + Self::set_between(&self.join_defaults(left, right, parent), results) } - - result.push_str(&escape_backslashes(&value[start..end], config.after)); - - result - } - - fn encode_char(character: char) -> String { - let hex_code = u32::from(character); - format!("&#x{:X};", hex_code) } + /// JS equivalent: . pub fn compile_pattern(pattern: &mut Unsafe) { if pattern.compiled.is_none() { let mut pattern_to_compile = String::new(); @@ -274,6 +141,48 @@ impl<'a> State<'a> { } } + /// JS equivalent: . + pub fn container_flow(&mut self, parent: &Node) -> Result { + let children = parent.children().expect("The node to be a flow parent."); + + if children.is_empty() { + return Ok(String::new()); + } + + let mut results: String = String::new(); + let mut children_iter = children.iter().peekable(); + let mut index = 0; + + self.index_stack.push(0); + + while let Some(child) = children_iter.next() { + if index > 0 { + let top = self + .index_stack + .last_mut() + .expect("The stack is populated with at least one child position"); + *top = index; + } + + if !matches!(child, Node::List(_)) { + self.bullet_last_used = None; + } + + results.push_str(&self.handle(child, &Info::new("\n", "\n"), Some(parent))?); + + if let Some(next_child) = children_iter.peek() { + self.between(child, next_child, parent, &mut results); + } + + index += 1; + } + + self.index_stack.pop(); + + Ok(results) + } + + /// JS equivalent: . pub fn container_phrasing(&mut self, parent: &Node, info: &Info) -> Result { let children = parent .children() @@ -313,6 +222,12 @@ impl<'a> State<'a> { } } + // In some cases, html (text) can be found in phrasing right after an eol. + // When we’d serialize that, in most cases that would be seen as html + // (flow). + // As we can’t escape or so to prevent it from happening, we take a somewhat + // reasonable approach: replace that eol with a space. + // See: if !results.is_empty() { if info.before == "\r" || info.before == "\n" && matches!(child, Node::Html(_)) { // TODO Remove this check here it might not be needed since we're @@ -339,105 +254,121 @@ impl<'a> State<'a> { Ok(results) } - fn peek_node(&self, node: &Node) -> Option { - match node { - Node::Strong(_) => Some(peek_strong(self)), - Node::Emphasis(_) => Some(peek_emphasis(self)), - Node::Html(_) => Some(peek_html()), - Node::Image(_) => Some(peek_image()), - Node::Link(link) => Some(peek_link(link, node, self)), - Node::InlineCode(_) => Some(peek_inline_code()), - Node::ImageReference(_) => Some(peek_image_reference()), - Node::LinkReference(_) => Some(peek_link_reference()), - _ => None, - } - } - - pub fn container_flow(&mut self, parent: &Node) -> Result { - let children = parent.children().expect("The node to be a flow parent."); - - if children.is_empty() { - return Ok(String::new()); + /// JS equvialent: . + fn decode(caps: &Captures) -> String { + if let Some(first_cap) = caps.get(1) { + return String::from(first_cap.as_str()); } - let mut results: String = String::new(); - let mut children_iter = children.iter().peekable(); - let mut index = 0; - - self.index_stack.push(0); - - while let Some(child) = children_iter.next() { - if index > 0 { - let top = self - .index_stack - .last_mut() - .expect("The stack is populated with at least one child position"); - *top = index; - } - - if !matches!(child, Node::List(_)) { - self.bullet_last_used = None; - } - - results.push_str(&self.handle(child, &Info::new("\n", "\n"), Some(parent))?); - - if let Some(next_child) = children_iter.peek() { - self.between(child, next_child, parent, &mut results); + if let Some(head) = &caps[2].chars().nth(0) { + if *head == '#' { + let radix = match caps[2].chars().nth(1) { + Some('x') | Some('X') => 16, + _ => 10, + }; + let capture = &caps[2]; + let numeric_encoded = if radix == 16 { + &capture[2..] + } else { + &capture[1..] + }; + return markdown::decode_numeric(numeric_encoded, radix); } - - index += 1; } - self.index_stack.pop(); + markdown::decode_named(&caps[2], true).unwrap_or(caps[0].to_string()) + } - Ok(results) + /// No real JS equivalent, it’s written inline. + fn encode_char(character: char) -> String { + let hex_code = u32::from(character); + format!("&#x{:X};", hex_code) } - fn between(&self, left: &Node, right: &Node, parent: &Node, results: &mut String) { - if self.options.tight_definitions { - Self::set_between(&self.tight_definition(left, right), results) - } else { - Self::set_between(&self.join_defaults(left, right, parent), results) - } + pub fn enter(&mut self, name: ConstructName) { + self.stack.push(name); } - fn set_between(join: &Join, results: &mut String) { - if let Join::Break = join { - results.push_str("\n\n"); - } else if let Join::Lines(n) = join { - if *n == 1 { - results.push_str("\n\n"); - return; + pub fn exit(&mut self) { + self.stack.pop(); + } + + /// No JS equivalent. + pub fn handle( + &mut self, + node: &Node, + info: &Info, + parent: Option<&Node>, + ) -> Result { + match node { + Node::Break(r#break) => r#break.handle(self, info, parent, node), + Node::Blockquote(block_quote) => block_quote.handle(self, info, parent, node), + Node::Code(code) => code.handle(self, info, parent, node), + Node::Definition(definition) => definition.handle(self, info, parent, node), + Node::Emphasis(emphasis) => emphasis.handle(self, info, parent, node), + Node::Heading(heading) => heading.handle(self, info, parent, node), + Node::Html(html) => html.handle(self, info, parent, node), + Node::ImageReference(image_reference) => { + image_reference.handle(self, info, parent, node) } - results.push_str("\n".repeat(1 + n).as_ref()); - } else if let Join::HTMLComment = join { - results.push_str("\n\n\n\n"); + Node::Image(image) => image.handle(self, info, parent, node), + Node::InlineCode(inline_code) => inline_code.handle(self, info, parent, node), + Node::LinkReference(link_reference) => link_reference.handle(self, info, parent, node), + Node::Link(link) => link.handle(self, info, parent, node), + Node::ListItem(list_item) => list_item.handle(self, info, parent, node), + Node::List(list) => list.handle(self, info, parent, node), + Node::Paragraph(paragraph) => paragraph.handle(self, info, parent, node), + Node::Root(root) => root.handle(self, info, parent, node), + Node::Strong(strong) => strong.handle(self, info, parent, node), + Node::Text(text) => text.handle(self, info, parent, node), + Node::ThematicBreak(thematic_break) => thematic_break.handle(self, info, parent, node), + _ => Err(Message { + place: None, + reason: format!("Unexpected node type `{:?}`", node), + rule_id: Box::new("unexpected-node".into()), + source: Box::new("mdast-util-to-markdown".into()), + }), } } - fn tight_definition(&self, left: &Node, right: &Node) -> Join { - if matches!(left, Node::Definition(_)) && Self::matches((left, right)) { - return Join::Lines(0); + /// JS equivalent: . + pub fn indent_lines(&self, value: &str, map: impl Fn(&str, usize, bool) -> String) -> String { + let mut result = String::new(); + let mut start = 0; + let mut line = 0; + let eol = Regex::new(r"\r?\n|\r").unwrap(); + + for m in eol.captures_iter(value) { + let full_match = m.get(0).unwrap(); + let value_slice = &value[start..full_match.start()]; + result.push_str(&map(value_slice, line, value_slice.is_empty())); + result.push_str(full_match.as_str()); + start = full_match.start() + full_match.len(); + line += 1; } - Join::Break + + result.push_str(&map(&value[start..], line, value.is_empty())); + result } + /// No real JS equivalent, but see: + /// . fn join_defaults(&self, left: &Node, right: &Node, parent: &Node) -> Join { if let Node::Code(code) = right { if format_code_as_indented(code, self) && matches!(left, Node::List(_)) { - return Join::HTMLComment; + return Join::HtmlComment; } if let Node::Code(code) = left { if format_code_as_indented(code, self) { - return Join::HTMLComment; + return Join::HtmlComment; } } } - if matches!(parent, Node::List(_) | Node::ListItem(_)) { + if matches!(parent, Node::ListItem(_) | Node::List(_)) { if matches!(left, Node::Paragraph(_)) { - if Self::matches((left, right)) { + if matches!(right, Node::Paragraph(_)) { return Join::Break; } @@ -470,75 +401,164 @@ impl<'a> State<'a> { Join::Break } - fn matches(nodes: (&Node, &Node)) -> bool { - matches!( - nodes, - (Node::Root(_), Node::Root(_)) - | (Node::Blockquote(_), Node::Blockquote(_)) - | (Node::Definition(_), Node::Definition(_)) - | (Node::FootnoteDefinition(_), Node::FootnoteDefinition(_)) - | (Node::Heading(_), Node::Heading(_)) - | (Node::List(_), Node::List(_)) - | (Node::ListItem(_), Node::ListItem(_)) - | (Node::Paragraph(_), Node::Paragraph(_)) - | (Node::Table(_), Node::Table(_)) - ) + /// No JS equivalent. + pub fn new(options: &'a Options) -> Self { + State { + bullet_current: None, + bullet_last_used: None, + index_stack: Vec::new(), + options, + stack: Vec::new(), + r#unsafe: Unsafe::get_default_unsafe(), + } } - pub fn indent_lines(&self, value: &str, map: impl Fn(&str, usize, bool) -> String) -> String { - let mut result = String::new(); - let mut start = 0; - let mut line = 0; - let eol = Regex::new(r"\r?\n|\r").unwrap(); - for m in eol.captures_iter(value) { - let full_match = m.get(0).unwrap(); - let value_slice = &value[start..full_match.start()]; - result.push_str(&map(value_slice, line, value_slice.is_empty())); - result.push_str(full_match.as_str()); - start = full_match.start() + full_match.len(); - line += 1; + /// No JS equivalent. + fn peek_node(&self, node: &Node) -> Option { + match node { + Node::Emphasis(_) => Some(peek_emphasis(self)), + Node::Html(_) => Some(peek_html()), + Node::ImageReference(_) => Some(peek_image_reference()), + Node::Image(_) => Some(peek_image()), + Node::InlineCode(_) => Some(peek_inline_code()), + Node::LinkReference(_) => Some(peek_link_reference()), + Node::Link(link) => Some(peek_link(link, node, self)), + Node::Strong(_) => Some(peek_strong(self)), + _ => None, } - result.push_str(&map(&value[start..], line, value.is_empty())); - result } - pub fn association(&self, node: &impl Association) -> String { - if node.label().is_some() || node.identifier().is_empty() { - return node.label().clone().unwrap_or_default(); + /// JS equivalent: . + pub fn safe(&mut self, input: &str, config: &SafeConfig) -> String { + let value = format!("{}{}{}", config.before, input, config.after); + let mut positions: Vec = Vec::new(); + let mut result: String = String::new(); + let mut infos: BTreeMap = BTreeMap::new(); + + for pattern in &mut self.r#unsafe { + if !pattern_in_scope(&self.stack, pattern) { + continue; + } + + Self::compile_pattern(pattern); + + if let Some(regex) = &pattern.compiled { + for m in regex.captures_iter(&value) { + let full_match = m.get(0).expect("Guaranteed to have a match"); + let captured_group_len = m + .get(1) + .map(|captured_group| captured_group.len()) + .unwrap_or(0); + let before = pattern.before.is_some() || pattern.at_break; + let after = pattern.after.is_some(); + let position = full_match.start() + if before { captured_group_len } else { 0 }; + + if positions.contains(&position) { + if let Some(entry) = infos.get_mut(&position) { + if entry.before && !before { + entry.before = false; + } + if entry.after && !after { + entry.after = false; + } + } + } else { + infos.insert(position, EscapeInfos { after, before }); + positions.push(position); + } + } + } } - let character_escape_or_reference = - RegexBuilder::new(r"\\([!-/:-@\[-`{-~])|&(#(?:\d{1,7}|x[\da-f]{1,6})|[\da-z]{1,31});") - .case_insensitive(true) - .build() - .unwrap(); + positions.sort_unstable(); - character_escape_or_reference - .replace_all(node.identifier(), Self::decode) - .into_owned() - } + let mut start = config.before.len(); + let end = value.len() - config.after.len(); - fn decode(caps: &Captures) -> String { - if let Some(first_cap) = caps.get(1) { - return String::from(first_cap.as_str()); + for (index, position) in positions.iter().enumerate() { + if *position < start || *position >= end { + continue; + } + + // If this character is supposed to be escaped because it has a condition on + // the next character, and the next character is definitly being escaped, + // then skip this escape. + // This will never panic because the bounds are properly checked, and we + // guarantee that the positions are already keys in the `infos` map before this + // point in execution. + if index + 1 < positions.len() + && position + 1 < end + && positions[index + 1] == position + 1 + && infos[position].after + && !infos[&(position + 1)].before + && !infos[&(position + 1)].after + || index > 0 + && positions[index - 1] == position - 1 + && infos[position].before + && !infos[&(position - 1)].before + && !infos[&(position - 1)].after + { + continue; + } + + if start != *position { + result.push_str(&escape_backslashes(&value[start..*position], r"\")); + } + start = *position; + + let char_at_pos = value.chars().nth(*position); + match char_at_pos { + Some('!'..='/') | Some(':'..='@') | Some('['..='`') | Some('{'..='~') => { + if let Some(encode) = &config.encode { + let character = char_at_pos.expect("To be a valid char"); + if *encode != character { + result.push('\\'); + } else { + let encoded_char = Self::encode_char(character); + result.push_str(&encoded_char); + start += character.len_utf8(); + } + } else { + result.push('\\'); + } + } + Some(character) => { + let encoded_char = Self::encode_char(character); + result.push_str(&encoded_char); + start += character.len_utf8(); + } + _ => (), + }; } - if let Some(head) = &caps[2].chars().nth(0) { - if *head == '#' { - let radix = match caps[2].chars().nth(1) { - Some('x') | Some('X') => 16, - _ => 10, - }; - let capture = &caps[2]; - let numeric_encoded = if radix == 16 { - &capture[2..] - } else { - &capture[1..] - }; - return markdown::decode_numeric(numeric_encoded, radix); + result.push_str(&escape_backslashes(&value[start..end], config.after)); + + result + } + + /// No real JS equivalent, but see: + /// . + fn set_between(join: &Join, results: &mut String) { + if let Join::Break = join { + results.push_str("\n\n"); + } else if let Join::Lines(n) = join { + if *n == 1 { + results.push_str("\n\n"); + return; } + results.push_str("\n".repeat(1 + n).as_ref()); + } else if let Join::HtmlComment = join { + results.push_str("\n\n\n\n"); } + } - markdown::decode_named(&caps[2], true).unwrap_or(caps[0].to_string()) + /// No real JS equivalent, but see: + /// . + fn tight_definition(&self, left: &Node, right: &Node) -> Join { + if matches!(left, Node::Definition(_)) && matches!(right, Node::Definition(_)) { + return Join::Lines(0); + } + + Join::Break } } diff --git a/mdast_util_to_markdown/src/unsafe.rs b/mdast_util_to_markdown/src/unsafe.rs index 08c7613..3d4d658 100644 --- a/mdast_util_to_markdown/src/unsafe.rs +++ b/mdast_util_to_markdown/src/unsafe.rs @@ -1,23 +1,21 @@ -use alloc::{vec, vec::Vec}; -use regex::Regex; +//! Unsafe patterns. +//! +//! JS equivalent: . +//! Also: . use crate::construct_name::ConstructName; +use alloc::{vec, vec::Vec}; +use regex::Regex; #[derive(Default)] pub struct Unsafe<'a> { - pub character: char, - pub in_construct: Option, - pub not_in_construct: Option, - pub before: Option<&'a str>, pub after: Option<&'a str>, pub at_break: bool, + pub before: Option<&'a str>, + pub character: char, pub(crate) compiled: Option, -} - -// This could use a better name. -pub enum Construct { - List(Vec), - Single(ConstructName), + pub in_construct: Vec, + pub not_in_construct: Vec, } impl<'a> Unsafe<'a> { @@ -25,18 +23,18 @@ impl<'a> Unsafe<'a> { character: char, before: Option<&'a str>, after: Option<&'a str>, - in_construct: Option, - not_in_construct: Option, + in_construct: Vec, + not_in_construct: Vec, at_break: bool, ) -> Self { Unsafe { - character, - in_construct, - not_in_construct, - before, after, at_break, + before, + character, compiled: None, + in_construct, + not_in_construct, } } @@ -46,8 +44,8 @@ impl<'a> Unsafe<'a> { ConstructName::DestinationLiteral, ConstructName::DestinationRaw, ConstructName::Reference, - ConstructName::TitleQuote, ConstructName::TitleApostrophe, + ConstructName::TitleQuote, ]; vec![ @@ -55,266 +53,261 @@ impl<'a> Unsafe<'a> { '\t', None, "[\\r\\n]".into(), - Construct::Single(ConstructName::Phrasing).into(), - None, + vec![ConstructName::Phrasing], + vec![], false, ), Self::new( '\t', "[\\r\\n]".into(), None, - Construct::Single(ConstructName::Phrasing).into(), - None, + vec![ConstructName::Phrasing], + vec![], false, ), Self::new( '\t', None, None, - Construct::List(vec![ + vec![ ConstructName::CodeFencedLangGraveAccent, ConstructName::CodeFencedLangTilde, - ]) - .into(), - None, + ], + vec![], false, ), Self::new( '\r', None, None, - Construct::List(vec![ + vec![ ConstructName::CodeFencedLangGraveAccent, ConstructName::CodeFencedLangTilde, ConstructName::CodeFencedMetaGraveAccent, ConstructName::CodeFencedMetaTilde, ConstructName::DestinationLiteral, ConstructName::HeadingAtx, - ]) - .into(), - None, + ], + vec![], false, ), Self::new( '\n', None, None, - Construct::List(vec![ + vec![ ConstructName::CodeFencedLangGraveAccent, ConstructName::CodeFencedLangTilde, ConstructName::CodeFencedMetaGraveAccent, ConstructName::CodeFencedMetaTilde, ConstructName::DestinationLiteral, ConstructName::HeadingAtx, - ]) - .into(), - None, + ], + vec![], false, ), Self::new( ' ', None, "[\\r\\n]".into(), - Construct::Single(ConstructName::Phrasing).into(), - None, + vec![ConstructName::Phrasing], + vec![], false, ), Self::new( ' ', "[\\r\\n]".into(), None, - Construct::Single(ConstructName::Phrasing).into(), - None, + vec![ConstructName::Phrasing], + vec![], false, ), Self::new( ' ', None, None, - Construct::List(vec![ + vec![ ConstructName::CodeFencedLangGraveAccent, ConstructName::CodeFencedLangTilde, - ]) - .into(), - None, + ], + vec![], false, ), Self::new( '!', None, "\\[".into(), - Construct::Single(ConstructName::Phrasing).into(), - Construct::List(full_phrasing_spans.clone()).into(), + vec![ConstructName::Phrasing], + full_phrasing_spans.clone(), false, ), Self::new( '\"', None, None, - Construct::Single(ConstructName::TitleQuote).into(), - None, + vec![ConstructName::TitleQuote], + vec![], false, ), - Self::new('#', None, None, None, None, true), + Self::new('#', None, None, vec![], vec![], true), Self::new( '#', None, "(?:[\r\n]|$)".into(), - Construct::Single(ConstructName::HeadingAtx).into(), - None, + vec![ConstructName::HeadingAtx], + vec![], false, ), Self::new( '&', None, "[#A-Za-z]".into(), - Construct::Single(ConstructName::Phrasing).into(), - None, + vec![ConstructName::Phrasing], + vec![], false, ), Self::new( '\'', None, None, - Construct::Single(ConstructName::TitleApostrophe).into(), - None, + vec![ConstructName::TitleApostrophe], + vec![], false, ), Self::new( '(', None, None, - Construct::Single(ConstructName::DestinationRaw).into(), - None, + vec![ConstructName::DestinationRaw], + vec![], false, ), Self::new( '(', "\\]".into(), None, - Construct::Single(ConstructName::Phrasing).into(), - Construct::List(full_phrasing_spans.clone()).into(), + vec![ConstructName::Phrasing], + full_phrasing_spans.clone(), false, ), - Self::new(')', "\\d+".into(), None, None, None, true), + Self::new(')', "\\d+".into(), None, vec![], vec![], true), Self::new( ')', None, None, - Construct::Single(ConstructName::DestinationRaw).into(), - None, + vec![ConstructName::DestinationRaw], + vec![], false, ), - Self::new('*', None, "(?:[ \t\r\n*])".into(), None, None, true), + Self::new('*', None, "(?:[ \t\r\n*])".into(), vec![], vec![], true), Self::new( '*', None, None, - Construct::Single(ConstructName::Phrasing).into(), - Construct::List(full_phrasing_spans.clone()).into(), + vec![ConstructName::Phrasing], + full_phrasing_spans.clone(), false, ), - Self::new('+', None, "(?:[ \t\r\n])".into(), None, None, true), - Self::new('-', None, "(?:[ \t\r\n-])".into(), None, None, true), + Self::new('+', None, "(?:[ \t\r\n])".into(), vec![], vec![], true), + Self::new('-', None, "(?:[ \t\r\n-])".into(), vec![], vec![], true), Self::new( '.', "\\d+".into(), "(?:[ \t\r\n]|$)".into(), - None, - None, + vec![], + vec![], true, ), - Self::new('<', None, "[!/?A-Za-z]".into(), None, None, true), + Self::new('<', None, "[!/?A-Za-z]".into(), vec![], vec![], true), Self::new( '<', None, "[!/?A-Za-z]".into(), - Construct::Single(ConstructName::Phrasing).into(), - Construct::List(full_phrasing_spans.clone()).into(), + vec![ConstructName::Phrasing], + full_phrasing_spans.clone(), false, ), Self::new( '<', None, None, - Construct::Single(ConstructName::DestinationLiteral).into(), - None, + vec![ConstructName::DestinationLiteral], + vec![], false, ), - Self::new('=', None, None, None, None, true), - Self::new('>', None, None, None, None, true), + Self::new('=', None, None, vec![], vec![], true), + Self::new('>', None, None, vec![], vec![], true), Self::new( '>', None, None, - Construct::Single(ConstructName::DestinationLiteral).into(), - None, + vec![ConstructName::DestinationLiteral], + vec![], false, ), - Self::new('[', None, None, None, None, true), + Self::new('[', None, None, vec![], vec![], true), Self::new( '[', None, None, - Construct::Single(ConstructName::Phrasing).into(), - Construct::List(full_phrasing_spans.clone()).into(), + vec![ConstructName::Phrasing], + full_phrasing_spans.clone(), false, ), Self::new( '[', None, None, - Construct::List(vec![ConstructName::Label, ConstructName::Reference]).into(), - None, + vec![ConstructName::Label, ConstructName::Reference], + vec![], false, ), Self::new( '\\', None, "[\\r\\n]".into(), - Construct::Single(ConstructName::Phrasing).into(), - None, + vec![ConstructName::Phrasing], + vec![], false, ), Self::new( ']', None, None, - Construct::List(vec![ConstructName::Label, ConstructName::Reference]).into(), - None, + vec![ConstructName::Label, ConstructName::Reference], + vec![], false, ), - Self::new('_', None, None, None, None, true), + Self::new('_', None, None, vec![], vec![], true), Self::new( '_', None, None, - Construct::Single(ConstructName::Phrasing).into(), - Construct::List(full_phrasing_spans.clone()).into(), + vec![ConstructName::Phrasing], + full_phrasing_spans.clone(), false, ), - Self::new('`', None, None, None, None, true), + Self::new('`', None, None, vec![], vec![], true), Self::new( '`', None, None, - Construct::List(vec![ + vec![ ConstructName::CodeFencedLangGraveAccent, ConstructName::CodeFencedMetaGraveAccent, - ]) - .into(), - None, + ], + vec![], false, ), Self::new( '`', None, None, - Construct::Single(ConstructName::Phrasing).into(), - Construct::List(full_phrasing_spans.clone()).into(), + vec![ConstructName::Phrasing], + full_phrasing_spans.clone(), false, ), - Self::new('~', None, None, None, None, true), + Self::new('~', None, None, vec![], vec![], true), ] } diff --git a/mdast_util_to_markdown/src/util/check_bullet.rs b/mdast_util_to_markdown/src/util/check_bullet.rs index 3c7fc62..b6af979 100644 --- a/mdast_util_to_markdown/src/util/check_bullet.rs +++ b/mdast_util_to_markdown/src/util/check_bullet.rs @@ -1,20 +1,21 @@ -use alloc::{boxed::Box, format}; -use markdown::message::Message; +//! JS equivalent https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/util/check-bullet.js use crate::state::State; +use alloc::{boxed::Box, format}; +use markdown::message::Message; pub fn check_bullet(state: &mut State) -> Result { let marker = state.options.bullet; if marker != '*' && marker != '+' && marker != '-' { return Err(Message { + place: None, reason: format!( - "Cannot serialize items with `' {} '` for `options.bullet`, expected `*`, `+`, or `-`", + "Cannot serialize items with `{}` for `options.bullet`, expected `*`, `+`, or `-`", marker ), rule_id: Box::new("unexpected-marker".into()), source: Box::new("mdast-util-to-markdown".into()), - place: None, }); } diff --git a/mdast_util_to_markdown/src/util/check_bullet_ordered.rs b/mdast_util_to_markdown/src/util/check_bullet_ordered.rs index 1d5bd46..b4f3860 100644 --- a/mdast_util_to_markdown/src/util/check_bullet_ordered.rs +++ b/mdast_util_to_markdown/src/util/check_bullet_ordered.rs @@ -1,20 +1,21 @@ -use alloc::{boxed::Box, format}; -use markdown::message::Message; +//! JS equivalent https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/util/check-bullet-ordered.js use crate::state::State; +use alloc::{boxed::Box, format}; +use markdown::message::Message; pub fn check_bullet_ordered(state: &mut State) -> Result { let marker = state.options.bullet_ordered; if marker != '.' && marker != ')' { return Err(Message { + place: None, reason: format!( - "Cannot serialize items with `' {} '` for `options.bullet_ordered`, expected `.` or `)`", + "Cannot serialize items with `{}` for `options.bullet_ordered`, expected `.` or `)`", marker ), rule_id: Box::new("unexpected-marker".into()), source: Box::new("mdast-util-to-markdown".into()), - place: None, }); } diff --git a/mdast_util_to_markdown/src/util/check_bullet_other.rs b/mdast_util_to_markdown/src/util/check_bullet_other.rs index 0388d0d..960dbaf 100644 --- a/mdast_util_to_markdown/src/util/check_bullet_other.rs +++ b/mdast_util_to_markdown/src/util/check_bullet_other.rs @@ -1,9 +1,9 @@ -use alloc::{boxed::Box, format}; -use markdown::message::Message; - -use crate::state::State; +//! JS equivalent https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/util/check-bullet-other.js use super::check_bullet::check_bullet; +use crate::state::State; +use alloc::{boxed::Box, format}; +use markdown::message::Message; pub fn check_bullet_other(state: &mut State) -> Result { let bullet = check_bullet(state)?; @@ -15,25 +15,25 @@ pub fn check_bullet_other(state: &mut State) -> Result { if bullet_other != '*' && bullet_other != '+' && bullet_other != '-' { return Err(Message { + place: None, reason: format!( - "Cannot serialize items with `' {} '` for `options.bullet_other`, expected `*`, `+`, or `-`", + "Cannot serialize items with `{}` for `options.bullet_other`, expected `*`, `+`, or `-`", bullet_other ), rule_id: Box::new("unexpected-marker".into()), source: Box::new("mdast-util-to-markdown".into()), - place: None, }); } if bullet_other == bullet { return Err(Message { + place: None, reason: format!( - "Expected `bullet` (`' {} '`) and `bullet_other` (`' {} '`) to be different", + "Expected `bullet` (`{}`) and `bullet_other` (`{}`) to be different", bullet, bullet_other ), rule_id: Box::new("bullet-match-bullet_other".into()), source: Box::new("mdast-util-to-markdown".into()), - place: None, }); } diff --git a/mdast_util_to_markdown/src/util/check_emphasis.rs b/mdast_util_to_markdown/src/util/check_emphasis.rs index e395f2d..730b755 100644 --- a/mdast_util_to_markdown/src/util/check_emphasis.rs +++ b/mdast_util_to_markdown/src/util/check_emphasis.rs @@ -1,20 +1,21 @@ -use alloc::{boxed::Box, format}; -use markdown::message::Message; +//! JS equivalent https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/util/check-emphasis.js use crate::state::State; +use alloc::{boxed::Box, format}; +use markdown::message::Message; pub fn check_emphasis(state: &State) -> Result { let marker = state.options.emphasis; if marker != '*' && marker != '_' { return Err(Message { + place: None, reason: format!( "Cannot serialize emphasis with `{}` for `options.emphasis`, expected `*`, or `_`", marker ), rule_id: Box::new("unexpected-marker".into()), source: Box::new("mdast-util-to-markdown".into()), - place: None, }); } diff --git a/mdast_util_to_markdown/src/util/check_fence.rs b/mdast_util_to_markdown/src/util/check_fence.rs index fc11c9e..5118ce9 100644 --- a/mdast_util_to_markdown/src/util/check_fence.rs +++ b/mdast_util_to_markdown/src/util/check_fence.rs @@ -1,20 +1,21 @@ -use alloc::{boxed::Box, format}; -use markdown::message::Message; +//! JS equivalent https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/util/check-fence.js use crate::state::State; +use alloc::{boxed::Box, format}; +use markdown::message::Message; pub fn check_fence(state: &mut State) -> Result { let marker = state.options.fence; if marker != '`' && marker != '~' { return Err(Message { + place: None, reason: format!( "Cannot serialize code with `{}` for `options.fence`, expected `` ` `` or `~`", marker ), rule_id: Box::new("unexpected-marker".into()), source: Box::new("mdast-util-to-markdown".into()), - place: None, }); } diff --git a/mdast_util_to_markdown/src/util/check_quote.rs b/mdast_util_to_markdown/src/util/check_quote.rs index ac2c96b..e70c270 100644 --- a/mdast_util_to_markdown/src/util/check_quote.rs +++ b/mdast_util_to_markdown/src/util/check_quote.rs @@ -1,20 +1,21 @@ -use alloc::{boxed::Box, format}; -use markdown::message::Message; +//! JS equivalent https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/util/check-quote.js use crate::state::State; +use alloc::{boxed::Box, format}; +use markdown::message::Message; pub fn check_quote(state: &State) -> Result { let marker = state.options.quote; if marker != '"' && marker != '\'' { return Err(Message { + place: None, reason: format!( - "Cannot serialize title with `' {} '` for `options.quote`, expected `\"`, or `'`", + "Cannot serialize title with `{}` for `options.quote`, expected `\"`, or `'`", marker ), rule_id: Box::new("unexpected-marker".into()), source: Box::new("mdast-util-to-markdown".into()), - place: None, }); } diff --git a/mdast_util_to_markdown/src/util/check_rule.rs b/mdast_util_to_markdown/src/util/check_rule.rs index beba0ef..a0f1885 100644 --- a/mdast_util_to_markdown/src/util/check_rule.rs +++ b/mdast_util_to_markdown/src/util/check_rule.rs @@ -1,20 +1,21 @@ -use alloc::{boxed::Box, format}; -use markdown::message::Message; +//! JS equivalent https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/util/check-rule.js use crate::state::State; +use alloc::{boxed::Box, format}; +use markdown::message::Message; pub fn check_rule(state: &State) -> Result { let marker = state.options.rule; if marker != '*' && marker != '-' && marker != '_' { return Err(Message { + place: None, reason: format!( "Cannot serialize rules with `{}` for `options.rule`, expected `*`, `-`, or `_`", marker ), rule_id: Box::new("unexpected-marker".into()), source: Box::new("mdast-util-to-markdown".into()), - place: None, }); } diff --git a/mdast_util_to_markdown/src/util/check_rule_repetition.rs b/mdast_util_to_markdown/src/util/check_rule_repetition.rs index ea3614b..90f9c91 100644 --- a/mdast_util_to_markdown/src/util/check_rule_repetition.rs +++ b/mdast_util_to_markdown/src/util/check_rule_repetition.rs @@ -1,20 +1,21 @@ -use alloc::{boxed::Box, format}; -use markdown::message::Message; +//! JS equivalent https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/util/check-rule-repetition.js use crate::state::State; +use alloc::{boxed::Box, format}; +use markdown::message::Message; pub fn check_rule_repetition(state: &State) -> Result { let repetition = state.options.rule_repetition; if repetition < 3 { return Err(Message { + place: None, reason: format!( "Cannot serialize rules with repetition `{}` for `options.rule_repetition`, expected `3` or more", repetition ), rule_id: Box::new("unexpected-marker".into()), source: Box::new("mdast-util-to-markdown".into()), - place: None, }); } diff --git a/mdast_util_to_markdown/src/util/check_strong.rs b/mdast_util_to_markdown/src/util/check_strong.rs index eea2dd4..ae315b7 100644 --- a/mdast_util_to_markdown/src/util/check_strong.rs +++ b/mdast_util_to_markdown/src/util/check_strong.rs @@ -1,20 +1,21 @@ -use alloc::{boxed::Box, format}; -use markdown::message::Message; +//! JS equivalent https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/util/check-strong.js use crate::state::State; +use alloc::{boxed::Box, format}; +use markdown::message::Message; pub fn check_strong(state: &State) -> Result { let marker = state.options.strong; if marker != '*' && marker != '_' { return Err(Message { + place: None, reason: format!( "Cannot serialize strong with `{}` for `options.strong`, expected `*`, or `_`", marker ), rule_id: Box::new("unexpected-marker".into()), source: Box::new("mdast-util-to-markdown".into()), - place: None, }); } diff --git a/mdast_util_to_markdown/src/util/contains_control_or_whitespace.rs b/mdast_util_to_markdown/src/util/contains_control_or_whitespace.rs new file mode 100644 index 0000000..fe7f7a8 --- /dev/null +++ b/mdast_util_to_markdown/src/util/contains_control_or_whitespace.rs @@ -0,0 +1,3 @@ +pub fn contains_control_or_whitespace(value: &str) -> bool { + value.chars().any(|c| c.is_whitespace() || c.is_control()) +} diff --git a/mdast_util_to_markdown/src/util/format_code_as_indented.rs b/mdast_util_to_markdown/src/util/format_code_as_indented.rs index 68e318e..fd828f5 100644 --- a/mdast_util_to_markdown/src/util/format_code_as_indented.rs +++ b/mdast_util_to_markdown/src/util/format_code_as_indented.rs @@ -1,11 +1,13 @@ -use markdown::mdast::Code; -use regex::Regex; +//! JS equivalent https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/util/format-code-as-indented.js use crate::state::State; +use markdown::mdast::Code; +use regex::Regex; pub fn format_code_as_indented(code: &Code, state: &State) -> bool { let non_whitespace = code.value.chars().any(|c| !c.is_whitespace()); let blank = Regex::new(r"^[\t ]*(?:[\r\n]|$)|(?:^|[\r\n])[\t ]*$").unwrap(); + !state.options.fences && !code.value.is_empty() && code.lang.is_none() diff --git a/mdast_util_to_markdown/src/util/format_heading_as_setext.rs b/mdast_util_to_markdown/src/util/format_heading_as_setext.rs index 9fa5569..773400b 100644 --- a/mdast_util_to_markdown/src/util/format_heading_as_setext.rs +++ b/mdast_util_to_markdown/src/util/format_heading_as_setext.rs @@ -1,3 +1,5 @@ +//! JS equivalent https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/util/format-heading-as-setext.js + use alloc::string::{String, ToString}; use markdown::mdast::{Heading, Node}; use regex::Regex; @@ -7,6 +9,7 @@ use crate::state::State; pub fn format_heading_as_setext(heading: &Heading, state: &State) -> bool { let line_break = Regex::new(r"\r?\n|\r").unwrap(); let mut literal_with_line_break = false; + for child in &heading.children { if include_literal_with_line_break(child, &line_break) { literal_with_line_break = true; @@ -19,20 +22,23 @@ pub fn format_heading_as_setext(heading: &Heading, state: &State) -> bool { && (state.options.setext || literal_with_line_break) } +/// See: . fn include_literal_with_line_break(node: &Node, regex: &Regex) -> bool { match node { Node::Break(_) => true, - Node::MdxjsEsm(x) => regex.is_match(&x.value), - Node::Toml(x) => regex.is_match(&x.value), - Node::Yaml(x) => regex.is_match(&x.value), + // Literals. + Node::Code(x) => regex.is_match(&x.value), + Node::Html(x) => regex.is_match(&x.value), Node::InlineCode(x) => regex.is_match(&x.value), Node::InlineMath(x) => regex.is_match(&x.value), - Node::MdxTextExpression(x) => regex.is_match(&x.value), - Node::Html(x) => regex.is_match(&x.value), - Node::Text(x) => regex.is_match(&x.value), - Node::Code(x) => regex.is_match(&x.value), Node::Math(x) => regex.is_match(&x.value), Node::MdxFlowExpression(x) => regex.is_match(&x.value), + Node::MdxTextExpression(x) => regex.is_match(&x.value), + Node::MdxjsEsm(x) => regex.is_match(&x.value), + Node::Text(x) => regex.is_match(&x.value), + Node::Toml(x) => regex.is_match(&x.value), + Node::Yaml(x) => regex.is_match(&x.value), + // Anything else. _ => { if let Some(children) = node.children() { for child in children { @@ -47,6 +53,7 @@ fn include_literal_with_line_break(node: &Node, regex: &Regex) -> bool { } } +/// Tiny version of `mdast-util-to-string`. fn to_string(children: &[Node]) -> String { children.iter().map(ToString::to_string).collect() } diff --git a/mdast_util_to_markdown/src/util/format_link_as_auto_link.rs b/mdast_util_to_markdown/src/util/format_link_as_auto_link.rs index 7def88f..1809513 100644 --- a/mdast_util_to_markdown/src/util/format_link_as_auto_link.rs +++ b/mdast_util_to_markdown/src/util/format_link_as_auto_link.rs @@ -1,9 +1,10 @@ +//! JS equivalent https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/util/format-link-as-autolink.js + +use crate::state::State; use alloc::{format, string::ToString}; use markdown::mdast::{Link, Node}; use regex::RegexBuilder; -use crate::state::State; - pub fn format_link_as_auto_link(link: &Link, node: &Node, state: &State) -> bool { let raw = node.to_string(); @@ -12,7 +13,7 @@ pub fn format_link_as_auto_link(link: &Link, node: &Node, state: &State) -> bool return false; } - let mail_to = format!("mailto:{}", raw); + let mailto = format!("mailto:{}", raw); let start_with_protocol = RegexBuilder::new("^[a-z][a-z+.-]+:") .case_insensitive(true) .build() @@ -22,7 +23,7 @@ pub fn format_link_as_auto_link(link: &Link, node: &Node, state: &State) -> bool && !link.url.is_empty() && link.title.is_none() && matches!(children[0], Node::Text(_)) - && (raw == link.url || mail_to == link.url) + && (raw == link.url || mailto == link.url) && start_with_protocol.is_match(&link.url) && is_valid_url(&link.url); } @@ -32,5 +33,5 @@ pub fn format_link_as_auto_link(link: &Link, node: &Node, state: &State) -> bool fn is_valid_url(url: &str) -> bool { !url.chars() - .any(|c| c.is_whitespace() || c.is_control() || c == '>' || c == '<') + .any(|c| c.is_control() || c.is_whitespace() || c == '<' || c == '>') } diff --git a/mdast_util_to_markdown/src/util/longest_char_streak.rs b/mdast_util_to_markdown/src/util/longest_char_streak.rs index b5097d5..8b8b94a 100644 --- a/mdast_util_to_markdown/src/util/longest_char_streak.rs +++ b/mdast_util_to_markdown/src/util/longest_char_streak.rs @@ -1,3 +1,5 @@ +//! JS equivalent https://github.com/wooorm/longest-streak/blob/main/index.js + pub fn longest_char_streak(haystack: &str, needle: char) -> usize { let mut max = 0; let mut chars = haystack.chars(); @@ -20,7 +22,7 @@ pub fn longest_char_streak(haystack: &str, needle: char) -> usize { } #[cfg(test)] -mod longest_char_streak { +mod tests { use super::*; #[test] diff --git a/mdast_util_to_markdown/src/util/mod.rs b/mdast_util_to_markdown/src/util/mod.rs index 7863418..faffeac 100644 --- a/mdast_util_to_markdown/src/util/mod.rs +++ b/mdast_util_to_markdown/src/util/mod.rs @@ -7,6 +7,7 @@ pub mod check_quote; pub mod check_rule; pub mod check_rule_repetition; pub mod check_strong; +pub mod contains_control_or_whitespace; pub mod format_code_as_indented; pub mod format_heading_as_setext; pub mod format_link_as_auto_link; diff --git a/mdast_util_to_markdown/src/util/pattern_in_scope.rs b/mdast_util_to_markdown/src/util/pattern_in_scope.rs index 0480b33..13c7821 100644 --- a/mdast_util_to_markdown/src/util/pattern_in_scope.rs +++ b/mdast_util_to_markdown/src/util/pattern_in_scope.rs @@ -1,31 +1,24 @@ -use crate::{ - construct_name::ConstructName, - r#unsafe::{Construct, Unsafe}, -}; +//! JS equivalent https://github.com/syntax-tree/mdast-util-to-markdown/blob/main/lib/util/pattern-in-scope.js -pub fn pattern_in_scope(stack: &[ConstructName], pattern: &Unsafe) -> bool { - list_in_scope(stack, &pattern.in_construct, true) - && !list_in_scope(stack, &pattern.not_in_construct, false) -} +use crate::{construct_name::ConstructName, r#unsafe::Unsafe}; -fn list_in_scope(stack: &[ConstructName], list: &Option, none: bool) -> bool { - let Some(list) = list else { +/// JS: . +fn list_in_scope(stack: &[ConstructName], list: &[ConstructName], none: bool) -> bool { + if list.is_empty() { return none; - }; - match list { - Construct::Single(construct_name) => stack.contains(construct_name), - Construct::List(constructs_names) => { - if constructs_names.is_empty() { - return none; - } - - for construct_name in constructs_names { - if stack.contains(construct_name) { - return true; - } - } + } - false + for construct_name in list { + if stack.contains(construct_name) { + return true; } } + + false +} + +/// JS: . +pub fn pattern_in_scope(stack: &[ConstructName], pattern: &Unsafe) -> bool { + list_in_scope(stack, &pattern.in_construct, true) + && !list_in_scope(stack, &pattern.not_in_construct, false) } diff --git a/mdast_util_to_markdown/src/util/safe.rs b/mdast_util_to_markdown/src/util/safe.rs index 4fa33be..a2dd15e 100644 --- a/mdast_util_to_markdown/src/util/safe.rs +++ b/mdast_util_to_markdown/src/util/safe.rs @@ -1,27 +1,30 @@ +//! JS equivalent https://github.com/syntax-tree/mdast-util-to-markdown/blob/fd6a508/lib/util/safe.js + use alloc::{format, string::String, vec::Vec}; use regex::Regex; +pub struct EscapeInfos { + pub after: bool, + pub before: bool, +} + pub struct SafeConfig<'a> { - pub before: &'a str, pub after: &'a str, + pub before: &'a str, pub encode: Option, } impl<'a> SafeConfig<'a> { pub(crate) fn new(before: &'a str, after: &'a str, encode: Option) -> Self { SafeConfig { - before, after, + before, encode, } } } -pub struct EscapeInfos { - pub before: bool, - pub after: bool, -} - +/// JS: . pub fn escape_backslashes(value: &str, after: &str) -> String { let expression = Regex::new(r"\\[!-/:-@\[-`{-~]").unwrap(); let mut results: String = String::new(); diff --git a/mdast_util_to_markdown/tests/blockquote.rs b/mdast_util_to_markdown/tests/blockquote.rs index 1de821e..ac3c8f8 100644 --- a/mdast_util_to_markdown/tests/blockquote.rs +++ b/mdast_util_to_markdown/tests/blockquote.rs @@ -1,13 +1,11 @@ use markdown::mdast::{ - Blockquote, Break, Code, Heading, Html, Image, ImageReference, InlineCode, Link, LinkReference, - List, ListItem, Node, Paragraph, ReferenceKind, Strong, Text, ThematicBreak, + Blockquote, Break, Code, Definition, Emphasis, Heading, Html, Image, ImageReference, + InlineCode, Link, LinkReference, List, ListItem, Node, Paragraph, ReferenceKind, Strong, Text, + ThematicBreak, +}; +use mdast_util_to_markdown::{ + to_markdown as to, to_markdown_with_options as to_md_with_opts, Options, }; -use markdown::mdast::{Definition, Emphasis}; - -use mdast_util_to_markdown::to_markdown as to; -use mdast_util_to_markdown::to_markdown_with_options as to_md_with_opts; - -use mdast_util_to_markdown::Options; use pretty_assertions::assert_eq; #[test] diff --git a/mdast_util_to_markdown/tests/break.rs b/mdast_util_to_markdown/tests/break.rs index 228a589..68a43c3 100644 --- a/mdast_util_to_markdown/tests/break.rs +++ b/mdast_util_to_markdown/tests/break.rs @@ -1,7 +1,10 @@ -use markdown::mdast::{Break, Heading, Node, Text}; -use markdown::to_mdast as from; -use mdast_util_to_markdown::to_markdown_with_options as to_md_with_opts; -use mdast_util_to_markdown::{to_markdown as to, Options}; +use markdown::{ + mdast::{Break, Heading, Node, Text}, + to_mdast as from, +}; +use mdast_util_to_markdown::{ + to_markdown as to, to_markdown_with_options as to_md_with_opts, Options, +}; use pretty_assertions::assert_eq; #[test] diff --git a/mdast_util_to_markdown/tests/code.rs b/mdast_util_to_markdown/tests/code.rs index 4fbfa9b..f68c3b6 100644 --- a/mdast_util_to_markdown/tests/code.rs +++ b/mdast_util_to_markdown/tests/code.rs @@ -1,8 +1,7 @@ use markdown::mdast::{Code, Node}; -use mdast_util_to_markdown::to_markdown as to; -use mdast_util_to_markdown::to_markdown_with_options as to_md_with_opts; - -use mdast_util_to_markdown::Options; +use mdast_util_to_markdown::{ + to_markdown as to, to_markdown_with_options as to_md_with_opts, Options, +}; use pretty_assertions::assert_eq; #[test] diff --git a/mdast_util_to_markdown/tests/core.rs b/mdast_util_to_markdown/tests/core.rs index 528d396..09c14f5 100644 --- a/mdast_util_to_markdown/tests/core.rs +++ b/mdast_util_to_markdown/tests/core.rs @@ -1,7 +1,9 @@ -use markdown::mdast::{Break, Code, Definition, Heading, List, ListItem}; -use markdown::mdast::{Node, Paragraph, Root, Text, ThematicBreak}; -use mdast_util_to_markdown::to_markdown_with_options as to_md_with_opts; -use mdast_util_to_markdown::{to_markdown as to, Options}; +use markdown::mdast::{ + Break, Code, Definition, Heading, List, ListItem, Node, Paragraph, Root, Text, ThematicBreak, +}; +use mdast_util_to_markdown::{ + to_markdown as to, to_markdown_with_options as to_md_with_opts, Options, +}; use pretty_assertions::assert_eq; #[test] diff --git a/mdast_util_to_markdown/tests/definition.rs b/mdast_util_to_markdown/tests/definition.rs index f526376..143ea9a 100644 --- a/mdast_util_to_markdown/tests/definition.rs +++ b/mdast_util_to_markdown/tests/definition.rs @@ -1,7 +1,7 @@ use markdown::mdast::{Definition, Node}; -use mdast_util_to_markdown::{to_markdown as to, Options}; - -use mdast_util_to_markdown::to_markdown_with_options as to_md_with_opts; +use mdast_util_to_markdown::{ + to_markdown as to, to_markdown_with_options as to_md_with_opts, Options, +}; use pretty_assertions::assert_eq; #[test] diff --git a/mdast_util_to_markdown/tests/emphasis.rs b/mdast_util_to_markdown/tests/emphasis.rs index dcc1834..44050bb 100644 --- a/mdast_util_to_markdown/tests/emphasis.rs +++ b/mdast_util_to_markdown/tests/emphasis.rs @@ -1,9 +1,7 @@ -use markdown::mdast::Emphasis; -use markdown::mdast::{Node, Text}; -use mdast_util_to_markdown::to_markdown as to; -use mdast_util_to_markdown::to_markdown_with_options as to_md_with_opts; - -use mdast_util_to_markdown::Options; +use markdown::mdast::{Emphasis, Node, Text}; +use mdast_util_to_markdown::{ + to_markdown as to, to_markdown_with_options as to_md_with_opts, Options, +}; use pretty_assertions::assert_eq; #[test] diff --git a/mdast_util_to_markdown/tests/heading.rs b/mdast_util_to_markdown/tests/heading.rs index 4b12889..164d3b0 100644 --- a/mdast_util_to_markdown/tests/heading.rs +++ b/mdast_util_to_markdown/tests/heading.rs @@ -1,9 +1,7 @@ -use markdown::mdast::{Break, Html}; -use markdown::mdast::{Heading, Node, Text}; -use mdast_util_to_markdown::to_markdown as to; -use mdast_util_to_markdown::to_markdown_with_options as to_md_with_opts; - -use mdast_util_to_markdown::Options; +use markdown::mdast::{Break, Heading, Html, Node, Text}; +use mdast_util_to_markdown::{ + to_markdown as to, to_markdown_with_options as to_md_with_opts, Options, +}; use pretty_assertions::assert_eq; #[test] diff --git a/mdast_util_to_markdown/tests/html.rs b/mdast_util_to_markdown/tests/html.rs index 24967ea..c59fae9 100644 --- a/mdast_util_to_markdown/tests/html.rs +++ b/mdast_util_to_markdown/tests/html.rs @@ -1,6 +1,5 @@ use markdown::mdast::{Html, Node, Paragraph, Text}; use mdast_util_to_markdown::to_markdown as to; - use pretty_assertions::assert_eq; #[test] diff --git a/mdast_util_to_markdown/tests/image.rs b/mdast_util_to_markdown/tests/image.rs index 45308da..1c247e6 100644 --- a/mdast_util_to_markdown/tests/image.rs +++ b/mdast_util_to_markdown/tests/image.rs @@ -1,8 +1,7 @@ use markdown::mdast::{Image, Node}; -use mdast_util_to_markdown::to_markdown as to; -use mdast_util_to_markdown::to_markdown_with_options as to_md_with_opts; - -use mdast_util_to_markdown::Options; +use mdast_util_to_markdown::{ + to_markdown as to, to_markdown_with_options as to_md_with_opts, Options, +}; use pretty_assertions::assert_eq; #[test] diff --git a/mdast_util_to_markdown/tests/image_reference.rs b/mdast_util_to_markdown/tests/image_reference.rs index fba31bf..1575987 100644 --- a/mdast_util_to_markdown/tests/image_reference.rs +++ b/mdast_util_to_markdown/tests/image_reference.rs @@ -1,6 +1,5 @@ use markdown::mdast::{ImageReference, Node, Paragraph, ReferenceKind}; use mdast_util_to_markdown::to_markdown as to; - use pretty_assertions::assert_eq; #[test] diff --git a/mdast_util_to_markdown/tests/inline_code.rs b/mdast_util_to_markdown/tests/inline_code.rs index 62bf2fe..99da9a6 100644 --- a/mdast_util_to_markdown/tests/inline_code.rs +++ b/mdast_util_to_markdown/tests/inline_code.rs @@ -1,6 +1,5 @@ use markdown::mdast::{InlineCode, Node}; use mdast_util_to_markdown::to_markdown as to; - use pretty_assertions::assert_eq; #[test] diff --git a/mdast_util_to_markdown/tests/link.rs b/mdast_util_to_markdown/tests/link.rs index dfb9cd7..b8497e9 100644 --- a/mdast_util_to_markdown/tests/link.rs +++ b/mdast_util_to_markdown/tests/link.rs @@ -1,8 +1,7 @@ use markdown::mdast::{Link, Node, Text}; -use mdast_util_to_markdown::to_markdown as to; - -use mdast_util_to_markdown::to_markdown_with_options as to_md_with_opts; -use mdast_util_to_markdown::Options; +use mdast_util_to_markdown::{ + to_markdown as to, to_markdown_with_options as to_md_with_opts, Options, +}; use pretty_assertions::assert_eq; #[test] diff --git a/mdast_util_to_markdown/tests/link_reference.rs b/mdast_util_to_markdown/tests/link_reference.rs index de8f9e8..f6045ff 100644 --- a/mdast_util_to_markdown/tests/link_reference.rs +++ b/mdast_util_to_markdown/tests/link_reference.rs @@ -1,6 +1,5 @@ use markdown::mdast::{LinkReference, Node, Paragraph, ReferenceKind, Text}; use mdast_util_to_markdown::to_markdown as to; - use pretty_assertions::assert_eq; #[test] diff --git a/mdast_util_to_markdown/tests/list.rs b/mdast_util_to_markdown/tests/list.rs index 6a9952e..33107a6 100644 --- a/mdast_util_to_markdown/tests/list.rs +++ b/mdast_util_to_markdown/tests/list.rs @@ -1,9 +1,7 @@ use markdown::mdast::{List, ListItem, Node, Paragraph, Text, ThematicBreak}; -use mdast_util_to_markdown::to_markdown as to; -use mdast_util_to_markdown::to_markdown_with_options as to_md_with_opts; - -use mdast_util_to_markdown::IndentOptions; -use mdast_util_to_markdown::Options; +use mdast_util_to_markdown::{ + to_markdown as to, to_markdown_with_options as to_md_with_opts, IndentOptions, Options, +}; use pretty_assertions::assert_eq; #[test] diff --git a/mdast_util_to_markdown/tests/list_item.rs b/mdast_util_to_markdown/tests/list_item.rs index ce9ce09..b691660 100644 --- a/mdast_util_to_markdown/tests/list_item.rs +++ b/mdast_util_to_markdown/tests/list_item.rs @@ -1,9 +1,7 @@ -use markdown::mdast::{List, ListItem, Node, Root, Text}; -use markdown::mdast::{Paragraph, ThematicBreak}; -use mdast_util_to_markdown::{to_markdown as to, IndentOptions}; - -use mdast_util_to_markdown::to_markdown_with_options as to_md_with_opts; -use mdast_util_to_markdown::Options; +use markdown::mdast::{List, ListItem, Node, Paragraph, Root, Text, ThematicBreak}; +use mdast_util_to_markdown::{ + to_markdown as to, to_markdown_with_options as to_md_with_opts, IndentOptions, Options, +}; use pretty_assertions::assert_eq; #[test] diff --git a/mdast_util_to_markdown/tests/paragraph.rs b/mdast_util_to_markdown/tests/paragraph.rs index d0a7265..b04929d 100644 --- a/mdast_util_to_markdown/tests/paragraph.rs +++ b/mdast_util_to_markdown/tests/paragraph.rs @@ -1,6 +1,5 @@ use markdown::mdast::{Node, Paragraph, Text}; use mdast_util_to_markdown::to_markdown as to; - use pretty_assertions::assert_eq; #[test] diff --git a/mdast_util_to_markdown/tests/roundtrip.rs b/mdast_util_to_markdown/tests/roundtrip.rs index 1bc85a5..c3402c9 100644 --- a/mdast_util_to_markdown/tests/roundtrip.rs +++ b/mdast_util_to_markdown/tests/roundtrip.rs @@ -1,10 +1,9 @@ -use mdast_util_to_markdown::{to_markdown as to, Options}; - use markdown::{mdast::Node, to_mdast as from}; +use mdast_util_to_markdown::{ + to_markdown as to, to_markdown_with_options as to_md_with_opts, Options, +}; use pretty_assertions::assert_eq; -use mdast_util_to_markdown::to_markdown_with_options as to_md_with_opts; - #[test] fn roundtrip() { let doc: String = document(vec![ @@ -122,23 +121,23 @@ fn roundtrip() { let step1 = "\\ \\\\ \\\\\\ \\\\\\\\"; let step2 = "\\ \\ \\\\\\ \\\\\\\\\n"; assert_eq!( - to(&from(&step1, &Default::default()).unwrap()).unwrap(), + to(&from(step1, &Default::default()).unwrap()).unwrap(), step2 ); assert_eq!( - to(&from(&step2, &Default::default()).unwrap()).unwrap(), + to(&from(step2, &Default::default()).unwrap()).unwrap(), step2 ); let doc = "\\\\\\*a\n"; - assert_eq!(to(&from(&doc, &Default::default()).unwrap()).unwrap(), doc); + assert_eq!(to(&from(doc, &Default::default()).unwrap()).unwrap(), doc); let doc = "\\\\*a\\\\\\*"; assert_eq!( remove_pos(&mut from(doc, &Default::default()).unwrap()), remove_pos( &mut from( - &to(&from(&doc, &Default::default()).unwrap()).unwrap(), + &to(&from(doc, &Default::default()).unwrap()).unwrap(), &Default::default() ) .unwrap() @@ -146,13 +145,13 @@ fn roundtrip() { ); let doc = "```\n \n```\n"; - assert_eq!(to(&from(&doc, &Default::default()).unwrap()).unwrap(), doc); + assert_eq!(to(&from(doc, &Default::default()).unwrap()).unwrap(), doc); let doc = "* * -\n"; - assert_eq!(to(&from(&doc, &Default::default()).unwrap()).unwrap(), doc); + assert_eq!(to(&from(doc, &Default::default()).unwrap()).unwrap(), doc); let doc = "- ***\n"; - assert_eq!(to(&from(&doc, &Default::default()).unwrap()).unwrap(), doc); + assert_eq!(to(&from(doc, &Default::default()).unwrap()).unwrap(), doc); let mut tree = from("* a\n- b", &Default::default()).unwrap(); assert_eq!( diff --git a/mdast_util_to_markdown/tests/strong.rs b/mdast_util_to_markdown/tests/strong.rs index f4e8491..6babb31 100644 --- a/mdast_util_to_markdown/tests/strong.rs +++ b/mdast_util_to_markdown/tests/strong.rs @@ -1,8 +1,7 @@ use markdown::mdast::{Node, Strong, Text}; -use mdast_util_to_markdown::to_markdown as to; -use mdast_util_to_markdown::to_markdown_with_options as to_md_with_opts; - -use mdast_util_to_markdown::Options; +use mdast_util_to_markdown::{ + to_markdown as to, to_markdown_with_options as to_md_with_opts, Options, +}; use pretty_assertions::assert_eq; #[test] diff --git a/mdast_util_to_markdown/tests/text.rs b/mdast_util_to_markdown/tests/text.rs index 58270ed..7021720 100644 --- a/mdast_util_to_markdown/tests/text.rs +++ b/mdast_util_to_markdown/tests/text.rs @@ -1,6 +1,5 @@ use markdown::mdast::{Node, Text}; use mdast_util_to_markdown::to_markdown as to; - use pretty_assertions::assert_eq; #[test] diff --git a/mdast_util_to_markdown/tests/thematic_break.rs b/mdast_util_to_markdown/tests/thematic_break.rs index b1e4df4..3c00bf8 100644 --- a/mdast_util_to_markdown/tests/thematic_break.rs +++ b/mdast_util_to_markdown/tests/thematic_break.rs @@ -1,8 +1,7 @@ use markdown::mdast::{Node, ThematicBreak}; -use mdast_util_to_markdown::to_markdown as to; -use mdast_util_to_markdown::to_markdown_with_options as to_md_with_opts; - -use mdast_util_to_markdown::Options; +use mdast_util_to_markdown::{ + to_markdown as to, to_markdown_with_options as to_md_with_opts, Options, +}; use pretty_assertions::assert_eq; #[test] diff --git a/readme.md b/readme.md index 0669cdc..f9c4123 100644 --- a/readme.md +++ b/readme.md @@ -250,23 +250,23 @@ The following bash scripts are useful when working on this project: ``` * run examples: ```sh - RUST_BACKTRACE=1 RUST_LOG=trace cargo run --features log --example lib + RUST_BACKTRACE=1 RUST_LOG=trace cargo run --example lib --features log ``` * format: ```sh - cargo fmt && cargo fix --all-targets --all-features + cargo fmt && cargo fix --all-features --all-targets --workspace ``` * lint: ```sh - cargo fmt --check && cargo clippy --examples --tests --benches --all-features + cargo fmt --check && cargo clippy --all-features --all-targets --workspace ``` * test: ```sh - RUST_BACKTRACE=1 cargo test --all-features + RUST_BACKTRACE=1 cargo test --all-features --workspace ``` * docs: ```sh - cargo doc --document-private-items + cargo doc --document-private-items --examples --workspace ``` * fuzz: ```sh diff --git a/src/util/character_reference.rs b/src/util/character_reference.rs index cc371bb..c150dd0 100644 --- a/src/util/character_reference.rs +++ b/src/util/character_reference.rs @@ -12,8 +12,8 @@ use core::str; /// Turn the name coming from a named character reference (without the `&` or /// `;`) into a string. /// This looks the given string up at `0` in the tuples of -/// [`CHARACTER_REFERENCES`][] (or [`CHARACTER_REFERENCES_HTML_4`][]) and then -/// takes the corresponding value from `1`. +/// `CHARACTER_REFERENCES` (or `CHARACTER_REFERENCES_HTML_4`) +/// and then takes the corresponding value from `1`. /// /// The `html5` boolean is used for named character references, and specifier /// whether the 2125 names from HTML 5 or the 252 names from HTML 4 are