diff options
author | Titus Wormer <tituswormer@gmail.com> | 2022-10-06 15:57:55 +0200 |
---|---|---|
committer | Titus Wormer <tituswormer@gmail.com> | 2022-10-06 15:57:55 +0200 |
commit | cd5bb2d16c6b28332b0b6077b27b2b90a8051896 (patch) | |
tree | 848ebc6200b80d2dfdcd67bf0bb245eea06bb24f /src | |
parent | 6e80e03bb6d6af47aba2b339f160e4895ab5afba (diff) | |
download | markdown-rs-cd5bb2d16c6b28332b0b6077b27b2b90a8051896.tar.gz markdown-rs-cd5bb2d16c6b28332b0b6077b27b2b90a8051896.tar.bz2 markdown-rs-cd5bb2d16c6b28332b0b6077b27b2b90a8051896.zip |
Refactor to split parse from compile options
Diffstat (limited to 'src')
-rw-r--r-- | src/lib.rs | 435 | ||||
-rw-r--r-- | src/parser.rs | 9 | ||||
-rw-r--r-- | src/to_html.rs | 8 |
3 files changed, 273 insertions, 179 deletions
@@ -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, <i>venus</i>!", /// &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( /// "<javascript:alert(1)>", /// &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?"), - /// "<pre><code>indented code?\n</code></pre>" - /// ); - /// - /// // 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() - /// } - /// )?, - /// "<p>indented code?</p>" - /// ); - /// # 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() /// )?, /// "<p><sup><a href=\"#user-content-fn-a\" id=\"user-content-fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h2 id=\"footnote-label\" class=\"sr-only\">Footnotes</h2>\n<ol>\n<li id=\"user-content-fn-a\">\n<p>b <a href=\"#user-content-fnref-a\" data-footnote-backref=\"\" aria-label=\"Back to content\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\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() + /// } /// } /// )?, /// "<p><sup><a href=\"#user-content-fn-a\" id=\"user-content-fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h2 id=\"footnote-label\" class=\"sr-only\">Notes de bas de page</h2>\n<ol>\n<li id=\"user-content-fn-a\">\n<p>b <a href=\"#user-content-fnref-a\" data-footnote-backref=\"\" aria-label=\"Back to content\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\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() /// )?, /// "<p><sup><a href=\"#user-content-fn-a\" id=\"user-content-fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h2 id=\"footnote-label\" class=\"sr-only\">Footnotes</h2>\n<ol>\n<li id=\"user-content-fn-a\">\n<p>b <a href=\"#user-content-fnref-a\" data-footnote-backref=\"\" aria-label=\"Back to content\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\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() + /// } /// } /// )?, /// "<p><sup><a href=\"#user-content-fn-a\" id=\"user-content-fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h1 id=\"footnote-label\" class=\"sr-only\">Footnotes</h1>\n<ol>\n<li id=\"user-content-fn-a\">\n<p>b <a href=\"#user-content-fnref-a\" data-footnote-backref=\"\" aria-label=\"Back to content\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\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() /// )?, /// "<p><sup><a href=\"#user-content-fn-a\" id=\"user-content-fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h2 id=\"footnote-label\" class=\"sr-only\">Footnotes</h2>\n<ol>\n<li id=\"user-content-fn-a\">\n<p>b <a href=\"#user-content-fnref-a\" data-footnote-backref=\"\" aria-label=\"Back to content\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\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() + /// } /// } /// )?, /// "<p><sup><a href=\"#user-content-fn-a\" id=\"user-content-fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h2 id=\"footnote-label\" class=\"footnote-heading\">Footnotes</h2>\n<ol>\n<li id=\"user-content-fn-a\">\n<p>b <a href=\"#user-content-fnref-a\" data-footnote-backref=\"\" aria-label=\"Back to content\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\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() /// )?, /// "<p><sup><a href=\"#user-content-fn-a\" id=\"user-content-fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h2 id=\"footnote-label\" class=\"sr-only\">Footnotes</h2>\n<ol>\n<li id=\"user-content-fn-a\">\n<p>b <a href=\"#user-content-fnref-a\" data-footnote-backref=\"\" aria-label=\"Back to content\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\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() + /// } /// } /// )?, /// "<p><sup><a href=\"#user-content-fn-a\" id=\"user-content-fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h2 id=\"footnote-label\" class=\"sr-only\">Footnotes</h2>\n<ol>\n<li id=\"user-content-fn-a\">\n<p>b <a href=\"#user-content-fnref-a\" data-footnote-backref=\"\" aria-label=\"Arrière\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\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() /// )?, /// "<p><sup><a href=\"#user-content-fn-a\" id=\"user-content-fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h2 id=\"footnote-label\" class=\"sr-only\">Footnotes</h2>\n<ol>\n<li id=\"user-content-fn-a\">\n<p>b <a href=\"#user-content-fnref-a\" data-footnote-backref=\"\" aria-label=\"Back to content\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\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() + /// } /// } /// )?, /// "<p><sup><a href=\"#fn-a\" id=\"fnref-a\" data-footnote-ref=\"\" aria-describedby=\"footnote-label\">1</a></sup></p>\n<section data-footnotes=\"\" class=\"footnotes\"><h2 id=\"footnote-label\" class=\"sr-only\">Footnotes</h2>\n<ol>\n<li id=\"fn-a\">\n<p>b <a href=\"#fnref-a\" data-footnote-backref=\"\" aria-label=\"Back to content\" class=\"data-footnote-backref\">↩</a></p>\n</li>\n</ol>\n</section>\n" @@ -902,94 +873,161 @@ pub struct Options { /// ``` pub gfm_footnote_clobber_prefix: Option<String>, - /// 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~", + /// "<iframe>", /// &Options { - /// constructs: Constructs::gfm(), - /// ..Options::default() + /// parse: ParseOptions::gfm(), + /// compile: CompileOptions { + /// allow_dangerous_html: true, + /// ..CompileOptions::default() + /// } /// } /// )?, - /// "<p><del>a</del></p>" + /// "<iframe>" /// ); /// - /// // Pass `gfm_strikethrough_single_tilde: false` to turn that off: + /// // Pass `gfm_tagfilter: true` to make some of that safe: /// assert_eq!( /// micromark_with_options( - /// "~a~", + /// "<iframe>", + /// &Options { + /// parse: ParseOptions::gfm(), + /// compile: CompileOptions { + /// allow_dangerous_html: true, + /// gfm_tagfilter: true, + /// ..CompileOptions::default() + /// } + /// } + /// )?, + /// "<iframe>" + /// ); + /// # Ok(()) + /// # } + /// ``` + /// + /// ## References + /// + /// * [*§ 6.1 Disallowed Raw HTML (extension)* in GFM](https://github.github.com/gfm/#disallowed-raw-html-extension-) + /// * [`cmark-gfm#extensions/tagfilter.c`](https://github.com/github/cmark-gfm/blob/master/extensions/tagfilter.c) + pub gfm_tagfilter: bool, +} + +impl CompileOptions { + /// GFM. + /// + /// <https://github.github.com/gfm/> + /// + /// This turns on the GFM tag filter (which is pretty useless). + #[must_use] + pub fn gfm() -> Self { + Self { + gfm_tagfilter: true, + ..Self::default() + } + } +} + +/// Configuration that describes how to parse from markdown. +#[allow(clippy::struct_excessive_bools)] +pub struct ParseOptions { + // Note: when adding fields, don’t forget to add them to `fmt::Debug` below. + /// Which constructs to enable and disable. + /// The default is to follow `CommonMark`. + /// + /// ## Examples + /// + /// ``` + /// use micromark::{micromark, micromark_with_options, Constructs, Options, ParseOptions}; + /// # fn main() -> Result<(), String> { + /// + /// // micromark follows CommonMark by default: + /// assert_eq!( + /// micromark(" indented code?"), + /// "<pre><code>indented code?\n</code></pre>" + /// ); + /// + /// // Pass `constructs` to choose what to enable and disable: + /// assert_eq!( + /// micromark_with_options( + /// " indented code?", /// &Options { - /// constructs: Constructs::gfm(), - /// gfm_strikethrough_single_tilde: false, + /// parse: ParseOptions { + /// constructs: Constructs { + /// code_indented: false, + /// ..Constructs::default() + /// }, + /// ..ParseOptions::default() + /// }, /// ..Options::default() /// } /// )?, - /// "<p>~a~</p>" + /// "<p>indented code?</p>" /// ); /// # Ok(()) /// # } /// ``` - pub gfm_strikethrough_single_tilde: bool, + pub constructs: Constructs, - /// Whether to support the GFM tagfilter, when `allow_dangerous_html` is on - /// (default: `false`). + /// Whether to support GFM strikethrough (if enabled in `constructs`) with + /// a single tilde (default: `true`). /// - /// 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. + /// Single tildes work on github.com but are technically prohibited by GFM. /// /// ## Examples /// /// ``` - /// use micromark::{micromark_with_options, Options, Constructs}; + /// use micromark::{micromark, micromark_with_options, Constructs, Options, ParseOptions}; /// # fn main() -> Result<(), String> { /// - /// // With `allow_dangerous_html`, micromark passes HTML through untouched: + /// // micromark supports single tildes by default: /// assert_eq!( /// micromark_with_options( - /// "<iframe>", + /// "~a~", /// &Options { - /// allow_dangerous_html: true, - /// constructs: Constructs::gfm(), + /// parse: ParseOptions { + /// constructs: Constructs::gfm(), + /// ..ParseOptions::default() + /// }, /// ..Options::default() /// } /// )?, - /// "<iframe>" + /// "<p><del>a</del></p>" /// ); /// - /// // Pass `gfm_tagfilter: true` to make some of that safe: + /// // Pass `gfm_strikethrough_single_tilde: false` to turn that off: /// assert_eq!( /// micromark_with_options( - /// "<iframe>", + /// "~a~", /// &Options { - /// allow_dangerous_html: true, - /// constructs: Constructs::gfm(), - /// gfm_tagfilter: true, + /// parse: ParseOptions { + /// constructs: Constructs::gfm(), + /// gfm_strikethrough_single_tilde: false, + /// ..ParseOptions::default() + /// }, /// ..Options::default() /// } /// )?, - /// "<iframe>" + /// "<p>~a~</p>" /// ); /// # Ok(()) /// # } /// ``` - /// - /// ## References - /// - /// * [*§ 6.1 Disallowed Raw HTML (extension)* in GFM](https://github.github.com/gfm/#disallowed-raw-html-extension-) - /// * [`cmark-gfm#extensions/tagfilter.c`](https://github.com/github/cmark-gfm/blob/master/extensions/tagfilter.c) - pub gfm_tagfilter: bool, + pub gfm_strikethrough_single_tilde: bool, /// Whether to support math (text) (if enabled in `constructs`) with a /// single dollar (default: `true`). @@ -1000,7 +1038,7 @@ pub struct Options { /// ## Examples /// /// ``` - /// use micromark::{micromark, micromark_with_options, Options, Constructs}; + /// use micromark::{micromark, micromark_with_options, Constructs, Options, ParseOptions}; /// # fn main() -> Result<(), String> { /// /// // micromark supports single dollars by default: @@ -1008,9 +1046,12 @@ pub struct Options { /// micromark_with_options( /// "$a$", /// &Options { - /// constructs: Constructs { + /// parse: ParseOptions { + /// constructs: Constructs { /// math_text: true, /// ..Constructs::default() + /// }, + /// ..ParseOptions::default() /// }, /// ..Options::default() /// } @@ -1023,11 +1064,14 @@ pub struct Options { /// micromark_with_options( /// "$a$", /// &Options { - /// constructs: Constructs { + /// parse: ParseOptions { + /// constructs: Constructs { /// math_text: true, /// ..Constructs::default() + /// }, + /// math_text_single_dollar: false, + /// ..ParseOptions::default() /// }, - /// math_text_single_dollar: false, /// ..Options::default() /// } /// )?, @@ -1040,6 +1084,9 @@ pub struct Options { /// Function to parse expressions with. /// + /// It only makes sense to pass this when compiling to a syntax tree + /// with [`micromark_to_mdast`][]. + /// /// This can be used to parse expressions with a parser. /// It can be used to support for arbitrary programming languages within /// expressions. @@ -1050,6 +1097,9 @@ pub struct Options { /// Function to parse ESM with. /// + /// It only makes sense to pass this when compiling to a syntax tree + /// with [`micromark_to_mdast`][]. + /// /// This can be used to parse ESM with a parser. /// It can be used to support for arbitrary programming languages within /// ESM, however, the keywords (`export`, `import`) are currently hardcoded @@ -1061,32 +1111,14 @@ pub struct Options { // Note: when adding fields, don’t forget to add them to `fmt::Debug` below. } -impl fmt::Debug for Options { +impl fmt::Debug for ParseOptions { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Options") - .field("allow_dangerous_html", &self.allow_dangerous_html) - .field("allow_dangerous_protocol", &self.allow_dangerous_protocol) + f.debug_struct("ParseOptions") .field("constructs", &self.constructs) - .field("default_line_ending", &self.default_line_ending) - .field("gfm_footnote_label", &self.gfm_footnote_label) - .field( - "gfm_footnote_label_tag_name", - &self.gfm_footnote_label_tag_name, - ) - .field( - "gfm_footnote_label_attributes", - &self.gfm_footnote_label_attributes, - ) - .field("gfm_footnote_back_label", &self.gfm_footnote_back_label) - .field( - "gfm_footnote_clobber_prefix", - &self.gfm_footnote_clobber_prefix, - ) .field( "gfm_strikethrough_single_tilde", &self.gfm_strikethrough_single_tilde, ) - .field("gfm_tagfilter", &self.gfm_tagfilter) .field("math_text_single_dollar", &self.math_text_single_dollar) .field( "mdx_expression_parse", @@ -1100,21 +1132,12 @@ impl fmt::Debug for Options { } } -impl Default for Options { - /// Safe `CommonMark` defaults. +impl Default for ParseOptions { + /// `CommonMark` defaults. fn default() -> Self { Self { - allow_dangerous_html: false, - allow_dangerous_protocol: false, constructs: Constructs::default(), - default_line_ending: LineEnding::default(), - gfm_footnote_label: None, - gfm_footnote_label_tag_name: None, - gfm_footnote_label_attributes: None, - gfm_footnote_back_label: None, - gfm_footnote_clobber_prefix: None, gfm_strikethrough_single_tilde: true, - gfm_tagfilter: false, math_text_single_dollar: true, mdx_expression_parse: None, mdx_esm_parse: None, @@ -1122,6 +1145,68 @@ impl Default for Options { } } +impl ParseOptions { + /// GFM. + /// + /// <https://github.github.com/gfm/> + /// + /// This turns on `CommonMark` + GFM. + #[must_use] + pub fn gfm() -> Self { + Self { + constructs: Constructs::gfm(), + ..Self::default() + } + } + + /// MDX. + /// + /// <https://mdxjs.com> + /// + /// This turns on `CommonMark`, turns off some conflicting constructs + /// (autolinks, code (indented), html), and turns on MDX (JSX, + /// expressions, ESM). + /// + /// > 👉 **Note**: you *must* pass [`mdx_esm_parse`][MdxEsmParse] + /// > too to support ESM. + /// > You *can* pass + /// > [`mdx_expression_parse`][MdxExpressionParse] + /// > to parse expressions according to a certain grammar (typically, a + /// > programming language). + #[must_use] + pub fn mdx() -> Self { + Self { + constructs: Constructs::mdx(), + ..Self::default() + } + } +} + +/// Configuration (optional). +#[allow(clippy::struct_excessive_bools)] +#[derive(Debug, Default)] +pub struct Options { + /// Configuration that describes how to parse from markdown. + pub parse: ParseOptions, + /// Configuration that describes how to compile to HTML. + pub compile: CompileOptions, +} + +impl Options { + /// GFM. + /// + /// <https://github.github.com/gfm/> + /// + /// This turns on `CommonMark` + GFM. + #[must_use] + pub fn gfm() -> Self { + Self { + parse: ParseOptions::gfm(), + compile: CompileOptions::gfm(), + } + } +} + /// Turn markdown into HTML. /// /// ## Examples @@ -1150,12 +1235,15 @@ pub fn micromark(value: &str) -> String { /// ## Examples /// /// ``` -/// use micromark::{micromark_with_options, Options}; +/// use micromark::{micromark_with_options, CompileOptions, Options}; /// # fn main() -> Result<(), String> { /// /// let result = micromark_with_options("<div>\n\n# Hello, world!\n\n</div>", &Options { -/// allow_dangerous_html: true, -/// allow_dangerous_protocol: true, +/// compile: CompileOptions { +/// allow_dangerous_html: true, +/// allow_dangerous_protocol: true, +/// ..CompileOptions::default() +/// }, /// ..Options::default() /// })?; /// @@ -1164,8 +1252,8 @@ pub fn micromark(value: &str) -> String { /// # } /// ``` pub fn micromark_with_options(value: &str, options: &Options) -> Result<String, String> { - let (events, bytes) = parse(value, options)?; - Ok(to_html(&events, bytes, options)) + let (events, bytes) = parse(value, &options.parse)?; + Ok(to_html(&events, bytes, &options.compile)) } /// Turn markdown into a syntax tree. @@ -1181,16 +1269,16 @@ pub fn micromark_with_options(value: &str, options: &Options) -> Result<String, /// ## Examples /// /// ``` -/// use micromark::{micromark_to_mdast, Options}; +/// use micromark::{micromark_to_mdast, ParseOptions}; /// # fn main() -> Result<(), String> { /// -/// let tree = micromark_to_mdast("# hi!", &Options::default())?; +/// let tree = micromark_to_mdast("# hi!", &ParseOptions::default())?; /// /// println!("{:?}", tree); /// # Ok(()) /// # } /// ``` -pub fn micromark_to_mdast(value: &str, options: &Options) -> Result<Node, String> { +pub fn micromark_to_mdast(value: &str, options: &ParseOptions) -> Result<Node, String> { let (events, bytes) = parse(value, options)?; let node = to_mdast(&events, bytes)?; Ok(node) @@ -1198,18 +1286,21 @@ pub fn micromark_to_mdast(value: &str, options: &Options) -> Result<Node, String /// Do not use: exported for quick prototyping, will be removed. #[must_use] +#[doc(hidden)] pub fn sanitize_(value: &str) -> String { sanitize(value) } /// Do not use: exported for quick prototyping, will be removed. #[must_use] +#[doc(hidden)] pub fn id_start_(char: char) -> bool { id_start(char) } /// Do not use: exported for quick prototyping, will be removed. #[must_use] +#[doc(hidden)] pub fn id_cont_(char: char, jsx: bool) -> bool { id_cont(char, jsx) } diff --git a/src/parser.rs b/src/parser.rs index c69eb38..b694bc5 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -4,7 +4,7 @@ use crate::event::{Event, Point}; use crate::state::{Name as StateName, State}; use crate::subtokenize::subtokenize; use crate::tokenizer::Tokenizer; -use crate::Options; +use crate::ParseOptions; use alloc::{string::String, vec, vec::Vec}; /// Info needed, in all content types, when parsing markdown. @@ -14,7 +14,7 @@ use alloc::{string::String, vec, vec::Vec}; #[derive(Debug)] pub struct ParseState<'a> { /// Configuration. - pub options: &'a Options, + pub options: &'a ParseOptions, /// List of chars. pub bytes: &'a [u8], /// Set of defined definition identifiers. @@ -26,7 +26,10 @@ pub struct ParseState<'a> { /// Turn a string of markdown into events. /// /// Passes the bytes back so the compiler can access the source. -pub fn parse<'a>(value: &'a str, options: &'a Options) -> Result<(Vec<Event>, &'a [u8]), String> { +pub fn parse<'a>( + value: &'a str, + options: &'a ParseOptions, +) -> Result<(Vec<Event>, &'a [u8]), String> { let mut parse_state = ParseState { options, bytes: value.as_bytes(), diff --git a/src/to_html.rs b/src/to_html.rs index 814f7cf..a0ab152 100644 --- a/src/to_html.rs +++ b/src/to_html.rs @@ -12,7 +12,7 @@ use crate::util::{ skip, slice::{Position, Slice}, }; -use crate::{LineEnding, Options}; +use crate::{CompileOptions, LineEnding}; use alloc::{ format, string::{String, ToString}, @@ -81,7 +81,7 @@ struct CompileContext<'a> { /// List of bytes. bytes: &'a [u8], /// Configuration. - options: &'a Options, + options: &'a CompileOptions, // Fields used by handlers to track the things they need to track to // compile markdown. /// Rank of heading (atx). @@ -136,7 +136,7 @@ impl<'a> CompileContext<'a> { fn new( events: &'a [Event], bytes: &'a [u8], - options: &'a Options, + options: &'a CompileOptions, line_ending: LineEnding, ) -> CompileContext<'a> { CompileContext { @@ -208,7 +208,7 @@ impl<'a> CompileContext<'a> { } /// Turn events and bytes into a string of HTML. -pub fn compile(events: &[Event], bytes: &[u8], options: &Options) -> String { +pub fn compile(events: &[Event], bytes: &[u8], options: &CompileOptions) -> String { let mut index = 0; let mut line_ending_inferred = None; |