base_markdown/configuration.rs
1// Released under the MIT License.
2// Copyright, 2022, by Bernhard Berger.
3// Copyright, 2022-2025, by Titus Wormer.
4// Copyright, 2023, by cel.
5// Copyright, 2025, by Ophir Lojkine.
6// Copyright, 2026, by Samuel Williams.
7
8use crate::util::line_ending::LineEnding;
9use alloc::string::String;
10
11/// Control which constructs are enabled.
12///
13/// Not all constructs can be configured.
14/// Notably, blank lines and paragraphs cannot be turned off.
15///
16/// ## Examples
17///
18/// ```
19/// use base_markdown::Constructs;
20/// # fn main() {
21///
22/// // Use the default trait to get `CommonMark` constructs:
23/// let commonmark = Constructs::default();
24///
25/// // To turn on all of GFM, use the `gfm` method:
26/// let gfm = Constructs::gfm();
27///
28/// // Or, mix and match:
29/// let custom = Constructs {
30/// math_flow: true,
31/// math_text: true,
32/// ..Constructs::gfm()
33/// };
34/// # }
35/// ```
36#[allow(clippy::struct_excessive_bools)]
37#[derive(Clone, Debug, Eq, PartialEq)]
38#[cfg_attr(
39 feature = "serde",
40 derive(serde::Serialize, serde::Deserialize),
41 serde(rename_all = "camelCase")
42)]
43pub struct Constructs {
44 /// Attention.
45 ///
46 /// ```markdown
47 /// > | a *b* c **d**.
48 /// ^^^ ^^^^^
49 /// ```
50 pub attention: bool,
51 /// Autolink.
52 ///
53 /// ```markdown
54 /// > | a <https://example.com> b <user@example.org>.
55 /// ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^
56 /// ```
57 pub autolink: bool,
58 /// Block quote.
59 ///
60 /// ```markdown
61 /// > | > a
62 /// ^^^
63 /// ```
64 pub block_quote: bool,
65 /// Character escape.
66 ///
67 /// ```markdown
68 /// > | a \* b
69 /// ^^
70 /// ```
71 pub character_escape: bool,
72 /// Character reference.
73 ///
74 /// ```markdown
75 /// > | a & b
76 /// ^^^^^
77 /// ```
78 pub character_reference: bool,
79 /// Code (indented).
80 ///
81 /// ```markdown
82 /// > | a
83 /// ^^^^^
84 /// ```
85 pub code_indented: bool,
86 /// Code (fenced).
87 ///
88 /// ```markdown
89 /// > | ~~~js
90 /// ^^^^^
91 /// > | console.log(1)
92 /// ^^^^^^^^^^^^^^
93 /// > | ~~~
94 /// ^^^
95 /// ```
96 pub code_fenced: bool,
97 /// Code (text).
98 ///
99 /// ```markdown
100 /// > | a `b` c
101 /// ^^^
102 /// ```
103 pub code_text: bool,
104 /// Definition.
105 ///
106 /// ```markdown
107 /// > | [a]: b "c"
108 /// ^^^^^^^^^^
109 /// ```
110 pub definition: bool,
111 /// Frontmatter with an unindented opening fence on the first document line,
112 /// outside list items and blockquotes, and a matching closing fence.
113 ///
114 /// All forms produce a source-backed opaque node with [`NodeSyntax::Frontmatter`][crate::NodeSyntax::Frontmatter] metadata.
115 /// Untagged `---` and `+++` imply YAML and TOML. A format hint after either
116 /// delimiter, or a language-tagged backtick/tilde code fence, supplies an
117 /// explicit language. The parser preserves the raw body and authored info
118 /// string; HTML omits the frontmatter.
119 ///
120 /// ````markdown
121 /// > | ---
122 /// ^^^
123 /// > | title: Neptune
124 /// ^^^^^^^^^^^^^^
125 /// > | ---
126 /// ^^^
127 /// ````
128 pub frontmatter: bool,
129 /// Autolink literal.
130 ///
131 /// ```markdown
132 /// > | https://example.com
133 /// ^^^^^^^^^^^^^^^^^^^
134 /// ```
135 pub autolink_literal: bool,
136 /// Footnote definition.
137 ///
138 /// ```markdown
139 /// > | [^a]: b
140 /// ^^^^^^^
141 /// ```
142 pub footnote_definition: bool,
143 /// Footnote label start.
144 ///
145 /// ```markdown
146 /// > | a[^b]
147 /// ^^
148 /// ```
149 pub label_start_footnote: bool,
150 /// Strikethrough.
151 ///
152 /// ```markdown
153 /// > | a ~b~ c.
154 /// ^^^
155 /// ```
156 pub strikethrough: bool,
157 /// Table.
158 ///
159 /// ```markdown
160 /// > | | a |
161 /// ^^^^^
162 /// > | | - |
163 /// ^^^^^
164 /// > | | b |
165 /// ^^^^^
166 /// ```
167 pub table: bool,
168 /// Task list item.
169 ///
170 /// ```markdown
171 /// > | * [x] y.
172 /// ^^^
173 /// ```
174 pub task_list_item: bool,
175 /// Hard break (escape).
176 ///
177 /// ```markdown
178 /// > | a\
179 /// ^
180 /// | b
181 /// ```
182 pub hard_break_escape: bool,
183 /// Hard break (trailing).
184 ///
185 /// ```markdown
186 /// > | a␠␠
187 /// ^^
188 /// | b
189 /// ```
190 pub hard_break_trailing: bool,
191 /// Heading (atx).
192 ///
193 /// ```markdown
194 /// > | # a
195 /// ^^^
196 /// ```
197 pub heading_atx: bool,
198 /// Heading (setext).
199 ///
200 /// ```markdown
201 /// > | a
202 /// ^^
203 /// > | ==
204 /// ^^
205 /// ```
206 pub heading_setext: bool,
207 /// HTML (flow).
208 ///
209 /// ```markdown
210 /// > | <div>
211 /// ^^^^^
212 /// ```
213 pub html_flow: bool,
214 /// HTML (text).
215 ///
216 /// ```markdown
217 /// > | a <b> c
218 /// ^^^
219 /// ```
220 pub html_text: bool,
221 /// Label start (image).
222 ///
223 /// ```markdown
224 /// > | a  d
225 /// ^^
226 /// ```
227 pub label_start_image: bool,
228 /// Label start (link).
229 ///
230 /// ```markdown
231 /// > | a [b](c) d
232 /// ^
233 /// ```
234 pub label_start_link: bool,
235 /// Label end.
236 ///
237 /// ```markdown
238 /// > | a [b](c) d
239 /// ^^^^
240 /// ```
241 pub label_end: bool,
242 /// List items.
243 ///
244 /// ```markdown
245 /// > | * a
246 /// ^^^
247 /// ```
248 pub list_item: bool,
249 /// Math (flow).
250 ///
251 /// ```markdown
252 /// > | $$
253 /// ^^
254 /// > | \frac{1}{2}
255 /// ^^^^^^^^^^^
256 /// > | $$
257 /// ^^
258 /// ```
259 pub math_flow: bool,
260 /// Math (text).
261 ///
262 /// ```markdown
263 /// > | a $b$ c
264 /// ^^^
265 /// ```
266 pub math_text: bool,
267
268 /// Thematic break.
269 ///
270 /// ```markdown
271 /// > | ***
272 /// ^^^
273 /// ```
274 pub thematic_break: bool,
275}
276
277impl Default for Constructs {
278 /// `CommonMark`.
279 ///
280 /// `CommonMark` is a relatively strong specification of how markdown
281 /// works.
282 /// Most markdown parsers try to follow it.
283 ///
284 /// For more information, see the `CommonMark` specification:
285 /// <https://spec.commonmark.org>.
286 fn default() -> Self {
287 Self {
288 attention: true,
289 autolink: true,
290 block_quote: true,
291 character_escape: true,
292 character_reference: true,
293 code_indented: true,
294 code_fenced: true,
295 code_text: true,
296 definition: true,
297 frontmatter: false,
298 autolink_literal: false,
299 label_start_footnote: false,
300 footnote_definition: false,
301 strikethrough: false,
302 table: false,
303 task_list_item: false,
304 hard_break_escape: true,
305 hard_break_trailing: true,
306 heading_atx: true,
307 heading_setext: true,
308 html_flow: true,
309 html_text: true,
310 label_start_image: true,
311 label_start_link: true,
312 label_end: true,
313 list_item: true,
314 math_flow: false,
315 math_text: false,
316
317 thematic_break: true,
318 }
319 }
320}
321
322impl Constructs {
323 /// GFM.
324 ///
325 /// GFM stands for **GitHub flavored markdown**.
326 /// GFM extends `CommonMark` and adds support for autolink literals,
327 /// footnotes, strikethrough, tables, and tasklists.
328 ///
329 /// For more information, see the GFM specification:
330 /// <https://github.github.com/gfm/>.
331 pub fn gfm() -> Self {
332 Self {
333 autolink_literal: true,
334 footnote_definition: true,
335 label_start_footnote: true,
336 strikethrough: true,
337 table: true,
338 task_list_item: true,
339 ..Self::default()
340 }
341 }
342}
343
344/// Configuration that describes how to compile to HTML.
345///
346/// You likely either want to turn on the dangerous options
347/// (`allow_dangerous_html`, `allow_dangerous_protocol`) when dealing with
348/// input you trust, or want to customize how footnotes are compiled
349/// (typically because the input markdown is not in English).
350///
351/// ## Examples
352///
353/// ```
354/// use base_markdown::CompileOptions;
355/// # fn main() {
356///
357/// // Use the default trait to get safe defaults:
358/// let safe = CompileOptions::default();
359///
360/// // Live dangerously / trust the author:
361/// let danger = CompileOptions {
362/// allow_dangerous_html: true,
363/// allow_dangerous_protocol: true,
364/// ..CompileOptions::default()
365/// };
366///
367/// // In French:
368/// let enFrançais = CompileOptions {
369/// footnote_back_label: Some("Arrière".into()),
370/// footnote_label: Some("Notes de bas de page".into()),
371/// ..CompileOptions::default()
372/// };
373/// # }
374/// ```
375#[allow(clippy::struct_excessive_bools)]
376#[derive(Clone, Debug, Default)]
377#[cfg_attr(
378 feature = "serde",
379 derive(serde::Serialize, serde::Deserialize),
380 serde(default, rename_all = "camelCase")
381)]
382pub struct CompileOptions {
383 /// Whether to allow all values in images.
384 ///
385 /// The default is `false`,
386 /// which lets `allow_dangerous_protocol` control protocol safety for
387 /// both links and images.
388 ///
389 /// Pass `true` to allow all values as `src` on images,
390 /// regardless of `allow_dangerous_protocol`.
391 /// This is safe because the
392 /// [HTML specification][whatwg-html-image-processing]
393 /// does not allow executable code in images.
394 ///
395 /// [whatwg-html-image-processing]: https://html.spec.whatwg.org/multipage/images.html#images-processing-model
396 ///
397 /// ## Examples
398 ///
399 /// ```
400 /// use base_markdown::{to_html_with_options, CompileOptions, Options};
401 ///
402 /// // By default, some protocols in image sources are dropped:
403 /// assert_eq!(
404 /// to_html_with_options(
405 /// "",
406 /// &Options::default()
407 /// ),
408 /// "<p><img src=\"\" alt=\"\" /></p>"
409 /// );
410 ///
411 /// // Turn `allow_any_img_src` on to allow all values as `src` on images.
412 /// // This is safe because browsers do not execute code in images.
413 /// assert_eq!(
414 /// to_html_with_options(
415 /// ")",
416 /// &Options {
417 /// compile: CompileOptions {
418 /// allow_any_img_src: true,
419 /// ..CompileOptions::default()
420 /// },
421 /// ..Options::default()
422 /// }
423 /// ),
424 /// "<p><img src=\"javascript:alert(1)\" alt=\"\" /></p>"
425 /// );
426 /// ```
427 pub allow_any_img_src: bool,
428
429 /// Whether to allow (dangerous) HTML.
430 ///
431 /// The default is `false`, which still parses the HTML according to
432 /// `CommonMark` but shows the HTML as text instead of as elements.
433 ///
434 /// Pass `true` for trusted content to get actual HTML elements.
435 ///
436 /// When using GFM, make sure to also turn off `tagfilter`.
437 /// Otherwise, some dangerous HTML is still ignored.
438 ///
439 /// ## Examples
440 ///
441 /// ```
442 /// use base_markdown::{to_html, to_html_with_options, CompileOptions, Options};
443 ///
444 /// // `markdown-rs` is safe by default:
445 /// assert_eq!(
446 /// to_html("Hi, <i>venus</i>!"),
447 /// "<p>Hi, <i>venus</i>!</p>"
448 /// );
449 ///
450 /// // Turn `allow_dangerous_html` on to allow potentially dangerous HTML:
451 /// assert_eq!(
452 /// to_html_with_options(
453 /// "Hi, <i>venus</i>!",
454 /// &Options {
455 /// compile: CompileOptions {
456 /// allow_dangerous_html: true,
457 /// ..CompileOptions::default()
458 /// },
459 /// ..Options::default()
460 /// }
461 /// ),
462 /// "<p>Hi, <i>venus</i>!</p>"
463 /// );
464 /// ```
465 pub allow_dangerous_html: bool,
466
467 /// Whether to allow dangerous protocols in links and images.
468 ///
469 /// The default is `false`, which drops URLs in links and images that use
470 /// dangerous protocols.
471 ///
472 /// Pass `true` for trusted content to support all protocols.
473 ///
474 /// URLs that have no protocol (which means it’s relative to the current
475 /// page, such as `./some/page.html`) and URLs that have a safe protocol
476 /// (for images: `http`, `https`; for links: `http`, `https`, `irc`,
477 /// `ircs`, `mailto`, `xmpp`), are safe.
478 /// All other URLs are dangerous and dropped.
479 ///
480 /// When the option `allow_all_protocols_in_img` is enabled,
481 /// `allow_dangerous_protocol` only applies to links.
482 ///
483 /// This is safe because the
484 /// [HTML specification][whatwg-html-image-processing]
485 /// does not allow executable code in images.
486 /// All modern browsers respect this.
487 ///
488 /// [whatwg-html-image-processing]: https://html.spec.whatwg.org/multipage/images.html#images-processing-model
489 ///
490 /// ## Examples
491 ///
492 /// ```
493 /// use base_markdown::{to_html, to_html_with_options, CompileOptions, Options};
494 ///
495 /// // `markdown-rs` is safe by default:
496 /// assert_eq!(
497 /// to_html("<javascript:alert(1)>"),
498 /// "<p><a href=\"\">javascript:alert(1)</a></p>"
499 /// );
500 ///
501 /// // Turn `allow_dangerous_protocol` on to allow potentially dangerous protocols:
502 /// assert_eq!(
503 /// to_html_with_options(
504 /// "<javascript:alert(1)>",
505 /// &Options {
506 /// compile: CompileOptions {
507 /// allow_dangerous_protocol: true,
508 /// ..CompileOptions::default()
509 /// },
510 /// ..Options::default()
511 /// }
512 /// ),
513 /// "<p><a href=\"javascript:alert(1)\">javascript:alert(1)</a></p>"
514 /// );
515 /// ```
516 pub allow_dangerous_protocol: bool,
517
518 // To do: `doc_markdown` is broken.
519 #[allow(clippy::doc_markdown)]
520 /// Default line ending to use when compiling to HTML, for line endings not
521 /// in `value`.
522 ///
523 /// Generally, `markdown-rs` copies line endings (`\r`, `\n`, `\r\n`) in
524 /// the markdown document over to the compiled HTML.
525 /// In some cases, such as `> a`, CommonMark requires that extra line
526 /// endings are added: `<blockquote>\n<p>a</p>\n</blockquote>`.
527 ///
528 /// To create that line ending, the document is checked for the first line
529 /// ending that is used.
530 /// If there is no line ending, `default_line_ending` is used.
531 /// If that isn’t configured, `\n` is used.
532 ///
533 /// ## Examples
534 ///
535 /// ```
536 /// use base_markdown::{to_html, to_html_with_options, CompileOptions, LineEnding, Options};
537 ///
538 /// // `markdown-rs` uses `\n` by default:
539 /// assert_eq!(
540 /// to_html("> a"),
541 /// "<blockquote>\n<p>a</p>\n</blockquote>"
542 /// );
543 ///
544 /// // Define `default_line_ending` to configure the default:
545 /// assert_eq!(
546 /// to_html_with_options(
547 /// "> a",
548 /// &Options {
549 /// compile: CompileOptions {
550 /// default_line_ending: LineEnding::CarriageReturnLineFeed,
551 /// ..CompileOptions::default()
552 /// },
553 /// ..Options::default()
554 /// }
555 /// ),
556 /// "<blockquote>\r\n<p>a</p>\r\n</blockquote>"
557 /// );
558 /// ```
559 pub default_line_ending: LineEnding,
560
561 /// Textual label to describe the backreference back to footnote calls.
562 ///
563 /// The default value is `"Back to content"`.
564 /// Change it when the markdown is not in English.
565 ///
566 /// This label is used in the `aria-label` attribute on each backreference
567 /// (the `↩` links).
568 /// It affects users of assistive technology.
569 ///
570 /// ## Examples
571 ///
572 /// ```
573 /// use base_markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
574 ///
575 /// // `"Back to content"` is used by default:
576 /// assert_eq!(
577 /// to_html_with_options(
578 /// "[^a]\n\n[^a]: b",
579 /// &Options::gfm()
580 /// ),
581 /// "<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"
582 /// );
583 ///
584 /// // Pass `footnote_back_label` to use something else:
585 /// assert_eq!(
586 /// to_html_with_options(
587 /// "[^a]\n\n[^a]: b",
588 /// &Options {
589 /// parse: ParseOptions::gfm(),
590 /// compile: CompileOptions {
591 /// footnote_back_label: Some("Arrière".into()),
592 /// ..CompileOptions::gfm()
593 /// }
594 /// }
595 /// ),
596 /// "<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"
597 /// );
598 /// ```
599 pub footnote_back_label: Option<String>,
600
601 /// Prefix to use before the `id` attribute on footnotes to prevent them
602 /// from *clobbering*.
603 ///
604 /// The default is `"user-content-"`.
605 /// Pass `Some("".into())` for trusted markdown and when you are careful
606 /// with polyfilling.
607 /// You could pass a different prefix.
608 ///
609 /// DOM clobbering is this:
610 ///
611 /// ```html
612 /// <p id="x"></p>
613 /// <script>alert(x) // `x` now refers to the `p#x` DOM element</script>
614 /// ```
615 ///
616 /// The above example shows that elements are made available by browsers,
617 /// by their ID, on the `window` object.
618 /// This is a security risk because you might be expecting some other
619 /// variable at that place.
620 /// It can also break polyfills.
621 /// Using a prefix solves these problems.
622 ///
623 /// ## Examples
624 ///
625 /// ```
626 /// use base_markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
627 ///
628 /// // `"user-content-"` is used by default:
629 /// assert_eq!(
630 /// to_html_with_options(
631 /// "[^a]\n\n[^a]: b",
632 /// &Options::gfm()
633 /// ),
634 /// "<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"
635 /// );
636 ///
637 /// // Pass `footnote_clobber_prefix` to use something else:
638 /// assert_eq!(
639 /// to_html_with_options(
640 /// "[^a]\n\n[^a]: b",
641 /// &Options {
642 /// parse: ParseOptions::gfm(),
643 /// compile: CompileOptions {
644 /// footnote_clobber_prefix: Some("".into()),
645 /// ..CompileOptions::gfm()
646 /// }
647 /// }
648 /// ),
649 /// "<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"
650 /// );
651 /// ```
652 pub footnote_clobber_prefix: Option<String>,
653
654 /// Attributes to use on the footnote label.
655 ///
656 /// The default value is `"class=\"sr-only\""`.
657 /// Change it to show the label and add other attributes.
658 ///
659 /// This label is typically hidden visually (assuming a `sr-only` CSS class
660 /// is defined that does that), and thus affects screen readers only.
661 /// If you do have such a class, but want to show this section to everyone,
662 /// pass an empty string.
663 /// You can also add different attributes.
664 ///
665 /// > 👉 **Note**: `id="footnote-label"` is always added, because footnote
666 /// > calls use it with `aria-describedby` to provide an accessible label.
667 ///
668 /// ## Examples
669 ///
670 /// ```
671 /// use base_markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
672 ///
673 /// // `"class=\"sr-only\""` is used by default:
674 /// assert_eq!(
675 /// to_html_with_options(
676 /// "[^a]\n\n[^a]: b",
677 /// &Options::gfm()
678 /// ),
679 /// "<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"
680 /// );
681 ///
682 /// // Pass `footnote_label_attributes` to use something else:
683 /// assert_eq!(
684 /// to_html_with_options(
685 /// "[^a]\n\n[^a]: b",
686 /// &Options {
687 /// parse: ParseOptions::gfm(),
688 /// compile: CompileOptions {
689 /// footnote_label_attributes: Some("class=\"footnote-heading\"".into()),
690 /// ..CompileOptions::gfm()
691 /// }
692 /// }
693 /// ),
694 /// "<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"
695 /// );
696 /// ```
697 pub footnote_label_attributes: Option<String>,
698
699 /// HTML tag name to use for the footnote label element.
700 ///
701 /// The default value is `"h2"`.
702 /// Change it to match your document structure.
703 ///
704 /// This label is typically hidden visually (assuming a `sr-only` CSS class
705 /// is defined that does that), and thus affects screen readers only.
706 /// If you do have such a class, but want to show this section to everyone,
707 /// pass different attributes with the `footnote_label_attributes`
708 /// option.
709 ///
710 /// ## Examples
711 ///
712 /// ```
713 /// use base_markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
714 ///
715 /// // `"h2"` is used by default:
716 /// assert_eq!(
717 /// to_html_with_options(
718 /// "[^a]\n\n[^a]: b",
719 /// &Options::gfm()
720 /// ),
721 /// "<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"
722 /// );
723 ///
724 /// // Pass `footnote_label_tag_name` to use something else:
725 /// assert_eq!(
726 /// to_html_with_options(
727 /// "[^a]\n\n[^a]: b",
728 /// &Options {
729 /// parse: ParseOptions::gfm(),
730 /// compile: CompileOptions {
731 /// footnote_label_tag_name: Some("h1".into()),
732 /// ..CompileOptions::gfm()
733 /// }
734 /// }
735 /// ),
736 /// "<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"
737 /// );
738 /// ```
739 pub footnote_label_tag_name: Option<String>,
740
741 /// Textual label to use for the footnotes section.
742 ///
743 /// The default value is `"Footnotes"`.
744 /// Change it when the markdown is not in English.
745 ///
746 /// This label is typically hidden visually (assuming a `sr-only` CSS class
747 /// is defined that does that), and thus affects screen readers only.
748 /// If you do have such a class, but want to show this section to everyone,
749 /// pass different attributes with the `footnote_label_attributes`
750 /// option.
751 ///
752 /// ## Examples
753 ///
754 /// ```
755 /// use base_markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
756 ///
757 /// // `"Footnotes"` is used by default:
758 /// assert_eq!(
759 /// to_html_with_options(
760 /// "[^a]\n\n[^a]: b",
761 /// &Options::gfm()
762 /// ),
763 /// "<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"
764 /// );
765 ///
766 /// // Pass `footnote_label` to use something else:
767 /// assert_eq!(
768 /// to_html_with_options(
769 /// "[^a]\n\n[^a]: b",
770 /// &Options {
771 /// parse: ParseOptions::gfm(),
772 /// compile: CompileOptions {
773 /// footnote_label: Some("Notes de bas de page".into()),
774 /// ..CompileOptions::gfm()
775 /// }
776 /// }
777 /// ),
778 /// "<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"
779 /// );
780 /// ```
781 pub footnote_label: Option<String>,
782
783 /// Whether or not task list HTML `<input>` items are enabled.
784 ///
785 /// This determines whether or not the user of the browser is able
786 /// to click and toggle generated checkbox items. The default is false.
787 ///
788 /// ## Examples
789 ///
790 /// ```
791 /// use base_markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
792 ///
793 /// // With `task_list_item_checkable`, generated `<input type="checkbox" />`
794 /// // tags do not contain the attribute `disabled=""` and are thus toggleable by
795 /// // browser users.
796 /// assert_eq!(
797 /// to_html_with_options(
798 /// "* [x] y.",
799 /// &Options {
800 /// parse: ParseOptions::gfm(),
801 /// compile: CompileOptions {
802 /// task_list_item_checkable: true,
803 /// ..CompileOptions::gfm()
804 /// }
805 /// }
806 /// ),
807 /// "<ul>\n<li><input type=\"checkbox\" checked=\"\" /> y.</li>\n</ul>"
808 /// );
809 /// ```
810 pub task_list_item_checkable: bool,
811
812 /// Whether to filter disallowed raw HTML tags.
813 ///
814 /// This option does nothing if `allow_dangerous_html` is not turned on.
815 /// The default is `false`, which does not filter raw HTML tags.
816 /// Pass `true` for output that is a bit closer to GitHub’s actual output.
817 ///
818 /// The tagfilter is kinda weird and kinda useless.
819 /// The tag filter is a naïve attempt at XSS protection.
820 /// You should use a proper HTML sanitizing algorithm instead.
821 ///
822 /// ## Examples
823 ///
824 /// ```
825 /// use base_markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
826 ///
827 /// // With `allow_dangerous_html`, `markdown-rs` passes HTML through untouched:
828 /// assert_eq!(
829 /// to_html_with_options(
830 /// "<iframe>",
831 /// &Options {
832 /// parse: ParseOptions::gfm(),
833 /// compile: CompileOptions {
834 /// allow_dangerous_html: true,
835 /// ..CompileOptions::default()
836 /// }
837 /// }
838 /// ),
839 /// "<iframe>"
840 /// );
841 ///
842 /// // Pass `tagfilter: true` to make some of that safe:
843 /// assert_eq!(
844 /// to_html_with_options(
845 /// "<iframe>",
846 /// &Options {
847 /// parse: ParseOptions::gfm(),
848 /// compile: CompileOptions {
849 /// allow_dangerous_html: true,
850 /// tagfilter: true,
851 /// ..CompileOptions::default()
852 /// }
853 /// }
854 /// ),
855 /// "<iframe>"
856 /// );
857 /// ```
858 ///
859 /// ## References
860 ///
861 /// * [*§ 6.1 Disallowed Raw HTML (extension)* in GFM](https://github.github.com/gfm/#disallowed-raw-html-extension-)
862 /// * [`cmark-gfm#extensions/tagfilter.c`](https://github.com/github/cmark-gfm/blob/master/extensions/tagfilter.c)
863 pub tagfilter: bool,
864
865 /// Whether to add unique `id` attributes to headings for in-page links
866 /// and tables of contents.
867 ///
868 /// IDs use the same text normalization and duplicate suffixes as
869 /// [`Document::headings`][crate::Document::headings].
870 ///
871 /// The default is `false`.
872 pub heading_ids: bool,
873}
874
875impl CompileOptions {
876 /// GFM.
877 ///
878 /// GFM stands for **GitHub flavored markdown**.
879 /// On the compilation side, the GFM preset turns on the tagfilter.
880 /// The tagfilter is useless, but it’s included here for consistency, and
881 /// this method exists for parity to parse options.
882 ///
883 /// For more information, see the GFM specification:
884 /// <https://github.github.com/gfm/>.
885 pub fn gfm() -> Self {
886 Self {
887 tagfilter: true,
888 ..Self::default()
889 }
890 }
891}
892
893/// Configuration that describes how to parse from markdown.
894///
895/// You can use this:
896///
897/// * To control what markdown constructs are turned on and off
898/// * To control some of those constructs
899///
900/// In most cases, you will want to use the default trait or `gfm` method.
901///
902/// ## Examples
903///
904/// ```
905/// use base_markdown::ParseOptions;
906/// # fn main() {
907///
908/// // Use the default trait to parse markdown according to `CommonMark`:
909/// let commonmark = ParseOptions::default();
910///
911/// // Use the `gfm` method to parse markdown according to GFM:
912/// let gfm = ParseOptions::gfm();
913/// # }
914/// ```
915#[allow(clippy::struct_excessive_bools)]
916#[cfg_attr(
917 feature = "serde",
918 derive(serde::Serialize, serde::Deserialize),
919 serde(default, rename_all = "camelCase")
920)]
921#[derive(Debug)]
922pub struct ParseOptions {
923 /// Which constructs to enable and disable.
924 ///
925 /// The default is to follow `CommonMark`.
926 ///
927 /// ## Examples
928 ///
929 /// ```
930 /// use base_markdown::{to_html, to_html_with_options, Constructs, Options, ParseOptions};
931 ///
932 /// // `markdown-rs` follows CommonMark by default:
933 /// assert_eq!(
934 /// to_html(" indented code?"),
935 /// "<pre><code>indented code?\n</code></pre>"
936 /// );
937 ///
938 /// // Pass `constructs` to choose what to enable and disable:
939 /// assert_eq!(
940 /// to_html_with_options(
941 /// " indented code?",
942 /// &Options {
943 /// parse: ParseOptions {
944 /// constructs: Constructs {
945 /// code_indented: false,
946 /// ..Constructs::default()
947 /// },
948 /// ..ParseOptions::default()
949 /// },
950 /// ..Options::default()
951 /// }
952 /// ),
953 /// "<p>indented code?</p>"
954 /// );
955 /// ```
956 #[cfg_attr(feature = "serde", serde(default))]
957 pub constructs: Constructs,
958
959 /// Whether to parse a language prefix, such as `ruby:`, before inline code.
960 ///
961 /// When enabled, the prefix is removed from the surrounding text and
962 /// stored as the inline code node's `lang` value. The default is `false`.
963 #[cfg_attr(feature = "serde", serde(default))]
964 pub inline_code_info: bool,
965
966 /// Whether indented type 6 and type 7 HTML blocks continue across blank
967 /// lines when later content keeps a consistent indentation.
968 ///
969 /// The default is `false`, which follows `CommonMark`'s blank-line rule.
970 #[cfg_attr(feature = "serde", serde(default))]
971 pub html_block_blank_lines: bool,
972
973 /// Whether `:` is allowed in HTML tag names for namespace prefixes such
974 /// as `svg:circle`.
975 ///
976 /// The default is `false` to preserve `CommonMark` parsing of text such as
977 /// `<m:abc>`.
978 #[cfg_attr(feature = "serde", serde(default))]
979 pub html_tag_namespaces: bool,
980
981 /// Whether to support strikethrough with a single tilde
982 ///
983 /// This option does nothing if `strikethrough` is not turned on in
984 /// `constructs`.
985 /// This option does not affect strikethrough with double tildes.
986 ///
987 /// The default is `true`, which follows how markdown on `github.com`
988 /// works, as strikethrough with single tildes is supported.
989 /// Pass `false`, to follow the GFM spec more strictly, by not allowing
990 /// strikethrough with single tildes.
991 ///
992 /// ## Examples
993 ///
994 /// ```
995 /// use base_markdown::{to_html_with_options, Constructs, Options, ParseOptions};
996 ///
997 /// // `markdown-rs` supports single tildes by default:
998 /// assert_eq!(
999 /// to_html_with_options(
1000 /// "~a~",
1001 /// &Options {
1002 /// parse: ParseOptions {
1003 /// constructs: Constructs::gfm(),
1004 /// ..ParseOptions::default()
1005 /// },
1006 /// ..Options::default()
1007 /// }
1008 /// ),
1009 /// "<p><del>a</del></p>"
1010 /// );
1011 ///
1012 /// // Pass `strikethrough_single_tilde: false` to turn that off:
1013 /// assert_eq!(
1014 /// to_html_with_options(
1015 /// "~a~",
1016 /// &Options {
1017 /// parse: ParseOptions {
1018 /// constructs: Constructs::gfm(),
1019 /// strikethrough_single_tilde: false,
1020 /// ..ParseOptions::default()
1021 /// },
1022 /// ..Options::default()
1023 /// }
1024 /// ),
1025 /// "<p>~a~</p>"
1026 /// );
1027 /// ```
1028 #[cfg_attr(feature = "serde", serde(default))]
1029 pub strikethrough_single_tilde: bool,
1030
1031 /// Whether to support math (text) with a single dollar
1032 ///
1033 /// This option does nothing if `math_text` is not turned on in
1034 /// `constructs`.
1035 /// This option does not affect math (text) with two or more dollars.
1036 ///
1037 /// The default is `true`, which is more close to how code (text) and
1038 /// Pandoc work, as it allows math with a single dollar to form.
1039 /// However, single dollars can interfere with “normal” dollars in text.
1040 /// Pass `false`, to only allow math (text) to form when two or more
1041 /// dollars are used.
1042 /// If you pass `false`, you can still use two or more dollars for text
1043 /// math.
1044 ///
1045 /// ## Examples
1046 ///
1047 /// ```
1048 /// use base_markdown::{to_html_with_options, Constructs, Options, ParseOptions};
1049 ///
1050 /// // `markdown-rs` supports single dollars by default:
1051 /// assert_eq!(
1052 /// to_html_with_options(
1053 /// "$a$",
1054 /// &Options {
1055 /// parse: ParseOptions {
1056 /// constructs: Constructs {
1057 /// math_text: true,
1058 /// ..Constructs::default()
1059 /// },
1060 /// ..ParseOptions::default()
1061 /// },
1062 /// ..Options::default()
1063 /// }
1064 /// ),
1065 /// "<p><code class=\"language-math math-inline\">a</code></p>"
1066 /// );
1067 ///
1068 /// // Pass `math_text_single_dollar: false` to turn that off:
1069 /// assert_eq!(
1070 /// to_html_with_options(
1071 /// "$a$",
1072 /// &Options {
1073 /// parse: ParseOptions {
1074 /// constructs: Constructs {
1075 /// math_text: true,
1076 /// ..Constructs::default()
1077 /// },
1078 /// math_text_single_dollar: false,
1079 /// ..ParseOptions::default()
1080 /// },
1081 /// ..Options::default()
1082 /// }
1083 /// ),
1084 /// "<p>$a$</p>"
1085 /// );
1086 /// ```
1087 #[cfg_attr(feature = "serde", serde(default))]
1088 pub math_text_single_dollar: bool,
1089}
1090
1091impl Default for ParseOptions {
1092 /// `CommonMark` defaults.
1093 fn default() -> Self {
1094 Self {
1095 constructs: Constructs::default(),
1096 inline_code_info: false,
1097 html_block_blank_lines: false,
1098 html_tag_namespaces: false,
1099 strikethrough_single_tilde: true,
1100 math_text_single_dollar: true,
1101 }
1102 }
1103}
1104
1105impl ParseOptions {
1106 /// GFM.
1107 ///
1108 /// GFM stands for GitHub flavored markdown.
1109 /// GFM extends `CommonMark` and adds support for autolink literals,
1110 /// footnotes, strikethrough, tables, and tasklists.
1111 ///
1112 /// For more information, see the GFM specification:
1113 /// <https://github.github.com/gfm/>
1114 pub fn gfm() -> Self {
1115 Self {
1116 constructs: Constructs::gfm(),
1117 ..Self::default()
1118 }
1119 }
1120}
1121
1122/// Configuration that describes how to parse from markdown and compile to
1123/// HTML.
1124///
1125/// In most cases, you will want to use the default trait or `gfm` method.
1126///
1127/// ## Examples
1128///
1129/// ```
1130/// use base_markdown::Options;
1131/// # fn main() {
1132///
1133/// // Use the default trait to compile markdown to HTML according to `CommonMark`:
1134/// let commonmark = Options::default();
1135///
1136/// // Use the `gfm` method to compile markdown to HTML according to GFM:
1137/// let gfm = Options::gfm();
1138/// # }
1139/// ```
1140#[allow(clippy::struct_excessive_bools)]
1141#[derive(Debug, Default)]
1142#[cfg_attr(
1143 feature = "serde",
1144 derive(serde::Serialize, serde::Deserialize),
1145 serde(default)
1146)]
1147pub struct Options {
1148 /// Configuration that describes how to parse from markdown.
1149 pub parse: ParseOptions,
1150 /// Configuration that describes how to compile to HTML.
1151 pub compile: CompileOptions,
1152}
1153
1154impl Options {
1155 /// GFM.
1156 ///
1157 /// GFM stands for GitHub flavored markdown.
1158 /// GFM extends `CommonMark` and adds support for autolink literals,
1159 /// footnotes, strikethrough, tables, and tasklists.
1160 /// On the compilation side, the GFM preset turns on the tagfilter.
1161 /// The tagfilter is useless, but it’s included here for consistency.
1162 ///
1163 /// For more information, see the GFM specification:
1164 /// <https://github.github.com/gfm/>
1165 pub fn gfm() -> Self {
1166 Self {
1167 parse: ParseOptions::gfm(),
1168 compile: CompileOptions::gfm(),
1169 }
1170 }
1171}
1172
1173#[cfg(test)]
1174#[path = "configuration/tests.rs"]
1175mod tests;