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 output HTML using the [HTML module](html/index.html).
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 pulldown_cmark::{Parser, Options};
32//!
33//! let markdown_input = "Hello world, this is a ~~complicated~~ *very simple* example.";
34//!
35//! // Set up options and parser. Strikethroughs are not part of the CommonMark standard
36//! // and we therefore must enable it explicitly.
37//! let mut options = Options::empty();
38//! options.insert(Options::ENABLE_STRIKETHROUGH);
39//! let parser = Parser::new_ext(markdown_input, options);
40//!
41//! # #[cfg(feature = "html")] {
42//! // Write to String buffer.
43//! let mut html_output = String::new();
44//! pulldown_cmark::html::push_html(&mut html_output, parser);
45//!
46//! // Check that the output is what we expected.
47//! let expected_html = "<p>Hello world, this is a <del>complicated</del> <em>very simple</em> example.</p>\n";
48//! assert_eq!(expected_html, &html_output);
49//! # }
50//! ```
51//!
52//! Note that consecutive text events can happen due to the manner in which the
53//! parser evaluates the source. A utility `TextMergeStream` exists to improve
54//! the comfort of iterating the events:
55//!
56//! ```rust
57//! use pulldown_cmark::{Event, Parser, TextMergeStream};
58//!
59//! let markdown_input = "Hello world, this is a ~~complicated~~ *very simple* example.";
60//!
61//! let iterator = TextMergeStream::new(Parser::new(markdown_input));
62//!
63//! for event in iterator {
64//! match event {
65//! Event::Text(text) => println!("{}", text),
66//! _ => {}
67//! }
68//! }
69//! ```
70//!
71
72// When compiled for the rustc compiler itself we want to make sure that this is
73// an unstable crate.
74#![cfg_attr(rustbuild, feature(staged_api, rustc_private))]
75#![cfg_attr(rustbuild, unstable(feature = "rustc_private", issue = "27812"))]
76// Forbid unsafe code unless the SIMD feature is enabled.
77#![cfg_attr(not(feature = "simd"), forbid(unsafe_code))]
78#![warn(missing_debug_implementations)]
79
80#[cfg(feature = "serde")]
81use serde::{Deserialize, Serialize};
82
83#[cfg(feature = "html")]
84pub mod html;
85
86pub mod utils;
87
88mod entities;
89mod firstpass;
90mod linklabel;
91mod parse;
92mod puncttable;
93mod scanners;
94mod strings;
95mod tree;
96
97use std::fmt::Display;
98
99pub use crate::parse::{
100 BrokenLink, BrokenLinkCallback, DefaultBrokenLinkCallback, OffsetIter, Parser, RefDefs,
101};
102pub use crate::strings::{CowStr, InlineStr};
103pub use crate::utils::*;
104
105/// Codeblock kind.
106#[derive(Clone, Debug, PartialEq)]
107#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
108pub enum CodeBlockKind<'a> {
109 Indented,
110 /// The value contained in the tag describes the language of the code, which may be empty.
111 #[cfg_attr(feature = "serde", serde(borrow))]
112 Fenced(CowStr<'a>),
113}
114
115impl<'a> CodeBlockKind<'a> {
116 pub fn is_indented(&self) -> bool {
117 matches!(*self, CodeBlockKind::Indented)
118 }
119
120 pub fn is_fenced(&self) -> bool {
121 matches!(*self, CodeBlockKind::Fenced(_))
122 }
123
124 pub fn into_static(self) -> CodeBlockKind<'static> {
125 match self {
126 CodeBlockKind::Indented => CodeBlockKind::Indented,
127 CodeBlockKind::Fenced(s) => CodeBlockKind::Fenced(s.into_static()),
128 }
129 }
130}
131
132/// BlockQuote kind (Note, Tip, Important, Warning, Caution).
133#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
134#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
135pub enum BlockQuoteKind {
136 Note,
137 Tip,
138 Important,
139 Warning,
140 Caution,
141}
142
143/// Metadata block kind.
144#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
145#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
146pub enum MetadataBlockKind {
147 YamlStyle,
148 PlusesStyle,
149}
150
151/// Tags for elements that can contain other elements.
152#[derive(Clone, Debug, PartialEq)]
153#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
154pub enum Tag<'a> {
155 /// A paragraph of text and other inline elements.
156 Paragraph,
157
158 /// A heading, with optional identifier, classes and custom attributes.
159 /// The identifier is prefixed with `#` and the last one in the attributes
160 /// list is chosen, classes are prefixed with `.` and custom attributes
161 /// have no prefix and can optionally have a value (`myattr` or `myattr=myvalue`).
162 ///
163 /// `id`, `classes` and `attrs` are only parsed and populated with [`Options::ENABLE_HEADING_ATTRIBUTES`], `None` or empty otherwise.
164 Heading {
165 level: HeadingLevel,
166 id: Option<CowStr<'a>>,
167 classes: Vec<CowStr<'a>>,
168 /// The first item of the tuple is the attr and second one the value.
169 attrs: Vec<(CowStr<'a>, Option<CowStr<'a>>)>,
170 },
171
172 /// A block quote.
173 ///
174 /// The `BlockQuoteKind` is only parsed & populated with [`Options::ENABLE_GFM`], `None` otherwise.
175 ///
176 /// ```markdown
177 /// > regular quote
178 ///
179 /// > [!NOTE]
180 /// > note quote
181 /// ```
182 BlockQuote(Option<BlockQuoteKind>),
183 /// A code block.
184 CodeBlock(CodeBlockKind<'a>),
185
186 /// An HTML block.
187 ///
188 /// 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.
189 ///
190 /// Most HTML blocks end on an empty line, though some e.g. `<pre>` like `<script>` or `<!-- Comments -->` don't.
191 /// ```markdown
192 /// <body> Is HTML block even though here is non-whitespace.
193 /// Block ends on an empty line.
194 ///
195 /// <some-random-tag>
196 /// This is HTML block.
197 ///
198 /// <pre> Doesn't end on empty lines.
199 ///
200 /// This is still the same block.</pre>
201 /// ```
202 HtmlBlock,
203
204 /// A list. If the list is ordered the field indicates the number of the first item.
205 /// Contains only list items.
206 List(Option<u64>), // TODO: add delim and tight for ast (not needed for html)
207 /// A list item.
208 Item,
209 /// A footnote definition. The value contained is the footnote's label by which it can
210 /// be referred to.
211 ///
212 /// Only parsed and emitted with [`Options::ENABLE_FOOTNOTES`] or [`Options::ENABLE_OLD_FOOTNOTES`].
213 #[cfg_attr(feature = "serde", serde(borrow))]
214 FootnoteDefinition(CowStr<'a>),
215
216 /// Only parsed and emitted with [`Options::ENABLE_DEFINITION_LIST`].
217 DefinitionList,
218 /// Only parsed and emitted with [`Options::ENABLE_DEFINITION_LIST`].
219 DefinitionListTitle,
220 /// Only parsed and emitted with [`Options::ENABLE_DEFINITION_LIST`].
221 DefinitionListDefinition,
222
223 /// A table. Contains a vector describing the text-alignment for each of its columns.
224 /// Only parsed and emitted with [`Options::ENABLE_TABLES`].
225 Table(Vec<Alignment>),
226 /// A table header. Contains only `TableCell`s. Note that the table body starts immediately
227 /// after the closure of the `TableHead` tag. There is no `TableBody` tag.
228 /// Only parsed and emitted with [`Options::ENABLE_TABLES`].
229 TableHead,
230 /// A table row. Is used both for header rows as body rows. Contains only `TableCell`s.
231 /// Only parsed and emitted with [`Options::ENABLE_TABLES`].
232 TableRow,
233 /// Only parsed and emitted with [`Options::ENABLE_TABLES`].
234 TableCell,
235
236 // span-level tags
237 /// [Emphasis](https://spec.commonmark.org/0.31.2/#emphasis-and-strong-emphasis).
238 /// ```markdown
239 /// half*emph* _strong_ _multi _level__
240 /// ```
241 Emphasis,
242 /// [Strong emphasis](https://spec.commonmark.org/0.31.2/#emphasis-and-strong-emphasis).
243 /// ```markdown
244 /// half**strong** __strong__ __multi __level____
245 /// ```
246 Strong,
247 /// Only parsed and emitted with [`Options::ENABLE_STRIKETHROUGH`].
248 ///
249 /// ```markdown
250 /// ~strike through~
251 /// ```
252 Strikethrough,
253 /// Only parsed and emitted with [`Options::ENABLE_SUPERSCRIPT`].
254 ///
255 /// ```markdown
256 /// ^superscript^
257 /// ```
258 Superscript,
259 /// Only parsed and emitted with [`Options::ENABLE_SUBSCRIPT`], if disabled `~something~` is parsed as [`Strikethrough`](Self::Strikethrough).
260 /// ```markdown
261 /// ~subscript~ ~~if also enabled this is strikethrough~~
262 /// ```
263 Subscript,
264
265 /// A link.
266 Link {
267 link_type: LinkType,
268 dest_url: CowStr<'a>,
269 title: CowStr<'a>,
270 /// Identifier of reference links, e.g. `world` in the link `[hello][world]`.
271 id: CowStr<'a>,
272 },
273
274 /// An image. The first field is the link type, the second the destination URL and the third is a title,
275 /// the fourth is the link identifier.
276 Image {
277 link_type: LinkType,
278 dest_url: CowStr<'a>,
279 title: CowStr<'a>,
280 /// Identifier of reference links, e.g. `world` in the link `[hello][world]`.
281 id: CowStr<'a>,
282 },
283
284 /// A metadata block.
285 /// Only parsed and emitted with [`Options::ENABLE_YAML_STYLE_METADATA_BLOCKS`]
286 /// or [`Options::ENABLE_PLUSES_DELIMITED_METADATA_BLOCKS`].
287 MetadataBlock(MetadataBlockKind),
288}
289
290impl<'a> Tag<'a> {
291 pub fn to_end(&self) -> TagEnd {
292 match self {
293 Tag::Paragraph => TagEnd::Paragraph,
294 Tag::Heading { level, .. } => TagEnd::Heading(*level),
295 Tag::BlockQuote(kind) => TagEnd::BlockQuote(*kind),
296 Tag::CodeBlock(_) => TagEnd::CodeBlock,
297 Tag::HtmlBlock => TagEnd::HtmlBlock,
298 Tag::List(number) => TagEnd::List(number.is_some()),
299 Tag::Item => TagEnd::Item,
300 Tag::FootnoteDefinition(_) => TagEnd::FootnoteDefinition,
301 Tag::Table(_) => TagEnd::Table,
302 Tag::TableHead => TagEnd::TableHead,
303 Tag::TableRow => TagEnd::TableRow,
304 Tag::TableCell => TagEnd::TableCell,
305 Tag::Subscript => TagEnd::Subscript,
306 Tag::Superscript => TagEnd::Superscript,
307 Tag::Emphasis => TagEnd::Emphasis,
308 Tag::Strong => TagEnd::Strong,
309 Tag::Strikethrough => TagEnd::Strikethrough,
310 Tag::Link { .. } => TagEnd::Link,
311 Tag::Image { .. } => TagEnd::Image,
312 Tag::MetadataBlock(kind) => TagEnd::MetadataBlock(*kind),
313 Tag::DefinitionList => TagEnd::DefinitionList,
314 Tag::DefinitionListTitle => TagEnd::DefinitionListTitle,
315 Tag::DefinitionListDefinition => TagEnd::DefinitionListDefinition,
316 }
317 }
318
319 pub fn into_static(self) -> Tag<'static> {
320 match self {
321 Tag::Paragraph => Tag::Paragraph,
322 Tag::Heading {
323 level,
324 id,
325 classes,
326 attrs,
327 } => Tag::Heading {
328 level,
329 id: id.map(|s| s.into_static()),
330 classes: classes.into_iter().map(|s| s.into_static()).collect(),
331 attrs: attrs
332 .into_iter()
333 .map(|(k, v)| (k.into_static(), v.map(|s| s.into_static())))
334 .collect(),
335 },
336 Tag::BlockQuote(k) => Tag::BlockQuote(k),
337 Tag::CodeBlock(kb) => Tag::CodeBlock(kb.into_static()),
338 Tag::HtmlBlock => Tag::HtmlBlock,
339 Tag::List(v) => Tag::List(v),
340 Tag::Item => Tag::Item,
341 Tag::FootnoteDefinition(a) => Tag::FootnoteDefinition(a.into_static()),
342 Tag::Table(v) => Tag::Table(v),
343 Tag::TableHead => Tag::TableHead,
344 Tag::TableRow => Tag::TableRow,
345 Tag::TableCell => Tag::TableCell,
346 Tag::Emphasis => Tag::Emphasis,
347 Tag::Strong => Tag::Strong,
348 Tag::Strikethrough => Tag::Strikethrough,
349 Tag::Superscript => Tag::Superscript,
350 Tag::Subscript => Tag::Subscript,
351 Tag::Link {
352 link_type,
353 dest_url,
354 title,
355 id,
356 } => Tag::Link {
357 link_type,
358 dest_url: dest_url.into_static(),
359 title: title.into_static(),
360 id: id.into_static(),
361 },
362 Tag::Image {
363 link_type,
364 dest_url,
365 title,
366 id,
367 } => Tag::Image {
368 link_type,
369 dest_url: dest_url.into_static(),
370 title: title.into_static(),
371 id: id.into_static(),
372 },
373 Tag::MetadataBlock(v) => Tag::MetadataBlock(v),
374 Tag::DefinitionList => Tag::DefinitionList,
375 Tag::DefinitionListTitle => Tag::DefinitionListTitle,
376 Tag::DefinitionListDefinition => Tag::DefinitionListDefinition,
377 }
378 }
379}
380
381/// The end of a `Tag`.
382#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
383#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
384pub enum TagEnd {
385 Paragraph,
386 Heading(HeadingLevel),
387
388 BlockQuote(Option<BlockQuoteKind>),
389 CodeBlock,
390
391 HtmlBlock,
392
393 /// A list, `true` for ordered lists.
394 List(bool),
395 Item,
396 FootnoteDefinition,
397
398 DefinitionList,
399 DefinitionListTitle,
400 DefinitionListDefinition,
401
402 Table,
403 TableHead,
404 TableRow,
405 TableCell,
406
407 Emphasis,
408 Strong,
409 Strikethrough,
410 Superscript,
411 Subscript,
412
413 Link,
414 Image,
415
416 MetadataBlock(MetadataBlockKind),
417}
418
419/// Make sure `TagEnd` is no more than two bytes in size.
420/// This is why it's used instead of just using `Tag`.
421#[cfg(target_pointer_width = "64")]
422const _STATIC_ASSERT_TAG_END_SIZE: [(); 2] = [(); std::mem::size_of::<TagEnd>()];
423
424impl<'a> From<Tag<'a>> for TagEnd {
425 fn from(value: Tag) -> Self {
426 value.to_end()
427 }
428}
429
430#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
431#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
432pub enum HeadingLevel {
433 H1 = 1,
434 H2,
435 H3,
436 H4,
437 H5,
438 H6,
439}
440
441impl Display for HeadingLevel {
442 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
443 match self {
444 Self::H1 => write!(f, "h1"),
445 Self::H2 => write!(f, "h2"),
446 Self::H3 => write!(f, "h3"),
447 Self::H4 => write!(f, "h4"),
448 Self::H5 => write!(f, "h5"),
449 Self::H6 => write!(f, "h6"),
450 }
451 }
452}
453
454/// Returned when trying to convert a `usize` into a `Heading` but it fails
455/// because the usize isn't a valid heading level
456#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
457pub struct InvalidHeadingLevel(usize);
458
459impl TryFrom<usize> for HeadingLevel {
460 type Error = InvalidHeadingLevel;
461
462 fn try_from(value: usize) -> Result<Self, Self::Error> {
463 match value {
464 1 => Ok(Self::H1),
465 2 => Ok(Self::H2),
466 3 => Ok(Self::H3),
467 4 => Ok(Self::H4),
468 5 => Ok(Self::H5),
469 6 => Ok(Self::H6),
470 _ => Err(InvalidHeadingLevel(value)),
471 }
472 }
473}
474
475/// Type specifier for inline links. See [the Tag::Link](enum.Tag.html#variant.Link) for more information.
476#[derive(Clone, Debug, PartialEq, Copy)]
477#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
478pub enum LinkType {
479 /// Inline link like `[foo](bar)`
480 Inline,
481 /// Reference link like `[foo][bar]`
482 Reference,
483 /// Reference without destination in the document, but resolved by the broken_link_callback
484 ReferenceUnknown,
485 /// Collapsed link like `[foo][]`
486 Collapsed,
487 /// Collapsed link without destination in the document, but resolved by the broken_link_callback
488 CollapsedUnknown,
489 /// Shortcut link like `[foo]`
490 Shortcut,
491 /// Shortcut without destination in the document, but resolved by the broken_link_callback
492 ShortcutUnknown,
493 /// Autolink like `<http://foo.bar/baz>`
494 Autolink,
495 /// Email address in autolink like `<john@example.org>`
496 Email,
497 /// Wikilink link like `[[foo]]` or `[[foo|bar]]`
498 WikiLink {
499 /// `true` if the wikilink was piped.
500 ///
501 /// * `true` - `[[foo|bar]]`
502 /// * `false` - `[[foo]]`
503 has_pothole: bool,
504 },
505}
506
507impl LinkType {
508 /// Map the link type to an equivalent _Unknown link type.
509 fn to_unknown(self) -> Self {
510 match self {
511 LinkType::Reference => LinkType::ReferenceUnknown,
512 LinkType::Collapsed => LinkType::CollapsedUnknown,
513 LinkType::Shortcut => LinkType::ShortcutUnknown,
514 _ => unreachable!(),
515 }
516 }
517}
518
519/// Markdown events that are generated in a preorder traversal of the document
520/// tree, with additional `End` events whenever all of an inner node's children
521/// have been visited.
522#[derive(Clone, Debug, PartialEq)]
523#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
524pub enum Event<'a> {
525 /// Start of a tagged element. Events that are yielded after this event
526 /// and before its corresponding `End` event are inside this element.
527 /// Start and end events are guaranteed to be balanced.
528 #[cfg_attr(feature = "serde", serde(borrow))]
529 Start(Tag<'a>),
530 /// End of a tagged element.
531 End(TagEnd),
532 /// A text node.
533 ///
534 /// All text, outside and inside [`Tag`]s.
535 #[cfg_attr(feature = "serde", serde(borrow))]
536 Text(CowStr<'a>),
537 /// An [inline code node](https://spec.commonmark.org/0.31.2/#code-spans).
538 ///
539 /// ```markdown
540 /// `code`
541 /// ```
542 #[cfg_attr(feature = "serde", serde(borrow))]
543 Code(CowStr<'a>),
544 /// An inline math environment node.
545 /// Requires [`Options::ENABLE_MATH`].
546 ///
547 /// ```markdown
548 /// $math$
549 /// ```
550 #[cfg_attr(feature = "serde", serde(borrow))]
551 InlineMath(CowStr<'a>),
552 /// A display math environment node.
553 /// Requires [`Options::ENABLE_MATH`].
554 ///
555 /// ```markdown
556 /// $$math$$
557 /// ```
558 #[cfg_attr(feature = "serde", serde(borrow))]
559 DisplayMath(CowStr<'a>),
560 /// An HTML node.
561 ///
562 /// A line of HTML inside [`Tag::HtmlBlock`] includes the line break.
563 #[cfg_attr(feature = "serde", serde(borrow))]
564 Html(CowStr<'a>),
565 /// An [inline HTML node](https://spec.commonmark.org/0.31.2/#raw-html).
566 ///
567 /// Contains only the tag itself, e.g. `<open-tag>`, `</close-tag>` or `<!-- comment -->`.
568 ///
569 /// **Note**: Under some conditions HTML can also be parsed as an HTML Block, see [`Tag::HtmlBlock`] for details.
570 #[cfg_attr(feature = "serde", serde(borrow))]
571 InlineHtml(CowStr<'a>),
572 /// A reference to a footnote with given label, which may or may not be defined
573 /// by an event with a [`Tag::FootnoteDefinition`] tag. Definitions and references to them may
574 /// occur in any order. Only parsed and emitted with [`Options::ENABLE_FOOTNOTES`] or [`Options::ENABLE_OLD_FOOTNOTES`].
575 ///
576 /// ```markdown
577 /// [^1]
578 /// ```
579 #[cfg_attr(feature = "serde", serde(borrow))]
580 FootnoteReference(CowStr<'a>),
581 /// A [soft line break](https://spec.commonmark.org/0.31.2/#soft-line-breaks).
582 ///
583 /// Any line break that isn't a [`HardBreak`](Self::HardBreak), or the end of e.g. a paragraph.
584 SoftBreak,
585 /// A [hard line break](https://spec.commonmark.org/0.31.2/#hard-line-breaks).
586 ///
587 /// A line ending that is either preceded by at least two spaces or `\`.
588 ///
589 /// ```markdown
590 /// hard··
591 /// line\
592 /// breaks
593 /// ```
594 /// *`·` is a space*
595 HardBreak,
596 /// A horizontal ruler.
597 ///
598 /// ```markdown
599 /// ***
600 /// ···---
601 /// _·_··_····_··
602 /// ```
603 /// *`·` is any whitespace*
604 Rule,
605 /// A task list marker, rendered as a checkbox in HTML. Contains a true when it is checked.
606 /// Only parsed and emitted with [`Options::ENABLE_TASKLISTS`].
607 /// ```markdown
608 /// - [ ] unchecked
609 /// - [x] checked
610 /// ```
611 TaskListMarker(bool),
612}
613
614impl<'a> Event<'a> {
615 pub fn into_static(self) -> Event<'static> {
616 match self {
617 Event::Start(t) => Event::Start(t.into_static()),
618 Event::End(e) => Event::End(e),
619 Event::Text(s) => Event::Text(s.into_static()),
620 Event::Code(s) => Event::Code(s.into_static()),
621 Event::InlineMath(s) => Event::InlineMath(s.into_static()),
622 Event::DisplayMath(s) => Event::DisplayMath(s.into_static()),
623 Event::Html(s) => Event::Html(s.into_static()),
624 Event::InlineHtml(s) => Event::InlineHtml(s.into_static()),
625 Event::FootnoteReference(s) => Event::FootnoteReference(s.into_static()),
626 Event::SoftBreak => Event::SoftBreak,
627 Event::HardBreak => Event::HardBreak,
628 Event::Rule => Event::Rule,
629 Event::TaskListMarker(b) => Event::TaskListMarker(b),
630 }
631 }
632}
633
634/// Table column text alignment.
635#[derive(Copy, Clone, Debug, PartialEq)]
636#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
637
638pub enum Alignment {
639 /// Default text alignment.
640 None,
641 Left,
642 Center,
643 Right,
644}
645
646bitflags::bitflags! {
647 /// Option struct containing flags for enabling extra features
648 /// that are not part of the CommonMark spec.
649 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
650 pub struct Options: u32 {
651 const ENABLE_TABLES = 1 << 1;
652 /// GitHub-compatible footnote syntax.
653 ///
654 /// Footnotes are referenced with the syntax `[^IDENT]`,
655 /// and defined with an identifier followed by a colon at top level.
656 ///
657 /// ---
658 ///
659 /// ```markdown
660 /// Footnote referenced [^1].
661 ///
662 /// [^1]: footnote defined
663 /// ```
664 ///
665 /// Footnote referenced [^1].
666 ///
667 /// [^1]: footnote defined
668 const ENABLE_FOOTNOTES = 1 << 2;
669 const ENABLE_STRIKETHROUGH = 1 << 3;
670 const ENABLE_TASKLISTS = 1 << 4;
671 /// Enables replacement of ASCII punctuation characters with
672 /// Unicode ligatures and smart quotes.
673 ///
674 /// This includes replacing `--` with `—`, `---` with `—`, `...` with `…`,
675 /// `"quote"` with `“quote”`, and `'quote'` with `‘quote’`.
676 ///
677 /// The replacement takes place during the parsing of the document.
678 const ENABLE_SMART_PUNCTUATION = 1 << 5;
679 /// Extension to allow headings to have ID and classes.
680 ///
681 /// `# text { #id .class1 .class2 myattr other_attr=myvalue }`
682 /// is interpreted as a level 1 heading
683 /// with the content `text`, ID `id`, classes `class1` and `class2` and
684 /// custom attributes `myattr` (without value) and
685 /// `other_attr` with value `myvalue`.
686 /// Note that ID, classes, and custom attributes should be space-separated.
687 const ENABLE_HEADING_ATTRIBUTES = 1 << 6;
688 /// Metadata blocks in YAML style, i.e.:
689 /// - starting with a `---` line
690 /// - ending with a `---` or `...` line
691 const ENABLE_YAML_STYLE_METADATA_BLOCKS = 1 << 7;
692 /// Metadata blocks delimited by:
693 /// - `+++` line at start
694 /// - `+++` line at end
695 const ENABLE_PLUSES_DELIMITED_METADATA_BLOCKS = 1 << 8;
696 /// Older footnote syntax. This flag implies `ENABLE_FOOTNOTES`, changing it to use an
697 /// older syntax instead of the new, default, GitHub-compatible syntax.
698 ///
699 /// New syntax is different from the old syntax regarding
700 /// indentation, nesting, and footnote references with no definition:
701 ///
702 /// ```markdown
703 /// [^1]: In new syntax, this is two footnote definitions.
704 /// [^2]: In old syntax, this is a single footnote definition with two lines.
705 ///
706 /// [^3]:
707 ///
708 /// In new syntax, this is a footnote with two paragraphs.
709 ///
710 /// In old syntax, this is a footnote followed by a code block.
711 ///
712 /// In new syntax, this undefined footnote definition renders as
713 /// literal text [^4]. In old syntax, it creates a dangling link.
714 /// ```
715 const ENABLE_OLD_FOOTNOTES = (1 << 9) | (1 << 2);
716 /// With this feature enabled, two events `Event::InlineMath` and `Event::DisplayMath`
717 /// are emitted that conventionally contain TeX formulas.
718 const ENABLE_MATH = 1 << 10;
719 /// Misc GitHub Flavored Markdown features not supported in CommonMark.
720 /// The following features are currently behind this tag:
721 /// - Blockquote tags ([!NOTE], [!TIP], [!IMPORTANT], [!WARNING], [!CAUTION]).
722 const ENABLE_GFM = 1 << 11;
723 /// Commonmark-HS-Extensions compatible definition lists.
724 ///
725 /// ```markdown
726 /// title 1
727 /// : definition 1
728 /// title 2
729 /// : definition 2
730 /// ```
731 const ENABLE_DEFINITION_LIST = 1 << 12;
732 const ENABLE_SUPERSCRIPT = 1 << 13;
733 const ENABLE_SUBSCRIPT = 1 << 14;
734 /// Obsidian-style Wikilinks.
735 const ENABLE_WIKILINKS = 1 << 15;
736 }
737}
738
739impl Options {
740 pub(crate) fn has_gfm_footnotes(&self) -> bool {
741 self.contains(Options::ENABLE_FOOTNOTES) && !self.contains(Options::ENABLE_OLD_FOOTNOTES)
742 }
743}