From cd5bb2d16c6b28332b0b6077b27b2b90a8051896 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Thu, 6 Oct 2022 15:57:55 +0200 Subject: Refactor to split parse from compile options --- build.rs | 9 +- examples/lib.rs | 25 +-- readme.md | 30 ++- src/lib.rs | 435 +++++++++++++++++++++++--------------- src/parser.rs | 9 +- src/to_html.rs | 8 +- tests/attention.rs | 20 +- tests/autolink.rs | 20 +- tests/block_quote.rs | 13 +- tests/character_escape.rs | 20 +- tests/character_reference.rs | 19 +- tests/code_fenced.rs | 15 +- tests/code_indented.rs | 28 ++- tests/code_text.rs | 20 +- tests/commonmark.rs | 9 +- tests/definition.rs | 20 +- tests/frontmatter.rs | 15 +- tests/fuzz.rs | 15 +- tests/gfm_autolink_literal.rs | 9 +- tests/gfm_footnote.rs | 47 ++-- tests/gfm_strikethrough.rs | 23 +- tests/gfm_table.rs | 102 ++++++--- tests/gfm_tagfilter.rs | 26 ++- tests/gfm_task_list_item.rs | 9 +- tests/hard_break_escape.rs | 13 +- tests/hard_break_trailing.rs | 13 +- tests/heading_atx.rs | 13 +- tests/heading_setext.rs | 13 +- tests/html_flow.rs | 61 ++++-- tests/html_text.rs | 19 +- tests/image.rs | 20 +- tests/link_reference.rs | 32 ++- tests/link_resource.rs | 11 +- tests/list.rs | 23 +- tests/math_flow.rs | 15 +- tests/math_text.rs | 47 ++-- tests/mdx_esm.rs | 13 +- tests/mdx_expression_flow.rs | 27 ++- tests/mdx_expression_text.rs | 27 ++- tests/mdx_jsx_flow.rs | 14 +- tests/mdx_jsx_text.rs | 64 +++--- tests/mdx_swc.rs | 23 +- tests/misc_dangerous_html.rs | 9 +- tests/misc_default_line_ending.rs | 12 +- tests/misc_line_ending.rs | 9 +- tests/misc_tabs.rs | 8 +- tests/thematic_break.rs | 13 +- tests/xxx_document.rs | 6 +- tests/xxx_jsx_rewrite.rs | 6 +- 49 files changed, 941 insertions(+), 516 deletions(-) diff --git a/build.rs b/build.rs index a971d27..8d24e6c 100644 --- a/build.rs +++ b/build.rs @@ -68,15 +68,18 @@ async fn commonmark() { // > It is generate from the latest CommonMark website. extern crate micromark; -use micromark::{{micromark_with_options, Options}}; +use micromark::{{micromark_with_options, CompileOptions, Options}}; use pretty_assertions::assert_eq; #[rustfmt::skip] #[test] fn commonmark() -> Result<(), String> {{ let danger = Options {{ - allow_dangerous_html: true, - allow_dangerous_protocol: true, + compile: CompileOptions {{ + allow_dangerous_html: true, + allow_dangerous_protocol: true, + ..CompileOptions::default() + }}, ..Options::default() }}; diff --git a/examples/lib.rs b/examples/lib.rs index ed108d2..d14f399 100644 --- a/examples/lib.rs +++ b/examples/lib.rs @@ -1,5 +1,7 @@ extern crate micromark; -use micromark::{micromark, micromark_to_mdast, micromark_with_options, Constructs, Options}; +use micromark::{ + micromark, micromark_to_mdast, micromark_with_options, CompileOptions, Options, ParseOptions, +}; fn main() -> Result<(), String> { // Turn on debugging. @@ -15,8 +17,11 @@ fn main() -> Result<(), String> { micromark_with_options( "
\n\n# Hello, tomato!\n\n
", &Options { - allow_dangerous_html: true, - allow_dangerous_protocol: true, + compile: CompileOptions { + allow_dangerous_html: true, + allow_dangerous_protocol: true, + ..CompileOptions::default() + }, ..Options::default() } ) @@ -27,24 +32,14 @@ fn main() -> Result<(), String> { "{}", micromark_with_options( "* [x] contact@example.com ~~strikethrough~~", - &Options { - constructs: Constructs::gfm(), - gfm_tagfilter: true, - ..Options::default() - } + &Options::gfm() )? ); // Access syntax tree and support MDX extensions: println!( "{:?}", - micromark_to_mdast( - "# , {username}!", - &Options { - constructs: Constructs::mdx(), - ..Options::default() - } - )? + micromark_to_mdast("# , {username}!", &ParseOptions::mdx())? ); Ok(()) diff --git a/readme.md b/readme.md index 91b7985..1afce35 100644 --- a/readme.md +++ b/readme.md @@ -92,17 +92,14 @@ Extensions (in this case GFM): ```rs extern crate micromark; -use micromark::{micromark_with_options, Constructs, Options}; +use micromark::{micromark_with_options, Options}; fn main() -> Result<(), String> { println!( "{}", micromark_with_options( "* [x] contact@example.com ~~strikethrough~~", - &Options { - constructs: Constructs::gfm(), - ..Options::default() - } + &Options::gfm() )? ); @@ -122,6 +119,29 @@ Yields: ``` +Syntax tree: + +```rs +extern crate micromark; +use micromark::{micromark_to_mdast, ParseOptions}; + +fn main() -> Result<(), String> { + println!( + "{:?}", + micromark_to_mdast("# Hey, *you*!", &ParseOptions::default())? + ); + + Ok(()) +} +``` + +Yields: + +```text +Root { children: [Heading { children: [Text { value: "Hey, ", position: Some(1:3-1:8 (2-7)) }, Emphasis { children: [Text { value: "you", position: Some(1:9-1:12 (8-11)) }], position: Some(1:8-1:13 (7-12)) }, Text { value: "!", position: Some(1:13-1:14 (12-13)) }], position: Some(1:1-1:14 (0-13)), depth: 1 }], position: Some(1:1-1:14 (0-13)) } + +``` + ## API `micromark` exposes diff --git a/src/lib.rs b/src/lib.rs index 8eaa2bb..02fb5f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -168,7 +168,7 @@ pub type MdxEsmParse = dyn Fn(&str) -> MdxSignal; /// Not all constructs can be configured. /// Notably, blank lines and paragraphs cannot be turned off. #[allow(clippy::struct_excessive_bools)] -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct Constructs { /// Attention. /// @@ -392,9 +392,9 @@ pub struct Constructs { /// ^^^^^^^^^^^^^^^^^ /// ``` /// - /// > 👉 **Note**: you *must* pass [`options.mdx_esm_parse`][MdxEsmParse] - /// > too. - /// > Otherwise, this option has no affect. + /// > 👉 **Note**: you *must* pass [`mdx_esm_parse`][MdxEsmParse] + /// > in [`ParseOptions`][] too. + /// > Otherwise, this option has no effect. pub mdx_esm: bool, /// MDX: expression (flow). /// @@ -522,10 +522,10 @@ impl Constructs { /// (autolinks, code (indented), html), and turns on MDX (JSX, /// expressions, ESM). /// - /// > 👉 **Note**: you *must* pass [`options.mdx_esm_parse`][MdxEsmParse] - /// > to support ESM. + /// > 👉 **Note**: you *must* pass [`mdx_esm_parse`][MdxEsmParse] + /// > in [`ParseOptions`][] too to support ESM. /// > You *can* pass - /// > [`options.mdx_expression_parse`][MdxExpressionParse] + /// > [`mdx_expression_parse`][MdxExpressionParse] /// > to parse expressions according to a certain grammar (typically, a /// > programming language). #[must_use] @@ -545,10 +545,10 @@ impl Constructs { } } -/// Configuration (optional). +/// Configuration that describes how to compile to HTML. #[allow(clippy::struct_excessive_bools)] -pub struct Options { - // Note: when adding fields, don’t forget to add them to `fmt::Debug` below. +#[derive(Clone, Debug, Default)] +pub struct CompileOptions { /// Whether to allow (dangerous) HTML. /// The default is `false`, you can turn it on to `true` for trusted /// content. @@ -556,7 +556,7 @@ pub struct Options { /// ## Examples /// /// ``` - /// use micromark::{micromark, micromark_with_options, Options}; + /// use micromark::{micromark, micromark_with_options, CompileOptions, Options}; /// # fn main() -> Result<(), String> { /// /// // micromark is safe by default: @@ -570,7 +570,10 @@ pub struct Options { /// micromark_with_options( /// "Hi, venus!", /// &Options { - /// allow_dangerous_html: true, + /// compile: CompileOptions { + /// allow_dangerous_html: true, + /// ..CompileOptions::default() + /// }, /// ..Options::default() /// } /// )?, @@ -588,7 +591,7 @@ pub struct Options { /// ## Examples /// /// ``` - /// use micromark::{micromark, micromark_with_options, Options}; + /// use micromark::{micromark, micromark_with_options, CompileOptions, Options}; /// # fn main() -> Result<(), String> { /// /// // micromark is safe by default: @@ -602,7 +605,10 @@ pub struct Options { /// micromark_with_options( /// "", /// &Options { - /// allow_dangerous_protocol: true, + /// compile: CompileOptions { + /// allow_dangerous_protocol: true, + /// ..CompileOptions::default() + /// }, /// ..Options::default() /// } /// )?, @@ -613,41 +619,8 @@ pub struct Options { /// ``` pub allow_dangerous_protocol: bool, - /// Which constructs to enable and disable. - /// The default is to follow `CommonMark`. - /// - /// ## Examples - /// - /// ``` - /// use micromark::{micromark, micromark_with_options, Options, Constructs}; - /// # fn main() -> Result<(), String> { - /// - /// // micromark follows CommonMark by default: - /// assert_eq!( - /// micromark(" indented code?"), - /// "
indented code?\n
" - /// ); - /// - /// // Pass `constructs` to choose what to enable and disable: - /// assert_eq!( - /// micromark_with_options( - /// " indented code?", - /// &Options { - /// constructs: Constructs { - /// code_indented: false, - /// ..Constructs::default() - /// }, - /// ..Options::default() - /// } - /// )?, - /// "

indented code?

" - /// ); - /// # Ok(()) - /// # } - /// ``` - pub constructs: Constructs, - - /// Default line ending to use, for line endings not in `value`. + /// Default line ending to use when compiling to HTML, for line endings not + /// in `value`. /// /// Generally, micromark copies line endings (`\r`, `\n`, `\r\n`) in the /// markdown document over to the compiled HTML. @@ -662,7 +635,7 @@ pub struct Options { /// ## Examples /// /// ``` - /// use micromark::{micromark, micromark_with_options, Options, LineEnding}; + /// use micromark::{micromark, micromark_with_options, CompileOptions, LineEnding, Options}; /// # fn main() -> Result<(), String> { /// /// // micromark uses `\n` by default: @@ -676,7 +649,10 @@ pub struct Options { /// micromark_with_options( /// "> a", /// &Options { - /// default_line_ending: LineEnding::CarriageReturnLineFeed, + /// compile: CompileOptions { + /// default_line_ending: LineEnding::CarriageReturnLineFeed, + /// ..CompileOptions::default() + /// }, /// ..Options::default() /// } /// )?, @@ -696,17 +672,14 @@ pub struct Options { /// ## Examples /// /// ``` - /// use micromark::{micromark, micromark_with_options, Options, Constructs}; + /// use micromark::{micromark, micromark_with_options, CompileOptions, Options, ParseOptions}; /// # fn main() -> Result<(), String> { /// /// // `"Footnotes"` is used by default: /// assert_eq!( /// micromark_with_options( /// "[^a]\n\n[^a]: b", - /// &Options { - /// constructs: Constructs::gfm(), - /// ..Options::default() - /// } + /// &Options::gfm() /// )?, /// "

1

\n

Footnotes

\n
    \n
  1. \n

    b ↩

    \n
  2. \n
\n
\n" /// ); @@ -716,9 +689,11 @@ pub struct Options { /// micromark_with_options( /// "[^a]\n\n[^a]: b", /// &Options { - /// constructs: Constructs::gfm(), - /// gfm_footnote_label: Some("Notes de bas de page".to_string()), - /// ..Options::default() + /// parse: ParseOptions::gfm(), + /// compile: CompileOptions { + /// gfm_footnote_label: Some("Notes de bas de page".to_string()), + /// ..CompileOptions::gfm() + /// } /// } /// )?, /// "

1

\n

Notes de bas de page

\n
    \n
  1. \n

    b ↩

    \n
  2. \n
\n
\n" @@ -735,17 +710,14 @@ pub struct Options { /// ## Examples /// /// ``` - /// use micromark::{micromark, micromark_with_options, Options, Constructs}; + /// use micromark::{micromark, micromark_with_options, CompileOptions, Options, ParseOptions}; /// # fn main() -> Result<(), String> { /// /// // `"h2"` is used by default: /// assert_eq!( /// micromark_with_options( /// "[^a]\n\n[^a]: b", - /// &Options { - /// constructs: Constructs::gfm(), - /// ..Options::default() - /// } + /// &Options::gfm() /// )?, /// "

1

\n

Footnotes

\n
    \n
  1. \n

    b ↩

    \n
  2. \n
\n
\n" /// ); @@ -755,9 +727,11 @@ pub struct Options { /// micromark_with_options( /// "[^a]\n\n[^a]: b", /// &Options { - /// constructs: Constructs::gfm(), - /// gfm_footnote_label_tag_name: Some("h1".to_string()), - /// ..Options::default() + /// parse: ParseOptions::gfm(), + /// compile: CompileOptions { + /// gfm_footnote_label_tag_name: Some("h1".to_string()), + /// ..CompileOptions::gfm() + /// } /// } /// )?, /// "

1

\n

Footnotes

\n
    \n
  1. \n

    b ↩

    \n
  2. \n
\n
\n" @@ -780,17 +754,14 @@ pub struct Options { /// ## Examples /// /// ``` - /// use micromark::{micromark, micromark_with_options, Options, Constructs}; + /// use micromark::{micromark, micromark_with_options, CompileOptions, Options, ParseOptions}; /// # fn main() -> Result<(), String> { /// /// // `"class=\"sr-only\""` is used by default: /// assert_eq!( /// micromark_with_options( /// "[^a]\n\n[^a]: b", - /// &Options { - /// constructs: Constructs::gfm(), - /// ..Options::default() - /// } + /// &Options::gfm() /// )?, /// "

1

\n

Footnotes

\n
    \n
  1. \n

    b ↩

    \n
  2. \n
\n
\n" /// ); @@ -800,9 +771,11 @@ pub struct Options { /// micromark_with_options( /// "[^a]\n\n[^a]: b", /// &Options { - /// constructs: Constructs::gfm(), - /// gfm_footnote_label_attributes: Some("class=\"footnote-heading\"".to_string()), - /// ..Options::default() + /// parse: ParseOptions::gfm(), + /// compile: CompileOptions { + /// gfm_footnote_label_attributes: Some("class=\"footnote-heading\"".to_string()), + /// ..CompileOptions::gfm() + /// } /// } /// )?, /// "

1

\n

Footnotes

\n
    \n
  1. \n

    b ↩

    \n
  2. \n
\n
\n" @@ -820,17 +793,14 @@ pub struct Options { /// ## Examples /// /// ``` - /// use micromark::{micromark, micromark_with_options, Options, Constructs}; + /// use micromark::{micromark, micromark_with_options, CompileOptions, Options, ParseOptions}; /// # fn main() -> Result<(), String> { /// /// // `"Back to content"` is used by default: /// assert_eq!( /// micromark_with_options( /// "[^a]\n\n[^a]: b", - /// &Options { - /// constructs: Constructs::gfm(), - /// ..Options::default() - /// } + /// &Options::gfm() /// )?, /// "

1

\n

Footnotes

\n
    \n
  1. \n

    b ↩

    \n
  2. \n
\n
\n" /// ); @@ -840,9 +810,11 @@ pub struct Options { /// micromark_with_options( /// "[^a]\n\n[^a]: b", /// &Options { - /// constructs: Constructs::gfm(), - /// gfm_footnote_back_label: Some("Arrière".to_string()), - /// ..Options::default() + /// parse: ParseOptions::gfm(), + /// compile: CompileOptions { + /// gfm_footnote_back_label: Some("Arrière".to_string()), + /// ..CompileOptions::gfm() + /// } /// } /// )?, /// "

1

\n

Footnotes

\n
    \n
  1. \n

    b ↩

    \n
  2. \n
\n
\n" @@ -870,17 +842,14 @@ pub struct Options { /// ## Examples /// /// ``` - /// use micromark::{micromark, micromark_with_options, Options, Constructs}; + /// use micromark::{micromark, micromark_with_options, CompileOptions, Options, ParseOptions}; /// # fn main() -> Result<(), String> { /// /// // `"user-content-"` is used by default: /// assert_eq!( /// micromark_with_options( /// "[^a]\n\n[^a]: b", - /// &Options { - /// constructs: Constructs::gfm(), - /// ..Options::default() - /// } + /// &Options::gfm() /// )?, /// "

1

\n

Footnotes

\n
    \n
  1. \n

    b ↩

    \n
  2. \n
\n
\n" /// ); @@ -890,9 +859,11 @@ pub struct Options { /// micromark_with_options( /// "[^a]\n\n[^a]: b", /// &Options { - /// constructs: Constructs::gfm(), - /// gfm_footnote_clobber_prefix: Some("".to_string()), - /// ..Options::default() + /// parse: ParseOptions::gfm(), + /// compile: CompileOptions { + /// gfm_footnote_clobber_prefix: Some("".to_string()), + /// ..CompileOptions::gfm() + /// } /// } /// )?, /// "

1

\n

Footnotes

\n
    \n
  1. \n

    b ↩

    \n
  2. \n
\n
\n" @@ -902,94 +873,161 @@ pub struct Options { /// ``` pub gfm_footnote_clobber_prefix: Option, - /// Whether to support GFM strikethrough (if enabled in `constructs`) with - /// a single tilde (default: `true`). + /// Whether to support the GFM tagfilter, when `allow_dangerous_html` is on + /// (default: `false`). /// - /// Single tildes work on github.com but are technically prohibited by GFM. + /// The tagfilter is kinda weird and kinda useless. + /// The tag filter is a naïve attempt at XSS protection. + /// You should use a proper HTML sanitizing algorithm. /// /// ## Examples /// /// ``` - /// use micromark::{micromark, micromark_with_options, Options, Constructs}; + /// use micromark::{micromark_with_options, CompileOptions, Options, ParseOptions}; /// # fn main() -> Result<(), String> { /// - /// // micromark supports single tildes by default: + /// // With `allow_dangerous_html`, micromark passes HTML through untouched: /// assert_eq!( /// micromark_with_options( - /// "~a~", + /// "