Skip to main content

satteri_pulldown_cmark/
lib.rs

1// Copyright 2015 Google Inc. All rights reserved.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19// THE SOFTWARE.
20
21//! Pull parser for [CommonMark](https://commonmark.org). This crate provides a [Parser](struct.Parser.html) struct
22//! which is an iterator over [Event](enum.Event.html)s. This iterator can be used
23//! directly, or to build an arena representation via [`parse()`].
24//!
25//! By default, only CommonMark features are enabled. To use extensions like tables,
26//! footnotes or task lists, enable them by setting the corresponding flags in the
27//! [Options](struct.Options.html) struct.
28//!
29//! # Example
30//! ```rust
31//! use satteri_pulldown_cmark::{parse, Options};
32//!
33//! let markdown_input = "Hello world, this is a ~~complicated~~ *very simple* example.";
34//!
35//! let mut options = Options::empty();
36//! options.insert(Options::ENABLE_STRIKETHROUGH);
37//! let (arena, _) = parse(markdown_input, options);
38//! let html = satteri_ast::mdast_to_html(&arena);
39//!
40//! let expected_html = "<p>Hello world, this is a <del>complicated</del> <em>very simple</em> example.</p>\n";
41//! assert_eq!(expected_html, &html);
42//! ```
43//!
44//! Note that consecutive text events can happen due to the manner in which the
45//! parser evaluates the source. A utility `TextMergeStream` exists to improve
46//! the comfort of iterating the events:
47//!
48//! ```rust
49//! use satteri_pulldown_cmark::{Event, Parser, TextMergeStream};
50//!
51//! let markdown_input = "Hello world, this is a ~~complicated~~ *very simple* example.";
52//!
53//! let iterator = TextMergeStream::new(Parser::new(markdown_input));
54//!
55//! for event in iterator {
56//!     match event {
57//!         Event::Text(text) => println!("{}", text),
58//!         _ => {}
59//!     }
60//! }
61//! ```
62//!
63#![warn(
64    clippy::alloc_instead_of_core,
65    clippy::std_instead_of_alloc,
66    clippy::std_instead_of_core
67)]
68#![forbid(unsafe_code)]
69#![warn(missing_debug_implementations)]
70#![cfg_attr(not(feature = "std"), no_std)]
71
72#[macro_use]
73extern crate alloc;
74
75#[cfg(feature = "std")]
76extern crate std;
77
78#[cfg(not(feature = "std"))]
79compile_error!("This crate requires the \"std\" feature.");
80
81use alloc::vec::Vec;
82
83#[cfg(feature = "serde")]
84use serde::{Deserialize, Serialize};
85
86pub mod utils;
87
88pub mod arena_build;
89mod entities;
90mod firstpass;
91mod linklabel;
92#[cfg(feature = "mdx")]
93mod mdx;
94mod parse;
95pub(crate) mod post_passes;
96mod puncttable;
97mod scanners;
98mod strings;
99mod tree;
100
101use core::fmt::Display;
102
103#[cfg(feature = "mdx")]
104pub use crate::arena_build::MDX_OPTIONS;
105pub use crate::{
106    arena_build::{
107        DEFAULT_OPTIONS, parse, parse_into, parse_no_positions, parse_no_positions_into,
108    },
109    parse::{
110        BrokenLink, BrokenLinkCallback, DefaultParserCallbacks, OffsetIter, Parser,
111        ParserCallbacks, RefDefs,
112    },
113    strings::{CowStr, InlineStr},
114    utils::*,
115};
116
117/// Codeblock kind.
118#[derive(Clone, Debug, PartialEq)]
119#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
120pub enum CodeBlockKind<'a> {
121    Indented,
122    /// The value contained in the tag describes the language of the code, which may be empty.
123    #[cfg_attr(feature = "serde", serde(borrow))]
124    Fenced(CowStr<'a>),
125}
126
127impl<'a> CodeBlockKind<'a> {
128    pub fn is_indented(&self) -> bool {
129        matches!(*self, CodeBlockKind::Indented)
130    }
131
132    pub fn is_fenced(&self) -> bool {
133        matches!(*self, CodeBlockKind::Fenced(_))
134    }
135
136    pub fn into_static(self) -> CodeBlockKind<'static> {
137        match self {
138            CodeBlockKind::Indented => CodeBlockKind::Indented,
139            CodeBlockKind::Fenced(s) => CodeBlockKind::Fenced(s.into_static()),
140        }
141    }
142}
143
144/// BlockQuote kind (Note, Tip, Important, Warning, Caution).
145#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
146#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
147pub enum BlockQuoteKind {
148    Note,
149    Tip,
150    Important,
151    Warning,
152    Caution,
153}
154
155/// Directive kind.
156#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
157#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
158pub enum DirectiveKind {
159    Container,
160    Leaf,
161    Text,
162}
163
164/// Metadata block kind.
165#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
166#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
167pub enum MetadataBlockKind {
168    YamlStyle,
169    PlusesStyle,
170}
171
172/// Tags for elements that can contain other elements.
173#[derive(Clone, Debug, PartialEq)]
174#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
175pub enum Tag<'a> {
176    /// A paragraph of text and other inline elements.
177    Paragraph,
178
179    /// A heading, with optional identifier, classes and custom attributes.
180    /// The identifier is prefixed with `#` and the last one in the attributes
181    /// list is chosen, classes are prefixed with `.` and custom attributes
182    /// have no prefix and can optionally have a value (`myattr` or `myattr=myvalue`).
183    ///
184    /// `id`, `classes` and `attrs` are only parsed and populated with [`Options::ENABLE_HEADING_ATTRIBUTES`], `None` or empty otherwise.
185    Heading {
186        level: HeadingLevel,
187        id: Option<CowStr<'a>>,
188        classes: Vec<CowStr<'a>>,
189        /// The first item of the tuple is the attr and second one the value.
190        attrs: Vec<(CowStr<'a>, Option<CowStr<'a>>)>,
191    },
192
193    /// A block quote.
194    ///
195    /// The `BlockQuoteKind` is only parsed & populated with [`Options::ENABLE_GFM`], `None` otherwise.
196    ///
197    /// ```markdown
198    /// > regular quote
199    ///
200    /// > [!NOTE]
201    /// > note quote
202    /// ```
203    BlockQuote(Option<BlockQuoteKind>),
204    /// A code block.
205    CodeBlock(CodeBlockKind<'a>),
206    /// A directive (container, leaf, or text).
207    /// Only parsed and emitted with [`Options::ENABLE_DIRECTIVE`].
208    Directive {
209        kind: DirectiveKind,
210        name: CowStr<'a>,
211        attributes: Vec<(CowStr<'a>, CowStr<'a>)>,
212    },
213
214    /// An HTML block.
215    ///
216    /// A line that begins with some predefined tags (HTML block tags) (see [CommonMark Spec](https://spec.commonmark.org/0.31.2/#html-blocks) for more details) or any tag that is followed only by whitespace.
217    ///
218    /// Most HTML blocks end on an empty line, though some e.g. `<pre>` like `<script>` or `<!-- Comments -->` don't.
219    /// ```markdown
220    /// <body> Is HTML block even though here is non-whitespace.
221    /// Block ends on an empty line.
222    ///
223    /// <some-random-tag>
224    /// This is HTML block.
225    ///
226    /// <pre> Doesn't end on empty lines.
227    ///
228    /// This is still the same block.</pre>
229    /// ```
230    HtmlBlock,
231
232    /// A list. If the list is ordered the first field indicates the number of the first item.
233    /// The second field is `true` when the list is tight (no blank lines between items).
234    /// Contains only list items.
235    List(Option<u64>, bool),
236    /// A list item.
237    Item,
238    /// A footnote definition. The value contained is the footnote's label by which it can
239    /// be referred to.
240    ///
241    /// Only parsed and emitted with [`Options::ENABLE_FOOTNOTES`].
242    #[cfg_attr(feature = "serde", serde(borrow))]
243    FootnoteDefinition(CowStr<'a>),
244
245    /// Only parsed and emitted with [`Options::ENABLE_DEFINITION_LIST`].
246    DefinitionList,
247    /// Only parsed and emitted with [`Options::ENABLE_DEFINITION_LIST`].
248    DefinitionListTitle,
249    /// Only parsed and emitted with [`Options::ENABLE_DEFINITION_LIST`].
250    DefinitionListDefinition,
251
252    /// A table. Contains a vector describing the text-alignment for each of its columns.
253    /// Only parsed and emitted with [`Options::ENABLE_TABLES`].
254    Table(Vec<Alignment>),
255    /// A table header. Contains only `TableCell`s. Note that the table body starts immediately
256    /// after the closure of the `TableHead` tag. There is no `TableBody` tag.
257    /// Only parsed and emitted with [`Options::ENABLE_TABLES`].
258    TableHead,
259    /// A table row. Is used both for header rows as body rows. Contains only `TableCell`s.
260    /// Only parsed and emitted with [`Options::ENABLE_TABLES`].
261    TableRow,
262    /// Only parsed and emitted with [`Options::ENABLE_TABLES`].
263    TableCell,
264
265    // span-level tags
266    /// [Emphasis](https://spec.commonmark.org/0.31.2/#emphasis-and-strong-emphasis).
267    /// ```markdown
268    /// half*emph* _strong_ _multi _level__
269    /// ```
270    Emphasis,
271    /// [Strong emphasis](https://spec.commonmark.org/0.31.2/#emphasis-and-strong-emphasis).
272    /// ```markdown
273    /// half**strong** __strong__ __multi __level____
274    /// ```
275    Strong,
276    /// Only parsed and emitted with [`Options::ENABLE_STRIKETHROUGH`].
277    ///
278    /// ```markdown
279    /// ~strike through~
280    /// ```
281    Strikethrough,
282    /// Only parsed and emitted with [`Options::ENABLE_SUPERSCRIPT`].
283    ///
284    /// ```markdown
285    /// ^superscript^
286    /// ```
287    Superscript,
288    /// Only parsed and emitted with [`Options::ENABLE_SUBSCRIPT`], if disabled `~something~` is parsed as [`Strikethrough`](Self::Strikethrough).
289    /// ```markdown
290    /// ~subscript~ ~~if also enabled this is strikethrough~~
291    /// ```
292    Subscript,
293
294    /// A link.
295    Link {
296        link_type: LinkType,
297        dest_url: CowStr<'a>,
298        title: CowStr<'a>,
299        /// Identifier of reference links, e.g. `world` in the link `[hello][world]`.
300        id: CowStr<'a>,
301    },
302
303    /// An image. The first field is the link type, the second the destination URL and the third is a title,
304    /// the fourth is the link identifier.
305    Image {
306        link_type: LinkType,
307        dest_url: CowStr<'a>,
308        title: CowStr<'a>,
309        /// Identifier of reference links, e.g. `world` in the link `[hello][world]`.
310        id: CowStr<'a>,
311    },
312
313    /// A metadata block.
314    /// Only parsed and emitted with [`Options::ENABLE_YAML_STYLE_METADATA_BLOCKS`]
315    /// or [`Options::ENABLE_PLUSES_DELIMITED_METADATA_BLOCKS`].
316    MetadataBlock(MetadataBlockKind),
317
318    /// An MDX JSX element (flow-level, i.e. block).
319    /// Only parsed and emitted with [`Options::ENABLE_MDX`].
320    /// The `CowStr` is the raw JSX tag content (e.g. `Component x={1}`).
321    /// ```mdx
322    /// <Component x={1}>
323    ///   children
324    /// </Component>
325    /// ```
326    #[cfg(feature = "mdx")]
327    #[cfg_attr(feature = "serde", serde(borrow))]
328    MdxJsxFlowElement(CowStr<'a>),
329
330    /// An MDX JSX element (text-level, i.e. inline).
331    /// Only parsed and emitted with [`Options::ENABLE_MDX`].
332    #[cfg(feature = "mdx")]
333    #[cfg_attr(feature = "serde", serde(borrow))]
334    MdxJsxTextElement(CowStr<'a>),
335}
336
337impl<'a> Tag<'a> {
338    pub fn to_end(&self) -> TagEnd {
339        match self {
340            Tag::Paragraph => TagEnd::Paragraph,
341            Tag::Heading { level, .. } => TagEnd::Heading(*level),
342            Tag::BlockQuote(kind) => TagEnd::BlockQuote(*kind),
343            Tag::CodeBlock(_) => TagEnd::CodeBlock,
344            Tag::Directive { kind, .. } => TagEnd::Directive(*kind),
345            Tag::HtmlBlock => TagEnd::HtmlBlock,
346            Tag::List(number, _) => TagEnd::List(number.is_some()),
347            Tag::Item => TagEnd::Item,
348            Tag::FootnoteDefinition(_) => TagEnd::FootnoteDefinition,
349            Tag::Table(_) => TagEnd::Table,
350            Tag::TableHead => TagEnd::TableHead,
351            Tag::TableRow => TagEnd::TableRow,
352            Tag::TableCell => TagEnd::TableCell,
353            Tag::Subscript => TagEnd::Subscript,
354            Tag::Superscript => TagEnd::Superscript,
355            Tag::Emphasis => TagEnd::Emphasis,
356            Tag::Strong => TagEnd::Strong,
357            Tag::Strikethrough => TagEnd::Strikethrough,
358            Tag::Link { .. } => TagEnd::Link,
359            Tag::Image { .. } => TagEnd::Image,
360            Tag::MetadataBlock(kind) => TagEnd::MetadataBlock(*kind),
361            Tag::DefinitionList => TagEnd::DefinitionList,
362            Tag::DefinitionListTitle => TagEnd::DefinitionListTitle,
363            Tag::DefinitionListDefinition => TagEnd::DefinitionListDefinition,
364            #[cfg(feature = "mdx")]
365            Tag::MdxJsxFlowElement(_) => TagEnd::MdxJsxFlowElement,
366            #[cfg(feature = "mdx")]
367            Tag::MdxJsxTextElement(_) => TagEnd::MdxJsxTextElement,
368        }
369    }
370
371    pub fn into_static(self) -> Tag<'static> {
372        match self {
373            Tag::Paragraph => Tag::Paragraph,
374            Tag::Heading {
375                level,
376                id,
377                classes,
378                attrs,
379            } => Tag::Heading {
380                level,
381                id: id.map(|s| s.into_static()),
382                classes: classes.into_iter().map(|s| s.into_static()).collect(),
383                attrs: attrs
384                    .into_iter()
385                    .map(|(k, v)| (k.into_static(), v.map(|s| s.into_static())))
386                    .collect(),
387            },
388            Tag::BlockQuote(k) => Tag::BlockQuote(k),
389            Tag::CodeBlock(kb) => Tag::CodeBlock(kb.into_static()),
390            Tag::Directive {
391                kind,
392                name,
393                attributes,
394            } => Tag::Directive {
395                kind,
396                name: name.into_static(),
397                attributes: attributes
398                    .into_iter()
399                    .map(|(k, v)| (k.into_static(), v.into_static()))
400                    .collect(),
401            },
402            Tag::HtmlBlock => Tag::HtmlBlock,
403            Tag::List(v, t) => Tag::List(v, t),
404            Tag::Item => Tag::Item,
405            Tag::FootnoteDefinition(a) => Tag::FootnoteDefinition(a.into_static()),
406            Tag::Table(v) => Tag::Table(v),
407            Tag::TableHead => Tag::TableHead,
408            Tag::TableRow => Tag::TableRow,
409            Tag::TableCell => Tag::TableCell,
410            Tag::Emphasis => Tag::Emphasis,
411            Tag::Strong => Tag::Strong,
412            Tag::Strikethrough => Tag::Strikethrough,
413            Tag::Superscript => Tag::Superscript,
414            Tag::Subscript => Tag::Subscript,
415            Tag::Link {
416                link_type,
417                dest_url,
418                title,
419                id,
420            } => Tag::Link {
421                link_type,
422                dest_url: dest_url.into_static(),
423                title: title.into_static(),
424                id: id.into_static(),
425            },
426            Tag::Image {
427                link_type,
428                dest_url,
429                title,
430                id,
431            } => Tag::Image {
432                link_type,
433                dest_url: dest_url.into_static(),
434                title: title.into_static(),
435                id: id.into_static(),
436            },
437            Tag::MetadataBlock(v) => Tag::MetadataBlock(v),
438            Tag::DefinitionList => Tag::DefinitionList,
439            Tag::DefinitionListTitle => Tag::DefinitionListTitle,
440            Tag::DefinitionListDefinition => Tag::DefinitionListDefinition,
441            #[cfg(feature = "mdx")]
442            Tag::MdxJsxFlowElement(s) => Tag::MdxJsxFlowElement(s.into_static()),
443            #[cfg(feature = "mdx")]
444            Tag::MdxJsxTextElement(s) => Tag::MdxJsxTextElement(s.into_static()),
445        }
446    }
447}
448
449/// The end of a `Tag`.
450#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
451#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
452pub enum TagEnd {
453    Paragraph,
454    Heading(HeadingLevel),
455
456    BlockQuote(Option<BlockQuoteKind>),
457    CodeBlock,
458    Directive(DirectiveKind),
459
460    HtmlBlock,
461
462    /// A list, `true` for ordered lists.
463    List(bool),
464    Item,
465    FootnoteDefinition,
466
467    DefinitionList,
468    DefinitionListTitle,
469    DefinitionListDefinition,
470
471    Table,
472    TableHead,
473    TableRow,
474    TableCell,
475
476    Emphasis,
477    Strong,
478    Strikethrough,
479    Superscript,
480    Subscript,
481
482    Link,
483    Image,
484
485    MetadataBlock(MetadataBlockKind),
486
487    #[cfg(feature = "mdx")]
488    MdxJsxFlowElement,
489    #[cfg(feature = "mdx")]
490    MdxJsxTextElement,
491}
492
493/// Make sure `TagEnd` is no more than two bytes in size.
494/// This is why it's used instead of just using `Tag`.
495#[cfg(target_pointer_width = "64")]
496const _STATIC_ASSERT_TAG_END_SIZE: [(); 2] = [(); core::mem::size_of::<TagEnd>()];
497
498impl<'a> From<Tag<'a>> for TagEnd {
499    fn from(value: Tag) -> Self {
500        value.to_end()
501    }
502}
503
504#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
505#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
506pub enum HeadingLevel {
507    H1 = 1,
508    H2,
509    H3,
510    H4,
511    H5,
512    H6,
513}
514
515impl Display for HeadingLevel {
516    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
517        match self {
518            Self::H1 => write!(f, "h1"),
519            Self::H2 => write!(f, "h2"),
520            Self::H3 => write!(f, "h3"),
521            Self::H4 => write!(f, "h4"),
522            Self::H5 => write!(f, "h5"),
523            Self::H6 => write!(f, "h6"),
524        }
525    }
526}
527
528/// Returned when trying to convert a `usize` into a `Heading` but it fails
529/// because the usize isn't a valid heading level
530#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
531pub struct InvalidHeadingLevel(usize);
532
533impl TryFrom<usize> for HeadingLevel {
534    type Error = InvalidHeadingLevel;
535
536    fn try_from(value: usize) -> Result<Self, Self::Error> {
537        match value {
538            1 => Ok(Self::H1),
539            2 => Ok(Self::H2),
540            3 => Ok(Self::H3),
541            4 => Ok(Self::H4),
542            5 => Ok(Self::H5),
543            6 => Ok(Self::H6),
544            _ => Err(InvalidHeadingLevel(value)),
545        }
546    }
547}
548
549/// Type specifier for inline links. See [the Tag::Link](enum.Tag.html#variant.Link) for more information.
550#[derive(Clone, Debug, PartialEq, Copy)]
551#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
552pub enum LinkType {
553    /// Inline link like `[foo](bar)`
554    Inline,
555    /// Reference link like `[foo][bar]`
556    Reference,
557    /// Reference without destination in the document, but resolved by the broken_link_callback
558    ReferenceUnknown,
559    /// Collapsed link like `[foo][]`
560    Collapsed,
561    /// Collapsed link without destination in the document, but resolved by the broken_link_callback
562    CollapsedUnknown,
563    /// Shortcut link like `[foo]`
564    Shortcut,
565    /// Shortcut without destination in the document, but resolved by the broken_link_callback
566    ShortcutUnknown,
567    /// Autolink like `<http://foo.bar/baz>`
568    Autolink,
569    /// Email address in autolink like `<john@example.org>`
570    Email,
571    /// Wikilink link like `[[foo]]` or `[[foo|bar]]`
572    WikiLink {
573        /// `true` if the wikilink was piped.
574        ///
575        /// * `true` - `[[foo|bar]]`
576        /// * `false` - `[[foo]]`
577        has_pothole: bool,
578    },
579}
580
581impl LinkType {
582    /// Map the link type to an equivalent _Unknown link type.
583    fn to_unknown(self) -> Self {
584        match self {
585            LinkType::Reference => LinkType::ReferenceUnknown,
586            LinkType::Collapsed => LinkType::CollapsedUnknown,
587            LinkType::Shortcut => LinkType::ShortcutUnknown,
588            _ => unreachable!(),
589        }
590    }
591}
592
593/// Markdown events that are generated in a preorder traversal of the document
594/// tree, with additional `End` events whenever all of an inner node's children
595/// have been visited.
596#[derive(Clone, Debug, PartialEq)]
597#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
598pub enum Event<'a> {
599    /// Start of a tagged element. Events that are yielded after this event
600    /// and before its corresponding `End` event are inside this element.
601    /// Start and end events are guaranteed to be balanced.
602    #[cfg_attr(feature = "serde", serde(borrow))]
603    Start(Tag<'a>),
604    /// End of a tagged element.
605    End(TagEnd),
606    /// A text node.
607    ///
608    /// All text, outside and inside [`Tag`]s.
609    #[cfg_attr(feature = "serde", serde(borrow))]
610    Text(CowStr<'a>),
611    /// An [inline code node](https://spec.commonmark.org/0.31.2/#code-spans).
612    ///
613    /// ```markdown
614    /// `code`
615    /// ```
616    #[cfg_attr(feature = "serde", serde(borrow))]
617    Code(CowStr<'a>),
618    /// An inline math environment node.
619    /// Requires [`Options::ENABLE_MATH`].
620    ///
621    /// ```markdown
622    /// $math$
623    /// ```
624    #[cfg_attr(feature = "serde", serde(borrow))]
625    InlineMath(CowStr<'a>),
626    /// A display math environment node.
627    /// Requires [`Options::ENABLE_MATH`].
628    ///
629    /// ```markdown
630    /// $$math$$
631    /// ```
632    #[cfg_attr(feature = "serde", serde(borrow))]
633    DisplayMath(CowStr<'a>),
634    /// An HTML node.
635    ///
636    /// A line of HTML inside [`Tag::HtmlBlock`] includes the line break.
637    #[cfg_attr(feature = "serde", serde(borrow))]
638    Html(CowStr<'a>),
639    /// An [inline HTML node](https://spec.commonmark.org/0.31.2/#raw-html).
640    ///
641    /// Contains only the tag itself, e.g. `<open-tag>`, `</close-tag>` or `<!-- comment -->`.
642    ///
643    /// **Note**: Under some conditions HTML can also be parsed as an HTML Block, see [`Tag::HtmlBlock`] for details.
644    #[cfg_attr(feature = "serde", serde(borrow))]
645    InlineHtml(CowStr<'a>),
646    /// A reference to a footnote with given label, defined
647    /// by an event with a [`Tag::FootnoteDefinition`] tag. Definitions and references to them may
648    /// occur in any order. Only parsed and emitted with [`Options::ENABLE_FOOTNOTES`].
649    ///
650    /// ```markdown
651    /// [^1]
652    /// ```
653    #[cfg_attr(feature = "serde", serde(borrow))]
654    FootnoteReference(CowStr<'a>),
655    /// A [soft line break](https://spec.commonmark.org/0.31.2/#soft-line-breaks).
656    ///
657    /// Any line break that isn't a [`HardBreak`](Self::HardBreak), or the end of e.g. a paragraph.
658    SoftBreak,
659    /// A [hard line break](https://spec.commonmark.org/0.31.2/#hard-line-breaks).
660    ///
661    /// A line ending that is either preceded by at least two spaces or `\`.
662    ///
663    /// ```markdown
664    /// hard··
665    /// line\
666    /// breaks
667    /// ```
668    /// *`·` is a space*
669    HardBreak,
670    /// A horizontal ruler.
671    ///
672    /// ```markdown
673    /// ***
674    /// ···---
675    /// _·_··_····_··
676    /// ```
677    /// *`·` is any whitespace*
678    Rule,
679    /// A task list marker, rendered as a checkbox in HTML. Contains a true when it is checked.
680    /// Only parsed and emitted with [`Options::ENABLE_TASKLISTS`].
681    /// ```markdown
682    /// - [ ] unchecked
683    /// - [x] checked
684    /// ```
685    TaskListMarker(bool),
686
687    /// An MDX flow expression (block-level).
688    /// Only parsed and emitted with [`Options::ENABLE_MDX`].
689    /// ```mdx
690    /// {1 + 1}
691    /// ```
692    #[cfg(feature = "mdx")]
693    #[cfg_attr(feature = "serde", serde(borrow))]
694    MdxFlowExpression(CowStr<'a>),
695
696    /// An MDX text expression (inline).
697    /// Only parsed and emitted with [`Options::ENABLE_MDX`].
698    /// ```mdx
699    /// a]n {expression} here
700    /// ```
701    #[cfg(feature = "mdx")]
702    #[cfg_attr(feature = "serde", serde(borrow))]
703    MdxTextExpression(CowStr<'a>),
704
705    /// An MDX ESM block (import/export at document level).
706    /// Only parsed and emitted with [`Options::ENABLE_MDX`].
707    /// ```mdx
708    /// import {Chart} from './chart.js'
709    /// export const meta = {}
710    /// ```
711    #[cfg(feature = "mdx")]
712    #[cfg_attr(feature = "serde", serde(borrow))]
713    MdxEsm(CowStr<'a>),
714}
715
716impl<'a> Event<'a> {
717    pub fn into_static(self) -> Event<'static> {
718        match self {
719            Event::Start(t) => Event::Start(t.into_static()),
720            Event::End(e) => Event::End(e),
721            Event::Text(s) => Event::Text(s.into_static()),
722            Event::Code(s) => Event::Code(s.into_static()),
723            Event::InlineMath(s) => Event::InlineMath(s.into_static()),
724            Event::DisplayMath(s) => Event::DisplayMath(s.into_static()),
725            Event::Html(s) => Event::Html(s.into_static()),
726            Event::InlineHtml(s) => Event::InlineHtml(s.into_static()),
727            Event::FootnoteReference(s) => Event::FootnoteReference(s.into_static()),
728            Event::SoftBreak => Event::SoftBreak,
729            Event::HardBreak => Event::HardBreak,
730            Event::Rule => Event::Rule,
731            Event::TaskListMarker(b) => Event::TaskListMarker(b),
732            #[cfg(feature = "mdx")]
733            Event::MdxFlowExpression(s) => Event::MdxFlowExpression(s.into_static()),
734            #[cfg(feature = "mdx")]
735            Event::MdxTextExpression(s) => Event::MdxTextExpression(s.into_static()),
736            #[cfg(feature = "mdx")]
737            Event::MdxEsm(s) => Event::MdxEsm(s.into_static()),
738        }
739    }
740}
741
742/// Table column text alignment.
743#[derive(Copy, Clone, Debug, PartialEq)]
744#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
745pub enum Alignment {
746    /// Default text alignment.
747    None,
748    Left,
749    Center,
750    Right,
751}
752
753bitflags::bitflags! {
754    /// Option struct containing flags for enabling extra features
755    /// that are not part of the CommonMark spec.
756    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
757    pub struct Options: u32 {
758        const ENABLE_TABLES = 1 << 1;
759        /// GitHub-compatible footnote syntax.
760        ///
761        /// Footnotes are referenced with the syntax `[^IDENT]`,
762        /// and defined with an identifier followed by a colon at top level.
763        ///
764        /// ---
765        ///
766        /// ```markdown
767        /// Footnote referenced [^1].
768        ///
769        /// [^1]: footnote defined
770        /// ```
771        ///
772        /// Footnote referenced [^1].
773        ///
774        /// [^1]: footnote defined
775        const ENABLE_FOOTNOTES = 1 << 2;
776        const ENABLE_STRIKETHROUGH = 1 << 3;
777        const ENABLE_TASKLISTS = 1 << 4;
778        /// Enables replacement of ASCII punctuation characters with
779        /// Unicode ligatures and smart quotes.
780        ///
781        /// This includes replacing `--` with `–`, `---` with `—`, `...` with `…`,
782        /// `”quote”` with `\u{201c}quote\u{201d}`, and `’quote’` with `\u{2018}quote\u{2019}`.
783        ///
784        /// Equivalent to enabling all of `ENABLE_SMART_QUOTES`,
785        /// `ENABLE_SMART_DASHES`, and `ENABLE_SMART_ELLIPSES`.
786        const ENABLE_SMART_PUNCTUATION = 1 << 5;
787        /// Replace straight quotes (`”`, `’`) with curly/smart quotes.
788        const ENABLE_SMART_QUOTES = 1 << 18;
789        /// Replace `--` with en-dash and `---` with em-dash.
790        const ENABLE_SMART_DASHES = 1 << 19;
791        /// Replace `...` with ellipsis (`…`).
792        const ENABLE_SMART_ELLIPSES = 1 << 20;
793        /// Extension to allow headings to have ID and classes.
794        ///
795        /// `# text { #id .class1 .class2 myattr other_attr=myvalue }`
796        /// is interpreted as a level 1 heading
797        /// with the content `text`, ID `id`, classes `class1` and `class2` and
798        /// custom attributes `myattr` (without value) and
799        /// `other_attr` with value `myvalue`.
800        /// Note that ID, classes, and custom attributes should be space-separated.
801        const ENABLE_HEADING_ATTRIBUTES = 1 << 6;
802        /// Metadata blocks in YAML style, i.e.:
803        /// - starting with a `---` line
804        /// - ending with a `---` or `...` line
805        const ENABLE_YAML_STYLE_METADATA_BLOCKS = 1 << 7;
806        /// Metadata blocks delimited by:
807        /// - `+++` line at start
808        /// - `+++` line at end
809        const ENABLE_PLUSES_DELIMITED_METADATA_BLOCKS = 1 << 8;
810        /// Emits `Event::InlineMath` and `Event::DisplayMath` for TeX formulas.
811        /// Umbrella over [`ENABLE_MATH_SINGLE_DOLLAR`](Self::ENABLE_MATH_SINGLE_DOLLAR)
812        /// and [`ENABLE_MATH_MULTI_DOLLAR`](Self::ENABLE_MATH_MULTI_DOLLAR).
813        const ENABLE_MATH = 1 << 10;
814        /// Single-dollar inline math (`$x$`).
815        const ENABLE_MATH_SINGLE_DOLLAR = 1 << 22;
816        /// Multi-dollar math: inline `$$x$$` and `$$` block fences.
817        const ENABLE_MATH_MULTI_DOLLAR = 1 << 23;
818        /// Misc GitHub Flavored Markdown features not supported in CommonMark.
819        const ENABLE_GFM = 1 << 11;
820        /// GitHub-style blockquote alerts ([!NOTE], [!TIP], [!IMPORTANT], [!WARNING], [!CAUTION]).
821        /// Not part of the GFM spec — this is a GitHub-specific feature.
822        const ENABLE_GITHUB_ALERTS = 1 << 21;
823        /// Commonmark-HS-Extensions compatible definition lists.
824        ///
825        /// ```markdown
826        /// title 1
827        ///   : definition 1
828        ///
829        /// title 2
830        ///   : definition 2a
831        ///   : definition 2b
832        /// ```
833        const ENABLE_DEFINITION_LIST = 1 << 12;
834        const ENABLE_SUPERSCRIPT = 1 << 13;
835        const ENABLE_SUBSCRIPT = 1 << 14;
836        /// Obsidian-style Wikilinks.
837        const ENABLE_WIKILINKS = 1 << 15;
838        /// Directives: container (:::), leaf (::), and text (:) directives.
839        const ENABLE_DIRECTIVE = 1 << 16;
840        /// MDX: enables JSX elements, expressions, and ESM import/export.
841        const ENABLE_MDX = 1 << 17;
842    }
843}
844
845impl Options {
846    pub(crate) fn has_smart_quotes(&self) -> bool {
847        self.contains(Options::ENABLE_SMART_PUNCTUATION)
848            || self.contains(Options::ENABLE_SMART_QUOTES)
849    }
850
851    pub(crate) fn has_smart_dashes(&self) -> bool {
852        self.contains(Options::ENABLE_SMART_PUNCTUATION)
853            || self.contains(Options::ENABLE_SMART_DASHES)
854    }
855
856    pub(crate) fn has_smart_ellipses(&self) -> bool {
857        self.contains(Options::ENABLE_SMART_PUNCTUATION)
858            || self.contains(Options::ENABLE_SMART_ELLIPSES)
859    }
860
861    pub(crate) fn has_math(&self) -> bool {
862        self.intersects(
863            Options::ENABLE_MATH
864                | Options::ENABLE_MATH_SINGLE_DOLLAR
865                | Options::ENABLE_MATH_MULTI_DOLLAR,
866        )
867    }
868}