jotup/
lib.rs

1//! A pull parser for [Djot](https://djot.net).
2//!
3//! The main entry is through [`Parser`] which constructs an [`Iterator`] of [`Event`]s. The events
4//! can then be processed before rendering them via the [`Render`] trait. This crate provides an
5//! [`html`] module that implements an HTML renderer.
6//!
7//! # Feature flags
8//!
9//! - `html` (default): build the html module and a binary that converts djot to HTML.
10//!
11//! # Examples
12//!
13//! Generate HTML from Djot input:
14//!
15//! ```
16//! # #[cfg(feature = "html")]
17//! # {
18//! let djot_input = "hello *world*!";
19//! let events = jotup::Parser::new(djot_input);
20//! let html = jotup::html::Renderer::default().render_events_to_string(events);
21//! assert_eq!(html, "<p>hello <strong>world</strong>!</p>\n");
22//! # }
23//! ```
24//!
25//! Apply some filter to a specific type of element:
26//!
27//! ```
28//! # #[cfg(feature = "html")]
29//! # {
30//! # use jotup::Event;
31//! # use jotup::Container::Link;
32//! let events =
33//!     jotup::Parser::new("a [link](https://example.com)").map(|e| match e {
34//!         Event::Start(Link(dst, ty), attrs) => {
35//!             Event::Start(Link(dst.replace(".com", ".net").into(), ty), attrs)
36//!         }
37//!         e => e,
38//!     });
39//! let html = jotup::html::Renderer::default().render_events_to_string(events);
40//! assert_eq!(html, "<p>a <a href=\"https://example.net\">link</a></p>\n");
41//! # }
42//! ```
43
44#[cfg(feature = "html")]
45pub mod html;
46
47#[cfg(feature = "async")]
48pub mod r#async;
49
50mod attr;
51mod block;
52mod inline;
53mod lex;
54
55pub use attr::AttributeKind;
56pub use attr::AttributeValue;
57pub use attr::AttributeValueParts;
58pub use attr::Attributes;
59pub use attr::ParseAttributesError;
60
61type CowStr<'s> = std::borrow::Cow<'s, str>;
62
63/// An implementation of rendering a djot document
64///
65/// This interface will be called by the djot parser with elements
66/// parsed by it.
67pub trait Render<'s> {
68    type Error;
69
70    /// Called iteratively with every single event emitted by parsing djot document
71    fn emit(&mut self, event: Event<'s>) -> Result<(), Self::Error>;
72}
73
74/// Utility extensions method for [`Render`] trait
75pub trait RenderExt<'s>: Render<'s> {
76    /// Parse and render the whole document with `renderer`
77    fn render_document(&mut self, src: &'s str) -> Result<(), Self::Error> {
78        self.render_events(Parser::new(src))
79    }
80
81    /// Render document as a list of events already parsed by the [`Parser`]
82    fn render_events<I>(&mut self, events: I) -> Result<(), Self::Error>
83    where
84        I: Iterator<Item = Event<'s>>,
85    {
86        self.emit(Event::Start(Container::Document, Attributes::new()))?;
87
88        for event in events {
89            self.emit(event)?;
90        }
91
92        self.emit(Event::End)
93    }
94}
95
96impl<'s, R> RenderExt<'s> for R where R: Render<'s> {}
97
98/// A `Render` that produces an output
99pub trait RenderOutput<'s>: Render<'s> {
100    type Output;
101    fn into_output(self) -> Self::Output;
102}
103
104/// Utility extension method for [`RenderOutput`] trait
105pub trait RenderOutputExt<'s>: RenderOutput<'s> {
106    fn render_into_document(
107        mut self,
108        input: &'s str,
109    ) -> Result<Self::Output, <Self as Render<'s>>::Error>
110    where
111        Self: Sized,
112    {
113        RenderExt::<'s>::render_document(&mut self, input)?;
114        Ok(self.into_output())
115    }
116}
117
118impl<'s, R> RenderOutputExt<'s> for R where R: RenderOutput<'s> {}
119
120/// A Djot event.
121///
122/// A Djot document is represented by a sequence of events. An element may consist of one or
123/// multiple events. [`Container`] elements are represented by a [`Event::Start`] followed by
124/// events representing its content, and finally a [`Event::End`]. Atomic elements without any
125/// inside elements are represented by a single event.
126#[derive(Debug, Clone, PartialEq, Eq)]
127pub enum Event<'s> {
128    /// Start of a container.
129    ///
130    /// Always paired with a matching [`Event::End`].
131    ///
132    /// # Examples
133    ///
134    /// ```
135    /// # use jotup::*;
136    /// let src = concat!(
137    ///     "{#a}\n",
138    ///     "[word]{#b}\n",
139    /// );
140    /// let events: Vec<_> = Parser::new(src).collect();
141    /// assert_eq!(
142    ///     &events,
143    ///     &[
144    ///         Event::Start(
145    ///             Container::Paragraph,
146    ///             [(AttributeKind::Id, "a".into())].into_iter().collect(),
147    ///         ),
148    ///         Event::Start(
149    ///             Container::Span,
150    ///             [(AttributeKind::Id, "b".into())].into_iter().collect(),
151    ///         ),
152    ///         Event::Str("word".into()),
153    ///         Event::End,
154    ///         Event::End,
155    ///     ],
156    /// );
157    /// let html = "<p id=\"a\"><span id=\"b\">word</span></p>\n";
158    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
159    /// ```
160    Start(Container<'s>, Attributes<'s>),
161    /// End of a container.
162    ///
163    /// Always paired with a matching [`Event::Start`].
164    End,
165    /// A string object, text only.
166    ///
167    /// The strings from the parser will always be borrowed, but users may replace them with owned
168    /// variants before rendering.
169    ///
170    /// # Examples
171    ///
172    /// ```
173    /// # use jotup::*;
174    /// let src = "str";
175    /// let events: Vec<_> = Parser::new(src).collect();
176    /// assert_eq!(
177    ///     &events,
178    ///     &[
179    ///         Event::Start(Container::Paragraph, Attributes::new()),
180    ///         Event::Str("str".into()),
181    ///         Event::End,
182    ///     ],
183    /// );
184    /// let html = "<p>str</p>\n";
185    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
186    /// ```
187    Str(CowStr<'s>),
188    /// A footnote reference.
189    ///
190    /// # Examples
191    ///
192    /// ```
193    /// # use jotup::*;
194    /// let src = "txt[^nb].";
195    /// let events: Vec<_> = Parser::new(src).collect();
196    /// assert_eq!(
197    ///     &events,
198    ///     &[
199    ///         Event::Start(Container::Paragraph, Attributes::new()),
200    ///         Event::Str("txt".into()),
201    ///         Event::FootnoteReference("nb".into()),
202    ///         Event::Str(".".into()),
203    ///         Event::End,
204    ///     ],
205    /// );
206    /// let html = concat!(
207    ///     "<p>txt<a id=\"fnref1\" href=\"#fn1\" role=\"doc-noteref\"><sup>1</sup></a>.</p>\n",
208    ///     "<section role=\"doc-endnotes\">\n",
209    ///     "<hr>\n",
210    ///     "<ol>\n",
211    ///     "<li id=\"fn1\">\n",
212    ///     "<p><a href=\"#fnref1\" role=\"doc-backlink\">↩\u{fe0e}</a></p>\n",
213    ///     "</li>\n",
214    ///     "</ol>\n",
215    ///     "</section>\n",
216    /// );
217    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
218    /// ```
219    FootnoteReference(CowStr<'s>),
220    /// A symbol, by default rendered literally but may be treated specially.
221    ///
222    /// # Examples
223    ///
224    /// ```
225    /// # use jotup::*;
226    /// let src = "a :sym:";
227    /// let events: Vec<_> = Parser::new(src).collect();
228    /// assert_eq!(
229    ///     &events,
230    ///     &[
231    ///         Event::Start(Container::Paragraph, Attributes::new()),
232    ///         Event::Str("a ".into()),
233    ///         Event::Symbol("sym".into()),
234    ///         Event::End,
235    ///     ],
236    /// );
237    /// let html = "<p>a :sym:</p>\n";
238    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
239    /// ```
240    Symbol(CowStr<'s>),
241    /// Left single quotation mark.
242    ///
243    /// # Examples
244    ///
245    /// ```
246    /// # use jotup::*;
247    /// let src = r#"'quote'"#;
248    /// let events: Vec<_> = Parser::new(src).collect();
249    /// assert_eq!(
250    ///     &events,
251    ///     &[
252    ///         Event::Start(Container::Paragraph, Attributes::new()),
253    ///         Event::LeftSingleQuote,
254    ///         Event::Str("quote".into()),
255    ///         Event::RightSingleQuote,
256    ///         Event::End,
257    ///     ],
258    /// );
259    /// let html = "<p>‘quote’</p>\n";
260    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
261    /// ```
262    LeftSingleQuote,
263    /// Right single quotation mark.
264    ///
265    /// # Examples
266    ///
267    /// ```
268    /// # use jotup::*;
269    /// let src = r#"'}Tis Socrates'"#;
270    /// let events: Vec<_> = Parser::new(src).collect();
271    /// assert_eq!(
272    ///     &events,
273    ///     &[
274    ///         Event::Start(Container::Paragraph, Attributes::new()),
275    ///         Event::RightSingleQuote,
276    ///         Event::Str("Tis Socrates".into()),
277    ///         Event::RightSingleQuote,
278    ///         Event::End,
279    ///     ],
280    /// );
281    /// let html = "<p>’Tis Socrates’</p>\n";
282    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
283    /// ```
284    RightSingleQuote,
285    /// Left single quotation mark.
286    ///
287    /// # Examples
288    ///
289    /// ```
290    /// # use jotup::*;
291    /// let src = r#""Hello," he said"#;
292    /// let events: Vec<_> = Parser::new(src).collect();
293    /// assert_eq!(
294    ///     &events,
295    ///     &[
296    ///         Event::Start(Container::Paragraph, Attributes::new()),
297    ///         Event::LeftDoubleQuote,
298    ///         Event::Str("Hello,".into()),
299    ///         Event::RightDoubleQuote,
300    ///         Event::Str(" he said".into()),
301    ///         Event::End,
302    ///     ],
303    /// );
304    /// let html = "<p>“Hello,” he said</p>\n";
305    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
306    /// ```
307    LeftDoubleQuote,
308    /// Right double quotation mark.
309    RightDoubleQuote,
310    /// A horizontal ellipsis, i.e. a set of three periods.
311    ///
312    /// # Examples
313    ///
314    /// ```
315    /// # use jotup::*;
316    /// let src = "yes...";
317    /// let events: Vec<_> = Parser::new(src).collect();
318    /// assert_eq!(
319    ///     &events,
320    ///     &[
321    ///         Event::Start(Container::Paragraph, Attributes::new()),
322    ///         Event::Str("yes".into()),
323    ///         Event::Ellipsis,
324    ///         Event::End,
325    ///     ],
326    /// );
327    /// let html = "<p>yes…</p>\n";
328    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
329    /// ```
330    Ellipsis,
331    /// An en dash.
332    ///
333    /// # Examples
334    ///
335    /// ```
336    /// # use jotup::*;
337    /// let src = "57--33";
338    /// let events: Vec<_> = Parser::new(src).collect();
339    /// assert_eq!(
340    ///     &events,
341    ///     &[
342    ///         Event::Start(Container::Paragraph, Attributes::new()),
343    ///         Event::Str("57".into()),
344    ///         Event::EnDash,
345    ///         Event::Str("33".into()),
346    ///         Event::End,
347    ///     ],
348    /// );
349    /// let html = "<p>57–33</p>\n";
350    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
351    /// ```
352    EnDash,
353    /// An em dash.
354    ///
355    /// # Examples
356    ///
357    /// ```
358    /// # use jotup::*;
359    /// let src = "oxen---and";
360    /// let events: Vec<_> = Parser::new(src).collect();
361    /// assert_eq!(
362    ///     &events,
363    ///     &[
364    ///         Event::Start(Container::Paragraph, Attributes::new()),
365    ///         Event::Str("oxen".into()),
366    ///         Event::EmDash,
367    ///         Event::Str("and".into()),
368    ///         Event::End,
369    ///     ],
370    /// );
371    /// let html = "<p>oxen—and</p>\n";
372    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
373    /// ```
374    EmDash,
375    /// A space that must not break a line.
376    ///
377    /// # Examples
378    ///
379    /// ```
380    /// # use jotup::*;
381    /// let src = "no\\ break";
382    /// let events: Vec<_> = Parser::new(src).collect();
383    /// assert_eq!(
384    ///     &events,
385    ///     &[
386    ///         Event::Start(Container::Paragraph, Attributes::new()),
387    ///         Event::Str("no".into()),
388    ///         Event::Escape,
389    ///         Event::NonBreakingSpace,
390    ///         Event::Str("break".into()),
391    ///         Event::End,
392    ///     ],
393    /// );
394    /// let html = "<p>no&nbsp;break</p>\n";
395    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
396    /// ```
397    NonBreakingSpace,
398    /// A newline that may or may not break a line in the output.
399    ///
400    /// # Examples
401    ///
402    /// ```
403    /// # use jotup::*;
404    /// let src = concat!(
405    ///     "soft\n",
406    ///     "break\n",
407    /// );
408    /// let events: Vec<_> = Parser::new(src).collect();
409    /// assert_eq!(
410    ///     &events,
411    ///     &[
412    ///         Event::Start(Container::Paragraph, Attributes::new()),
413    ///         Event::Str("soft".into()),
414    ///         Event::Softbreak,
415    ///         Event::Str("break".into()),
416    ///         Event::End,
417    ///     ],
418    /// );
419    /// let html = concat!(
420    ///     "<p>soft\n",
421    ///     "break</p>\n",
422    /// );
423    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
424    /// ```
425    Softbreak,
426    /// A newline that must break a line in the output.
427    ///
428    /// # Examples
429    ///
430    /// ```
431    /// # use jotup::*;
432    /// let src = concat!(
433    ///     "hard\\\n",
434    ///     "break\n",
435    /// );
436    /// let events: Vec<_> = Parser::new(src).collect();
437    /// assert_eq!(
438    ///     &events,
439    ///     &[
440    ///         Event::Start(Container::Paragraph, Attributes::new()),
441    ///         Event::Str("hard".into()),
442    ///         Event::Escape,
443    ///         Event::Hardbreak,
444    ///         Event::Str("break".into()),
445    ///         Event::End,
446    ///     ],
447    /// );
448    /// let html = concat!(
449    ///     "<p>hard<br>\n",
450    ///     "break</p>\n",
451    /// );
452    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
453    /// ```
454    Hardbreak,
455    /// An escape character, not visible in output.
456    ///
457    /// # Examples
458    ///
459    /// ```
460    /// # use jotup::*;
461    /// let src = "\\*a\\*";
462    /// let events: Vec<_> = Parser::new(src).collect();
463    /// assert_eq!(
464    ///     &events,
465    ///     &[
466    ///         Event::Start(Container::Paragraph, Attributes::new()),
467    ///         Event::Escape,
468    ///         Event::Str("*a".into()),
469    ///         Event::Escape,
470    ///         Event::Str("*".into()),
471    ///         Event::End,
472    ///     ],
473    /// );
474    /// let html = "<p>*a*</p>\n";
475    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
476    /// ```
477    Escape,
478    /// A blank line, not visible in output.
479    ///
480    /// # Examples
481    ///
482    /// ```
483    /// # use jotup::*;
484    /// let src = concat!(
485    ///     "para0\n",
486    ///     "\n",
487    ///     "para1\n",
488    /// );
489    /// let events: Vec<_> = Parser::new(src).collect();
490    /// assert_eq!(
491    ///     &events,
492    ///     &[
493    ///         Event::Start(Container::Paragraph, Attributes::new()),
494    ///         Event::Str("para0".into()),
495    ///         Event::End,
496    ///         Event::Blankline,
497    ///         Event::Start(Container::Paragraph, Attributes::new()),
498    ///         Event::Str("para1".into()),
499    ///         Event::End,
500    ///     ],
501    /// );
502    /// let html = concat!(
503    ///     "<p>para0</p>\n",
504    ///     "<p>para1</p>\n",
505    /// );
506    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
507    /// ```
508    Blankline,
509    /// A thematic break, typically a horizontal rule.
510    ///
511    /// # Examples
512    ///
513    /// ```
514    /// # use jotup::*;
515    /// let src = concat!(
516    ///     "para0\n",
517    ///     "\n",
518    ///     " * * * *\n",
519    ///     "para1\n",
520    ///     "\n",
521    ///     "{.c}\n",
522    ///     "----\n",
523    /// );
524    /// let events: Vec<_> = Parser::new(src).collect();
525    /// assert_eq!(
526    ///     &events,
527    ///     &[
528    ///         Event::Start(Container::Paragraph, Attributes::new()),
529    ///         Event::Str("para0".into()),
530    ///         Event::End,
531    ///         Event::Blankline,
532    ///         Event::ThematicBreak(Attributes::new()),
533    ///         Event::Start(Container::Paragraph, Attributes::new()),
534    ///         Event::Str("para1".into()),
535    ///         Event::End,
536    ///         Event::Blankline,
537    ///         Event::ThematicBreak(
538    ///             [(AttributeKind::Class, "c".into())]
539    ///                 .into_iter()
540    ///                 .collect(),
541    ///         ),
542    ///     ],
543    /// );
544    /// let html = concat!(
545    ///     "<p>para0</p>\n",
546    ///     "<hr>\n",
547    ///     "<p>para1</p>\n",
548    ///     "<hr class=\"c\">\n",
549    /// );
550    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
551    /// ```
552    ThematicBreak(Attributes<'s>),
553    /// Dangling attributes not attached to anything.
554    ///
555    /// # Examples
556    ///
557    /// ```
558    /// # use jotup::*;
559    /// let src = concat!(
560    ///     "{#a}\n",
561    ///     "\n",
562    ///     "inline {#b}\n",
563    ///     "\n",
564    ///     "{#c}\n",
565    /// );
566    /// let events: Vec<_> = Parser::new(src).collect();
567    /// assert_eq!(
568    ///     &events,
569    ///     &[
570    ///         Event::Attributes(
571    ///             [(AttributeKind::Id, "a".into())]
572    ///                 .into_iter()
573    ///                 .collect(),
574    ///         ),
575    ///         Event::Blankline,
576    ///         Event::Start(Container::Paragraph, Attributes::new()),
577    ///         Event::Str("inline ".into()),
578    ///         Event::Attributes(
579    ///             [(AttributeKind::Id, "b".into())]
580    ///                 .into_iter()
581    ///                 .collect(),
582    ///         ),
583    ///         Event::End,
584    ///         Event::Blankline,
585    ///         Event::Attributes(
586    ///             [(AttributeKind::Id, "c".into())]
587    ///                 .into_iter()
588    ///                 .collect(),
589    ///         ),
590    ///     ],
591    /// );
592    /// let html = concat!(
593    ///     "\n",
594    ///     "<p>inline </p>\n",
595    /// );
596    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
597    /// ```
598    Attributes(Attributes<'s>),
599}
600
601/// A container that may contain other elements.
602///
603/// There are three types of containers:
604///
605/// - inline, may only contain inline elements,
606/// - block leaf, may only contain inline elements,
607/// - block container, may contain any block-level elements.
608#[derive(Debug, Clone, PartialEq, Eq)]
609pub enum Container<'s> {
610    /// The top-level document container.
611    Document,
612    /// A blockquote element.
613    ///
614    /// # Examples
615    ///
616    /// ```
617    /// # use jotup::*;
618    /// let src = concat!(
619    ///     "> a\n",
620    ///     "> b\n",
621    /// );
622    /// let events: Vec<_> = Parser::new(src).collect();
623    /// assert_eq!(
624    ///     &events,
625    ///     &[
626    ///         Event::Start(Container::Blockquote, Attributes::new()),
627    ///         Event::Start(Container::Paragraph, Attributes::new()),
628    ///         Event::Str("a".into()),
629    ///         Event::Softbreak,
630    ///         Event::Str("b".into()),
631    ///         Event::End,
632    ///         Event::End,
633    ///     ],
634    /// );
635    /// let html = concat!(
636    ///     "<blockquote>\n",
637    ///     "<p>a\n",
638    ///     "b</p>\n",
639    ///     "</blockquote>\n",
640    /// );
641    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
642    /// ```
643    Blockquote,
644    /// A list.
645    ///
646    /// # Examples
647    ///
648    /// ```
649    /// # use jotup::*;
650    /// let src = concat!(
651    ///     "- a\n",
652    ///     "\n",
653    ///     "- b\n",
654    /// );
655    /// let events: Vec<_> = Parser::new(src).collect();
656    /// assert_eq!(
657    ///     &events,
658    ///     &[
659    ///         Event::Start(
660    ///             Container::List {
661    ///                 kind: ListKind::Unordered(ListBulletType::Dash),
662    ///                 tight: false,
663    ///             },
664    ///             Attributes::new(),
665    ///         ),
666    ///         Event::Start(Container::ListItem, Attributes::new()),
667    ///         Event::Start(Container::Paragraph, Attributes::new()),
668    ///         Event::Str("a".into()),
669    ///         Event::End,
670    ///         Event::Blankline,
671    ///         Event::End,
672    ///         Event::Start(Container::ListItem, Attributes::new()),
673    ///         Event::Start(Container::Paragraph, Attributes::new()),
674    ///         Event::Str("b".into()),
675    ///         Event::End,
676    ///         Event::End,
677    ///         Event::End,
678    ///     ],
679    /// );
680    /// let html = concat!(
681    ///     "<ul>\n",
682    ///     "<li>\n",
683    ///     "<p>a</p>\n",
684    ///     "</li>\n",
685    ///     "<li>\n",
686    ///     "<p>b</p>\n",
687    ///     "</li>\n",
688    ///     "</ul>\n",
689    /// );
690    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
691    /// ```
692    List { kind: ListKind, tight: bool },
693    /// An item of a list
694    ///
695    /// # Examples
696    ///
697    /// ```
698    /// # use jotup::*;
699    /// let src = "- a";
700    /// let events: Vec<_> = Parser::new(src).collect();
701    /// assert_eq!(
702    ///     &events,
703    ///     &[
704    ///         Event::Start(
705    ///             Container::List {
706    ///                 kind: ListKind::Unordered(ListBulletType::Dash),
707    ///                 tight: true,
708    ///             },
709    ///             Attributes::new(),
710    ///         ),
711    ///         Event::Start(Container::ListItem, Attributes::new()),
712    ///         Event::Start(Container::Paragraph, Attributes::new()),
713    ///         Event::Str("a".into()),
714    ///         Event::End,
715    ///         Event::End,
716    ///         Event::End,
717    ///     ],
718    /// );
719    /// let html = concat!(
720    ///     "<ul>\n",
721    ///     "<li>\n",
722    ///     "a\n",
723    ///     "</li>\n",
724    ///     "</ul>\n",
725    /// );
726    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
727    /// ```
728    ListItem,
729    /// An item of a task list, either checked or unchecked.
730    ///
731    /// # Examples
732    ///
733    /// ```
734    /// # use jotup::*;
735    /// let src = "- [x] a";
736    /// let events: Vec<_> = Parser::new(src).collect();
737    /// assert_eq!(
738    ///     &events,
739    ///     &[
740    ///         Event::Start(
741    ///             Container::List {
742    ///                 kind: ListKind::Task(ListBulletType::Dash),
743    ///                 tight: true
744    ///             },
745    ///             Attributes::new(),
746    ///         ),
747    ///         Event::Start(
748    ///             Container::TaskListItem { checked: true },
749    ///             Attributes::new(),
750    ///         ),
751    ///         Event::Start(Container::Paragraph, Attributes::new()),
752    ///         Event::Str("a".into()),
753    ///         Event::End,
754    ///         Event::End,
755    ///         Event::End,
756    ///     ],
757    /// );
758    /// let html = concat!(
759    ///     "<ul class=\"task-list\">\n",
760    ///     "<li>\n",
761    ///     "<input disabled=\"\" type=\"checkbox\" checked=\"\"/>\n",
762    ///     "a\n",
763    ///     "</li>\n",
764    ///     "</ul>\n",
765    /// );
766    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
767    /// ```
768    TaskListItem { checked: bool },
769    /// A description list.
770    ///
771    /// # Examples
772    ///
773    /// ```
774    /// # use jotup::*;
775    /// let src = concat!(
776    ///     ": orange\n",
777    ///     "\n",
778    ///     " citrus fruit\n",
779    ///     ": apple\n",
780    ///     "\n",
781    ///     " malus fruit\n",
782    /// );
783    /// let events: Vec<_> = Parser::new(src).collect();
784    /// assert_eq!(
785    ///     &events,
786    ///     &[
787    ///         Event::Start(Container::DescriptionList, Attributes::new()),
788    ///         Event::Start(Container::DescriptionTerm, Attributes::new()),
789    ///         Event::Str("orange".into()),
790    ///         Event::End,
791    ///         Event::Blankline,
792    ///         Event::Start(Container::DescriptionDetails, Attributes::new()),
793    ///         Event::Start(Container::Paragraph, Attributes::new()),
794    ///         Event::Str("citrus fruit".into()),
795    ///         Event::End,
796    ///         Event::End,
797    ///         Event::Start(Container::DescriptionTerm, Attributes::new()),
798    ///         Event::Str("apple".into()),
799    ///         Event::End,
800    ///         Event::Blankline,
801    ///         Event::Start(Container::DescriptionDetails, Attributes::new()),
802    ///         Event::Start(Container::Paragraph, Attributes::new()),
803    ///         Event::Str("malus fruit".into()),
804    ///         Event::End,
805    ///         Event::End,
806    ///         Event::End,
807    ///     ],
808    /// );
809    /// let html = concat!(
810    ///     "<dl>\n",
811    ///     "<dt>orange</dt>\n",
812    ///     "<dd>\n",
813    ///     "<p>citrus fruit</p>\n",
814    ///     "</dd>\n",
815    ///     "<dt>apple</dt>\n",
816    ///     "<dd>\n",
817    ///     "<p>malus fruit</p>\n",
818    ///     "</dd>\n",
819    ///     "</dl>\n",
820    /// );
821    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
822    /// ```
823    DescriptionList,
824    /// Details describing a term within a description list.
825    DescriptionDetails,
826    /// A footnote definition.
827    ///
828    /// # Examples
829    ///
830    /// ```
831    /// # use jotup::*;
832    /// let src = concat!(
833    ///     "txt[^nb]\n",
834    ///     "\n",
835    ///     "[^nb]: actually..\n",
836    /// );
837    /// let events: Vec<_> = Parser::new(src).collect();
838    /// assert_eq!(
839    ///     &events,
840    ///     &[
841    ///         Event::Start(Container::Paragraph, Attributes::new()),
842    ///         Event::Str("txt".into()),
843    ///         Event::FootnoteReference("nb".into()),
844    ///         Event::End,
845    ///         Event::Blankline,
846    ///         Event::Start(
847    ///             Container::Footnote { label: "nb".into() },
848    ///             Attributes::new(),
849    ///         ),
850    ///         Event::Start(Container::Paragraph, Attributes::new()),
851    ///         Event::Str("actually..".into()),
852    ///         Event::End,
853    ///         Event::End,
854    ///     ],
855    /// );
856    /// let html = concat!(
857    ///     "<p>txt<a id=\"fnref1\" href=\"#fn1\" role=\"doc-noteref\"><sup>1</sup></a></p>\n",
858    ///     "<section role=\"doc-endnotes\">\n",
859    ///     "<hr>\n",
860    ///     "<ol>\n",
861    ///     "<li id=\"fn1\">\n",
862    ///     "<p>actually..<a href=\"#fnref1\" role=\"doc-backlink\">↩\u{fe0e}</a></p>\n",
863    ///     "</li>\n",
864    ///     "</ol>\n",
865    ///     "</section>\n",
866    /// );
867    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
868    /// ```
869    Footnote { label: CowStr<'s> },
870    /// A table element.
871    ///
872    /// # Examples
873    ///
874    /// ```
875    /// # use jotup::*;
876    /// let src = concat!(
877    ///     "| a | b |\n",
878    ///     "|---|--:|\n",
879    ///     "| 1 | 2 |\n",
880    /// );
881    /// let events: Vec<_> = Parser::new(src).collect();
882    /// assert_eq!(
883    ///     &events,
884    ///     &[
885    ///         Event::Start(Container::Table, Attributes::new()),
886    ///         Event::Start(
887    ///             Container::TableRow { head: true },
888    ///             Attributes::new(),
889    ///         ),
890    ///         Event::Start(
891    ///             Container::TableCell {
892    ///                 alignment: Alignment::Unspecified,
893    ///                 head: true
894    ///             },
895    ///             Attributes::new(),
896    ///         ),
897    ///         Event::Str("a".into()),
898    ///         Event::End,
899    ///         Event::Start(
900    ///             Container::TableCell {
901    ///                 alignment: Alignment::Right,
902    ///                 head: true,
903    ///             },
904    ///             Attributes::new(),
905    ///         ),
906    ///         Event::Str("b".into()),
907    ///         Event::End,
908    ///         Event::End,
909    ///         Event::Start(
910    ///             Container::TableRow { head: false },
911    ///             Attributes::new(),
912    ///         ),
913    ///         Event::Start(
914    ///             Container::TableCell {
915    ///                 alignment: Alignment::Unspecified,
916    ///                 head: false
917    ///             },
918    ///             Attributes::new(),
919    ///         ),
920    ///         Event::Str("1".into()),
921    ///         Event::End,
922    ///         Event::Start(
923    ///             Container::TableCell {
924    ///                 alignment: Alignment::Right,
925    ///                 head: false,
926    ///             },
927    ///             Attributes::new(),
928    ///         ),
929    ///         Event::Str("2".into()),
930    ///         Event::End,
931    ///         Event::End,
932    ///         Event::End,
933    ///     ],
934    /// );
935    /// let html = concat!(
936    ///     "<table>\n",
937    ///     "<tr>\n",
938    ///     "<th>a</th>\n",
939    ///     "<th style=\"text-align: right;\">b</th>\n",
940    ///     "</tr>\n",
941    ///     "<tr>\n",
942    ///     "<td>1</td>\n",
943    ///     "<td style=\"text-align: right;\">2</td>\n",
944    ///     "</tr>\n",
945    ///     "</table>\n",
946    /// );
947    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
948    /// ```
949    Table,
950    /// A row element of a table.
951    TableRow { head: bool },
952    /// A section belonging to a top level heading.
953    ///
954    /// # Examples
955    ///
956    /// ```
957    /// # use jotup::*;
958    /// let src = concat!(
959    ///     "# outer\n",
960    ///     "\n",
961    ///     "## inner\n",
962    /// );
963    /// let events: Vec<_> = Parser::new(src).collect();
964    /// assert_eq!(
965    ///     &events,
966    ///     &[
967    ///         Event::Start(
968    ///             Container::Section { id: "outer".into() },
969    ///             Attributes::new(),
970    ///         ),
971    ///         Event::Start(
972    ///             Container::Heading {
973    ///                 level: 1,
974    ///                 has_section: true,
975    ///                 id: "outer".into(),
976    ///             },
977    ///             Attributes::new(),
978    ///         ),
979    ///         Event::Str("outer".into()),
980    ///         Event::End,
981    ///         Event::Blankline,
982    ///         Event::Start(
983    ///             Container::Section { id: "inner".into() },
984    ///             Attributes::new(),
985    ///         ),
986    ///         Event::Start(
987    ///             Container::Heading {
988    ///                 level: 2,
989    ///                 has_section: true,
990    ///                 id: "inner".into(),
991    ///             },
992    ///             Attributes::new(),
993    ///         ),
994    ///         Event::Str("inner".into()),
995    ///         Event::End,
996    ///         Event::End,
997    ///         Event::End,
998    ///     ],
999    /// );
1000    /// let html = concat!(
1001    ///     "<section id=\"outer\">\n",
1002    ///     "<h1>outer</h1>\n",
1003    ///     "<section id=\"inner\">\n",
1004    ///     "<h2>inner</h2>\n",
1005    ///     "</section>\n",
1006    ///     "</section>\n",
1007    /// );
1008    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
1009    /// ```
1010    Section { id: CowStr<'s> },
1011    /// A block-level divider element.
1012    ///
1013    /// # Examples
1014    ///
1015    /// ```
1016    /// # use jotup::*;
1017    /// let src = concat!(
1018    ///     "::: note\n",
1019    ///     "this is a note\n",
1020    ///     ":::\n",
1021    /// );
1022    /// let events: Vec<_> = Parser::new(src).collect();
1023    /// assert_eq!(
1024    ///     &events,
1025    ///     &[
1026    ///         Event::Start(
1027    ///             Container::Div { class: "note".into() },
1028    ///             Attributes::new(),
1029    ///         ),
1030    ///         Event::Start(Container::Paragraph, Attributes::new()),
1031    ///         Event::Str("this is a note".into()),
1032    ///         Event::End,
1033    ///         Event::End,
1034    ///     ],
1035    /// );
1036    /// let html = concat!(
1037    ///     "<div class=\"note\">\n",
1038    ///     "<p>this is a note</p>\n",
1039    ///     "</div>\n",
1040    /// );
1041    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
1042    /// ```
1043    Div { class: CowStr<'s> },
1044    /// A paragraph.
1045    Paragraph,
1046    /// A heading.
1047    ///
1048    /// # Examples
1049    ///
1050    /// ```
1051    /// # use jotup::*;
1052    /// let src = "# heading";
1053    /// let events: Vec<_> = Parser::new(src).collect();
1054    /// assert_eq!(
1055    ///     &events,
1056    ///     &[
1057    ///         Event::Start(
1058    ///             Container::Section { id: "heading".into() },
1059    ///             Attributes::new(),
1060    ///         ),
1061    ///         Event::Start(
1062    ///             Container::Heading {
1063    ///                 level: 1,
1064    ///                 has_section: true,
1065    ///                 id: "heading".into(),
1066    ///             },
1067    ///             Attributes::new(),
1068    ///         ),
1069    ///         Event::Str("heading".into()),
1070    ///         Event::End,
1071    ///         Event::End,
1072    ///     ],
1073    /// );
1074    /// let html = concat!(
1075    ///     "<section id=\"heading\">\n",
1076    ///     "<h1>heading</h1>\n",
1077    ///     "</section>\n",
1078    /// );
1079    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
1080    /// ```
1081    Heading {
1082        level: u16,
1083        has_section: bool,
1084        id: CowStr<'s>,
1085    },
1086    /// A cell element of row within a table.
1087    TableCell { alignment: Alignment, head: bool },
1088    /// A caption within a table.
1089    ///
1090    /// # Examples
1091    ///
1092    /// ```
1093    /// # use jotup::*;
1094    /// let src = concat!(
1095    ///     "|a|\n",
1096    ///     "^ caption\n",
1097    /// );
1098    /// let events: Vec<_> = Parser::new(src).collect();
1099    /// assert_eq!(
1100    ///     &events,
1101    ///     &[
1102    ///         Event::Start(Container::Table, Attributes::new()),
1103    ///         Event::Start(Container::Caption, Attributes::new()),
1104    ///         Event::Str("caption".into()),
1105    ///         Event::End,
1106    ///         Event::Start(
1107    ///             Container::TableRow { head: false },
1108    ///             Attributes::new(),
1109    ///         ),
1110    ///         Event::Start(
1111    ///             Container::TableCell {
1112    ///                 alignment: Alignment::Unspecified,
1113    ///                 head: false
1114    ///             },
1115    ///             Attributes::new(),
1116    ///         ),
1117    ///         Event::Str("a".into()),
1118    ///         Event::End,
1119    ///         Event::End,
1120    ///         Event::End,
1121    ///     ],
1122    /// );
1123    /// let html = concat!(
1124    ///     "<table>\n",
1125    ///     "<caption>caption</caption>\n",
1126    ///     "<tr>\n",
1127    ///     "<td>a</td>\n",
1128    ///     "</tr>\n",
1129    ///     "</table>\n",
1130    /// );
1131    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
1132    /// ```
1133    Caption,
1134    /// A term within a description list.
1135    DescriptionTerm,
1136    /// A link definition.
1137    ///
1138    /// # Examples
1139    ///
1140    /// ```
1141    /// # use jotup::*;
1142    /// let src = "[label]: url";
1143    /// let events: Vec<_> = Parser::new(src).collect();
1144    /// assert_eq!(
1145    ///     &events,
1146    ///     &[
1147    ///         Event::Start(
1148    ///             Container::LinkDefinition { label: "label".into() },
1149    ///             Attributes::new(),
1150    ///         ),
1151    ///         Event::Str("url".into()),
1152    ///         Event::End,
1153    ///     ],
1154    /// );
1155    /// let html = "\n";
1156    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
1157    /// ```
1158    LinkDefinition { label: CowStr<'s> },
1159    /// A block with raw markup for a specific output format.
1160    ///
1161    /// # Examples
1162    ///
1163    /// ```
1164    /// # use jotup::*;
1165    /// let src = concat!(
1166    ///     "```=html\n",
1167    ///     "<tag>x</tag>\n",
1168    ///     "```\n",
1169    /// );
1170    /// let events: Vec<_> = Parser::new(src).collect();
1171    /// assert_eq!(
1172    ///     &events,
1173    ///     &[
1174    ///         Event::Start(
1175    ///             Container::RawBlock { format: "html".into() },
1176    ///             Attributes::new(),
1177    ///         ),
1178    ///         Event::Str("<tag>x</tag>".into()),
1179    ///         Event::End,
1180    ///     ],
1181    /// );
1182    /// let html = "<tag>x</tag>\n";
1183    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
1184    /// ```
1185    RawBlock { format: CowStr<'s> },
1186    /// A block with code in a specific language.
1187    ///
1188    /// # Examples
1189    ///
1190    /// ```
1191    /// # use jotup::*;
1192    /// let src = concat!(
1193    ///     "```html\n",
1194    ///     "<tag>x</tag>\n",
1195    ///     "```\n",
1196    /// );
1197    /// let events: Vec<_> = Parser::new(src).collect();
1198    /// assert_eq!(
1199    ///     &events,
1200    ///     &[
1201    ///         Event::Start(
1202    ///             Container::CodeBlock { language: "html".into() },
1203    ///             Attributes::new(),
1204    ///         ),
1205    ///         Event::Str("<tag>x</tag>\n".into()),
1206    ///         Event::End,
1207    ///     ],
1208    /// );
1209    /// let html = concat!(
1210    ///     "<pre><code class=\"language-html\">&lt;tag&gt;x&lt;/tag&gt;\n",
1211    ///     "</code></pre>\n",
1212    /// );
1213    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
1214    /// ```
1215    CodeBlock { language: CowStr<'s> },
1216    /// An inline divider element.
1217    ///
1218    /// # Examples
1219    ///
1220    /// Can be used to add attributes:
1221    ///
1222    /// ```
1223    /// # use jotup::*;
1224    /// let src = concat!(
1225    ///     "word{#a}\n",
1226    ///     "[two words]{#b}\n",
1227    /// );
1228    /// let events: Vec<_> = Parser::new(src).collect();
1229    /// assert_eq!(
1230    ///     &events,
1231    ///     &[
1232    ///         Event::Start(Container::Paragraph, Attributes::new()),
1233    ///         Event::Start(
1234    ///             Container::Span,
1235    ///             [(AttributeKind::Id, "a".into())].into_iter().collect(),
1236    ///         ),
1237    ///         Event::Str("word".into()),
1238    ///         Event::End,
1239    ///         Event::Softbreak,
1240    ///         Event::Start(
1241    ///             Container::Span,
1242    ///             [(AttributeKind::Id, "b".into())].into_iter().collect(),
1243    ///         ),
1244    ///         Event::Str("two words".into()),
1245    ///         Event::End,
1246    ///         Event::End,
1247    ///     ],
1248    /// );
1249    /// let html = concat!(
1250    ///     "<p><span id=\"a\">word</span>\n",
1251    ///     "<span id=\"b\">two words</span></p>\n",
1252    /// );
1253    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
1254    /// ```
1255    Span,
1256    /// An inline link, the first field is either a destination URL or an unresolved tag.
1257    ///
1258    /// # Examples
1259    ///
1260    /// URLs or email addresses can be enclosed with angled brackets to create a hyperlink:
1261    ///
1262    /// ```
1263    /// # use jotup::*;
1264    /// let src = concat!(
1265    ///     "<https://example.com>\n",
1266    ///     "<me@example.com>\n",
1267    /// );
1268    /// let events: Vec<_> = Parser::new(src).collect();
1269    /// assert_eq!(
1270    ///     &events,
1271    ///     &[
1272    ///         Event::Start(Container::Paragraph, Attributes::new()),
1273    ///         Event::Start(
1274    ///             Container::Link(
1275    ///                 "https://example.com".into(),
1276    ///                 LinkType::AutoLink,
1277    ///             ),
1278    ///             Attributes::new(),
1279    ///         ),
1280    ///         Event::Str("https://example.com".into()),
1281    ///         Event::End,
1282    ///         Event::Softbreak,
1283    ///         Event::Start(
1284    ///             Container::Link(
1285    ///                 "me@example.com".into(),
1286    ///                 LinkType::Email,
1287    ///             ),
1288    ///             Attributes::new(),
1289    ///         ),
1290    ///         Event::Str("me@example.com".into()),
1291    ///         Event::End,
1292    ///         Event::End,
1293    ///     ],
1294    /// );
1295    /// let html = concat!(
1296    ///     "<p><a href=\"https://example.com\">https://example.com</a>\n",
1297    ///     "<a href=\"mailto:me@example.com\">me@example.com</a></p>\n",
1298    /// );
1299    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
1300    /// ```
1301    ///
1302    /// Anchor text and the URL can be specified inline:
1303    ///
1304    /// ```
1305    /// # use jotup::*;
1306    /// let src = "[anchor](url)\n";
1307    /// let events: Vec<_> = Parser::new(src).collect();
1308    /// assert_eq!(
1309    ///     &events,
1310    ///     &[
1311    ///         Event::Start(Container::Paragraph, Attributes::new()),
1312    ///         Event::Start(
1313    ///             Container::Link(
1314    ///                 "url".into(),
1315    ///                 LinkType::Span(SpanLinkType::Inline),
1316    ///             ),
1317    ///             Attributes::new(),
1318    ///         ),
1319    ///         Event::Str("anchor".into()),
1320    ///         Event::End,
1321    ///         Event::End,
1322    ///     ],
1323    /// );
1324    /// let html = "<p><a href=\"url\">anchor</a></p>\n";
1325    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
1326    /// ```
1327    ///
1328    /// Alternatively, the URL can be retrieved from a link definition using hard brackets, if it
1329    /// exists:
1330    ///
1331    /// ```
1332    /// # use jotup::*;
1333    /// let src = concat!(
1334    ///     "[a][label]\n",
1335    ///     "[b][non-existent]\n",
1336    ///     "\n",
1337    ///     "[label]: url\n",
1338    /// );
1339    /// let events: Vec<_> = Parser::new(src).collect();
1340    /// assert_eq!(
1341    ///     &events,
1342    ///     &[
1343    ///         Event::Start(Container::Paragraph, Attributes::new()),
1344    ///         Event::Start(
1345    ///             Container::Link(
1346    ///                 "url".into(),
1347    ///                 LinkType::Span(SpanLinkType::Reference),
1348    ///             ),
1349    ///             Attributes::new(),
1350    ///         ),
1351    ///         Event::Str("a".into()),
1352    ///         Event::End,
1353    ///         Event::Softbreak,
1354    ///         Event::Start(
1355    ///             Container::Link(
1356    ///                 "non-existent".into(),
1357    ///                 LinkType::Span(SpanLinkType::Unresolved),
1358    ///             ),
1359    ///             Attributes::new(),
1360    ///         ),
1361    ///         Event::Str("b".into()),
1362    ///         Event::End,
1363    ///         Event::End,
1364    ///         Event::Blankline,
1365    ///         Event::Start(
1366    ///             Container::LinkDefinition { label: "label".into() },
1367    ///             Attributes::new(),
1368    ///         ),
1369    ///         Event::Str("url".into()),
1370    ///         Event::End,
1371    ///     ],
1372    /// );
1373    /// let html = concat!(
1374    ///     "<p><a href=\"url\">a</a>\n",
1375    ///     "<a>b</a></p>\n",
1376    /// );
1377    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
1378    /// ```
1379    Link(CowStr<'s>, LinkType),
1380    /// An inline image, the first field is either a destination URL or an unresolved tag.
1381    ///
1382    /// # Examples
1383    ///
1384    /// Inner Str objects compose the alternative text:
1385    ///
1386    /// ```
1387    /// # use jotup::*;
1388    /// let src = "![alt text](img.png)";
1389    /// let events: Vec<_> = Parser::new(src).collect();
1390    /// assert_eq!(
1391    ///     &events,
1392    ///     &[
1393    ///         Event::Start(Container::Paragraph, Attributes::new()),
1394    ///         Event::Start(
1395    ///             Container::Image("img.png".into(), SpanLinkType::Inline),
1396    ///             Attributes::new(),
1397    ///         ),
1398    ///         Event::Str("alt text".into()),
1399    ///         Event::End,
1400    ///         Event::End,
1401    ///     ],
1402    /// );
1403    /// let html = "<p><img alt=\"alt text\" src=\"img.png\"></p>\n";
1404    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
1405    /// ```
1406    Image(CowStr<'s>, SpanLinkType),
1407    /// An inline verbatim string.
1408    ///
1409    /// # Examples
1410    ///
1411    /// ```
1412    /// # use jotup::*;
1413    /// let src = "inline `verbatim`";
1414    /// let events: Vec<_> = Parser::new(src).collect();
1415    /// assert_eq!(
1416    ///     &events,
1417    ///     &[
1418    ///         Event::Start(Container::Paragraph, Attributes::new()),
1419    ///         Event::Str("inline ".into()),
1420    ///         Event::Start(Container::Verbatim, Attributes::new()),
1421    ///         Event::Str("verbatim".into()),
1422    ///         Event::End,
1423    ///         Event::End,
1424    ///     ],
1425    /// );
1426    /// let html = "<p>inline <code>verbatim</code></p>\n";
1427    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
1428    /// ```
1429    Verbatim,
1430    /// An inline or display math element.
1431    ///
1432    /// # Examples
1433    ///
1434    /// ```
1435    /// # use jotup::*;
1436    /// let src = concat!(
1437    ///     "inline $`a\\cdot{}b` or\n",
1438    ///     "display $$`\\frac{a}{b}`\n",
1439    /// );
1440    /// let events: Vec<_> = Parser::new(src).collect();
1441    /// assert_eq!(
1442    ///     &events,
1443    ///     &[
1444    ///         Event::Start(Container::Paragraph, Attributes::new()),
1445    ///         Event::Str("inline ".into()),
1446    ///         Event::Start(
1447    ///             Container::Math { display: false },
1448    ///             Attributes::new(),
1449    ///         ),
1450    ///         Event::Str(r"a\cdot{}b".into()),
1451    ///         Event::End,
1452    ///         Event::Str(" or".into()),
1453    ///         Event::Softbreak,
1454    ///         Event::Str("display ".into()),
1455    ///         Event::Start(
1456    ///             Container::Math { display: true },
1457    ///             Attributes::new(),
1458    ///         ),
1459    ///         Event::Str(r"\frac{a}{b}".into()),
1460    ///         Event::End,
1461    ///         Event::End,
1462    ///     ],
1463    /// );
1464    /// let html = concat!(
1465    ///     "<p>inline <span class=\"math inline\">\\(a\\cdot{}b\\)</span> or\n",
1466    ///     "display <span class=\"math display\">\\[\\frac{a}{b}\\]</span></p>\n",
1467    /// );
1468    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
1469    /// ```
1470    Math { display: bool },
1471    /// Inline raw markup for a specific output format.
1472    ///
1473    /// # Examples
1474    ///
1475    /// ```
1476    /// # use jotup::*;
1477    /// let src = "`<tag>a</tag>`{=html}";
1478    /// let events: Vec<_> = Parser::new(src).collect();
1479    /// assert_eq!(
1480    ///     &events,
1481    ///     &[
1482    ///         Event::Start(Container::Paragraph, Attributes::new()),
1483    ///         Event::Start(
1484    ///             Container::RawInline { format: "html".into() }, Attributes::new(),
1485    ///         ),
1486    ///         Event::Str("<tag>a</tag>".into()),
1487    ///         Event::End,
1488    ///         Event::End,
1489    ///     ],
1490    /// );
1491    /// let html = "<p><tag>a</tag></p>\n";
1492    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
1493    /// ```
1494    RawInline { format: CowStr<'s> },
1495    /// A subscripted element.
1496    ///
1497    /// # Examples
1498    ///
1499    /// ```
1500    /// # use jotup::*;
1501    /// let src = "~SUB~";
1502    /// let events: Vec<_> = Parser::new(src).collect();
1503    /// assert_eq!(
1504    ///     &events,
1505    ///     &[
1506    ///         Event::Start(Container::Paragraph, Attributes::new()),
1507    ///         Event::Start(Container::Subscript, Attributes::new()),
1508    ///         Event::Str("SUB".into()),
1509    ///         Event::End,
1510    ///         Event::End,
1511    ///     ],
1512    /// );
1513    /// let html = "<p><sub>SUB</sub></p>\n";
1514    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
1515    /// ```
1516    Subscript,
1517    /// A superscripted element.
1518    ///
1519    /// # Examples
1520    ///
1521    /// ```
1522    /// # use jotup::*;
1523    /// let src = "^SUP^";
1524    /// let events: Vec<_> = Parser::new(src).collect();
1525    /// assert_eq!(
1526    ///     &events,
1527    ///     &[
1528    ///         Event::Start(Container::Paragraph, Attributes::new()),
1529    ///         Event::Start(Container::Superscript, Attributes::new()),
1530    ///         Event::Str("SUP".into()),
1531    ///         Event::End,
1532    ///         Event::End,
1533    ///     ],
1534    /// );
1535    /// let html = "<p><sup>SUP</sup></p>\n";
1536    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
1537    /// ```
1538    Superscript,
1539    /// An inserted inline element.
1540    ///
1541    /// # Examples
1542    ///
1543    /// ```
1544    /// # use jotup::*;
1545    /// let src = "{+INS+}";
1546    /// let events: Vec<_> = Parser::new(src).collect();
1547    /// assert_eq!(
1548    ///     &events,
1549    ///     &[
1550    ///         Event::Start(Container::Paragraph, Attributes::new()),
1551    ///         Event::Start(Container::Insert, Attributes::new()),
1552    ///         Event::Str("INS".into()),
1553    ///         Event::End,
1554    ///         Event::End,
1555    ///     ],
1556    /// );
1557    /// let html = "<p><ins>INS</ins></p>\n";
1558    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
1559    /// ```
1560    Insert,
1561    /// A deleted inline element.
1562    ///
1563    /// # Examples
1564    ///
1565    /// ```
1566    /// # use jotup::*;
1567    /// let src = "{-DEL-}";
1568    /// let events: Vec<_> = Parser::new(src).collect();
1569    /// assert_eq!(
1570    ///     &events,
1571    ///     &[
1572    ///         Event::Start(Container::Paragraph, Attributes::new()),
1573    ///         Event::Start(Container::Delete, Attributes::new()),
1574    ///         Event::Str("DEL".into()),
1575    ///         Event::End,
1576    ///         Event::End,
1577    ///     ],
1578    /// );
1579    /// let html = "<p><del>DEL</del></p>\n";
1580    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
1581    /// ```
1582    Delete,
1583    /// An inline element emphasized with a bold typeface.
1584    ///
1585    /// # Examples
1586    ///
1587    /// ```
1588    /// # use jotup::*;
1589    /// let src = "*STRONG*";
1590    /// let events: Vec<_> = Parser::new(src).collect();
1591    /// assert_eq!(
1592    ///     &events,
1593    ///     &[
1594    ///         Event::Start(Container::Paragraph, Attributes::new()),
1595    ///         Event::Start(Container::Strong, Attributes::new()),
1596    ///         Event::Str("STRONG".into()),
1597    ///         Event::End,
1598    ///         Event::End,
1599    ///     ],
1600    /// );
1601    /// let html = "<p><strong>STRONG</strong></p>\n";
1602    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
1603    /// ```
1604    Strong,
1605    /// An emphasized inline element.
1606    ///
1607    /// # Examples
1608    ///
1609    /// ```
1610    /// # use jotup::*;
1611    /// let src = "_EM_";
1612    /// let events: Vec<_> = Parser::new(src).collect();
1613    /// assert_eq!(
1614    ///     &events,
1615    ///     &[
1616    ///         Event::Start(Container::Paragraph, Attributes::new()),
1617    ///         Event::Start(Container::Emphasis, Attributes::new()),
1618    ///         Event::Str("EM".into()),
1619    ///         Event::End,
1620    ///         Event::End,
1621    ///     ],
1622    /// );
1623    /// let html = "<p><em>EM</em></p>\n";
1624    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
1625    /// ```
1626    Emphasis,
1627    /// A highlighted inline element.
1628    ///
1629    /// # Examples
1630    ///
1631    /// ```
1632    /// # use jotup::*;
1633    /// let src = "{=MARK=}";
1634    /// let events: Vec<_> = Parser::new(src).collect();
1635    /// assert_eq!(
1636    ///     &events,
1637    ///     &[
1638    ///         Event::Start(Container::Paragraph, Attributes::new()),
1639    ///         Event::Start(Container::Mark, Attributes::new()),
1640    ///         Event::Str("MARK".into()),
1641    ///         Event::End,
1642    ///         Event::End,
1643    ///     ],
1644    /// );
1645    /// let html = "<p><mark>MARK</mark></p>\n";
1646    /// assert_eq!(&html::Renderer::default().render_events_to_string(events.into_iter()), html);
1647    /// ```
1648    Mark,
1649}
1650
1651impl Container<'_> {
1652    /// Is a block element.
1653    #[must_use]
1654    pub fn is_block(&self) -> bool {
1655        match self {
1656            Self::Document
1657            | Self::Blockquote
1658            | Self::List { .. }
1659            | Self::ListItem
1660            | Self::TaskListItem { .. }
1661            | Self::DescriptionList
1662            | Self::DescriptionDetails
1663            | Self::Footnote { .. }
1664            | Self::Table
1665            | Self::TableRow { .. }
1666            | Self::Section { .. }
1667            | Self::Div { .. }
1668            | Self::Paragraph
1669            | Self::Heading { .. }
1670            | Self::TableCell { .. }
1671            | Self::Caption
1672            | Self::DescriptionTerm
1673            | Self::LinkDefinition { .. }
1674            | Self::RawBlock { .. }
1675            | Self::CodeBlock { .. } => true,
1676            Self::Span
1677            | Self::Link(..)
1678            | Self::Image(..)
1679            | Self::Verbatim
1680            | Self::Math { .. }
1681            | Self::RawInline { .. }
1682            | Self::Subscript
1683            | Self::Superscript
1684            | Self::Insert
1685            | Self::Delete
1686            | Self::Strong
1687            | Self::Emphasis
1688            | Self::Mark => false,
1689        }
1690    }
1691
1692    /// Is a block element that may contain children blocks.
1693    #[must_use]
1694    pub fn is_block_container(&self) -> bool {
1695        match self {
1696            Self::Document
1697            | Self::Blockquote
1698            | Self::List { .. }
1699            | Self::ListItem
1700            | Self::TaskListItem { .. }
1701            | Self::DescriptionList
1702            | Self::DescriptionDetails
1703            | Self::Footnote { .. }
1704            | Self::Table
1705            | Self::TableRow { .. }
1706            | Self::Section { .. }
1707            | Self::Div { .. } => true,
1708            Self::Paragraph
1709            | Self::Heading { .. }
1710            | Self::TableCell { .. }
1711            | Self::Caption
1712            | Self::DescriptionTerm
1713            | Self::LinkDefinition { .. }
1714            | Self::RawBlock { .. }
1715            | Self::CodeBlock { .. }
1716            | Self::Span
1717            | Self::Link(..)
1718            | Self::Image(..)
1719            | Self::Verbatim
1720            | Self::Math { .. }
1721            | Self::RawInline { .. }
1722            | Self::Subscript
1723            | Self::Superscript
1724            | Self::Insert
1725            | Self::Delete
1726            | Self::Strong
1727            | Self::Emphasis
1728            | Self::Mark => false,
1729        }
1730    }
1731}
1732
1733/// Alignment of a table column.
1734#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1735pub enum Alignment {
1736    Unspecified,
1737    Left,
1738    Center,
1739    Right,
1740}
1741
1742/// The type of an inline span link.
1743#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1744pub enum SpanLinkType {
1745    /// E.g. `[text](url)`
1746    Inline,
1747    /// In the form `[text][tag]` or `[tag][]`.
1748    Reference,
1749    /// Like reference, but the tag is unresolved.
1750    Unresolved,
1751}
1752
1753/// The type of an inline link.
1754#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1755pub enum LinkType {
1756    /// E.g. `[text](url)`.
1757    Span(SpanLinkType),
1758    /// In the form `<url>`.
1759    AutoLink,
1760    /// In the form `<address>`.
1761    Email,
1762}
1763
1764/// Character used to create an unordered list item.
1765#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1766pub enum ListBulletType {
1767    /// `-`
1768    Dash,
1769    /// `*`
1770    Star,
1771    /// `+`
1772    Plus,
1773}
1774
1775impl TryFrom<u8> for ListBulletType {
1776    type Error = ();
1777
1778    fn try_from(c: u8) -> Result<Self, Self::Error> {
1779        match c {
1780            b'-' => Ok(Self::Dash),
1781            b'*' => Ok(Self::Star),
1782            b'+' => Ok(Self::Plus),
1783            _ => Err(()),
1784        }
1785    }
1786}
1787
1788impl TryFrom<char> for ListBulletType {
1789    type Error = ();
1790
1791    fn try_from(c: char) -> Result<Self, Self::Error> {
1792        u8::try_from(u32::from(c))
1793            .map_err(|_| ())
1794            .and_then(Self::try_from)
1795    }
1796}
1797
1798impl From<ListBulletType> for u8 {
1799    fn from(t: ListBulletType) -> Self {
1800        match t {
1801            ListBulletType::Dash => b'-',
1802            ListBulletType::Star => b'*',
1803            ListBulletType::Plus => b'+',
1804        }
1805    }
1806}
1807
1808impl From<ListBulletType> for char {
1809    fn from(t: ListBulletType) -> Self {
1810        u8::from(t).into()
1811    }
1812}
1813
1814/// The type of a list.
1815#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1816pub enum ListKind {
1817    /// A bullet list.
1818    Unordered(ListBulletType),
1819    /// An enumerated list.
1820    Ordered {
1821        numbering: OrderedListNumbering,
1822        style: OrderedListStyle,
1823        start: u64,
1824    },
1825    /// A task list.
1826    Task(ListBulletType),
1827}
1828
1829/// Numbering type of an ordered list.
1830#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1831pub enum OrderedListNumbering {
1832    /// Decimal numbering, e.g. `1)`.
1833    Decimal,
1834    /// Lowercase alphabetic numbering, e.g. `a)`.
1835    AlphaLower,
1836    /// Uppercase alphabetic numbering, e.g. `A)`.
1837    AlphaUpper,
1838    /// Lowercase roman or alphabetic numbering, e.g. `iv)`.
1839    RomanLower,
1840    /// Uppercase roman or alphabetic numbering, e.g. `IV)`.
1841    RomanUpper,
1842}
1843
1844/// Style of an ordered list.
1845#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1846pub enum OrderedListStyle {
1847    /// Number is followed by a period, e.g. `1.`.
1848    Period,
1849    /// Number is followed by a closing parenthesis, e.g. `1)`.
1850    Paren,
1851    /// Number is enclosed by parentheses, e.g. `(1)`.
1852    ParenParen,
1853}
1854
1855impl OrderedListNumbering {
1856    fn parse_number(self, n: &str) -> u64 {
1857        match self {
1858            Self::Decimal => n.parse().unwrap(),
1859            Self::AlphaLower | Self::AlphaUpper => {
1860                let d0 = u64::from(if matches!(self, Self::AlphaLower) {
1861                    b'a'
1862                } else {
1863                    b'A'
1864                });
1865                let weights = (1..=n.len()).scan(1, |a, _| {
1866                    let prev = *a;
1867                    *a *= 26;
1868                    Some(prev)
1869                });
1870                n.as_bytes()
1871                    .iter()
1872                    .rev()
1873                    .copied()
1874                    .map(u64::from)
1875                    .zip(weights)
1876                    .map(|(d, w)| w * (d - d0 + 1))
1877                    .sum()
1878            }
1879            Self::RomanLower | Self::RomanUpper => {
1880                fn value(d: char) -> u64 {
1881                    match d {
1882                        'i' | 'I' => 1,
1883                        'v' | 'V' => 5,
1884                        'x' | 'X' => 10,
1885                        'l' | 'L' => 50,
1886                        'c' | 'C' => 100,
1887                        'd' | 'D' => 500,
1888                        'm' | 'M' => 1000,
1889                        _ => panic!(),
1890                    }
1891                }
1892                let mut prev = 0;
1893                let mut sum = 0;
1894                for d in n.chars().rev() {
1895                    let v = value(d);
1896                    if v < prev {
1897                        sum -= v;
1898                    } else {
1899                        sum += v;
1900                    }
1901                    prev = v;
1902                }
1903                sum
1904            }
1905        }
1906    }
1907}
1908
1909impl OrderedListStyle {
1910    fn number(self, marker: &str) -> &str {
1911        &marker[usize::from(matches!(self, Self::ParenParen))..marker.len() - 1]
1912    }
1913}
1914
1915#[cfg(not(feature = "deterministic"))]
1916type Map<K, V> = std::collections::HashMap<K, V>;
1917#[cfg(feature = "deterministic")]
1918type Map<K, V> = std::collections::BTreeMap<K, V>;
1919
1920#[cfg(not(feature = "deterministic"))]
1921type Set<T> = std::collections::HashSet<T>;
1922#[cfg(feature = "deterministic")]
1923type Set<T> = std::collections::BTreeSet<T>;
1924
1925/// A parser that generates [`Event`]s from a Djot document.
1926///
1927/// When created, it will perform an initial pass and build up a tree of the document's block
1928/// structure that will be kept for the duration of the parser's lifetime. Then, when the iterator
1929/// is advanced, the parser will start from the beginning of the document and parse inline elements
1930/// and emit [`Event`]s.
1931///
1932/// It is possible to clone the parser to e.g. avoid performing the block parsing multiple times.
1933#[derive(Clone)]
1934pub struct Parser<'s> {
1935    src: &'s str,
1936
1937    /// Block tree parsed at first.
1938    blocks: std::iter::Peekable<std::vec::IntoIter<block::Event<'s>>>,
1939
1940    /// Contents obtained by the prepass.
1941    pre_pass: PrePass<'s>,
1942
1943    /// Last parsed block attributes, and its span.
1944    block_attributes: Option<(Attributes<'s>, std::ops::Range<usize>)>,
1945
1946    /// Current table row is a head row.
1947    table_head_row: bool,
1948
1949    /// Currently within a verbatim code block.
1950    verbatim: bool,
1951
1952    /// Inline parser.
1953    inline_parser: inline::Parser<'s>,
1954}
1955
1956#[derive(Clone)]
1957struct Heading {
1958    /// Location of heading in src.
1959    location: u32,
1960    /// Automatically generated id from heading text.
1961    id_auto: String,
1962    /// Text of heading, formatting stripped.
1963    text: String,
1964    /// Overriding id from an explicit attribute on the heading.
1965    id_override: Option<String>,
1966}
1967
1968/// Because of potential future references, an initial pass is required to obtain all definitions.
1969#[derive(Clone)]
1970struct PrePass<'s> {
1971    /// Link definitions and their attributes.
1972    link_definitions: Map<&'s str, (CowStr<'s>, attr::Attributes<'s>)>,
1973    /// Cache of all heading ids.
1974    headings: Vec<Heading>,
1975    /// Indices to headings sorted lexicographically.
1976    headings_lex: Vec<usize>,
1977}
1978
1979impl<'s> PrePass<'s> {
1980    #[must_use]
1981    fn new(
1982        src: &'s str,
1983        mut blocks: std::slice::Iter<block::Event<'s>>,
1984        inline_parser: &mut inline::Parser<'s>,
1985    ) -> Self {
1986        use std::fmt::Write;
1987        let mut link_definitions = Map::new();
1988        let mut headings: Vec<Heading> = Vec::new();
1989        let mut used_ids: Set<String> = Set::new();
1990
1991        let mut attr_prev: Vec<std::ops::Range<usize>> = Vec::new();
1992        while let Some(e) = blocks.next() {
1993            match e.kind {
1994                block::EventKind::Enter(block::Node::Leaf(block::Leaf::LinkDefinition {
1995                    label,
1996                })) => {
1997                    // All link definition tags have to be obtained initially, as references can
1998                    // appear before the definition.
1999                    let attrs = Attributes::from_iter(attr_prev.iter().flat_map(|sp| {
2000                        Attributes::try_from(&src[sp.clone()]).expect("should be valid")
2001                    }));
2002                    let url = if let Some(block::Event {
2003                        kind: block::EventKind::Inline,
2004                        span,
2005                    }) = blocks.next()
2006                    {
2007                        let start =
2008                            src[span.clone()].trim_matches(|c: char| c.is_ascii_whitespace());
2009                        if let Some(block::Event {
2010                            kind: block::EventKind::Inline,
2011                            span,
2012                        }) = blocks.next()
2013                        {
2014                            let mut url = start.to_string();
2015                            url.push_str(
2016                                src[span.clone()].trim_matches(|c: char| c.is_ascii_whitespace()),
2017                            );
2018                            while let Some(block::Event {
2019                                kind: block::EventKind::Inline,
2020                                span,
2021                            }) = blocks.next()
2022                            {
2023                                url.push_str(
2024                                    src[span.clone()]
2025                                        .trim_matches(|c: char| c.is_ascii_whitespace()),
2026                                );
2027                            }
2028                            url.into() // owned
2029                        } else {
2030                            start.into() // borrowed
2031                        }
2032                    } else {
2033                        "".into() // static
2034                    };
2035                    link_definitions.insert(label, (url, attrs));
2036                }
2037                block::EventKind::Enter(block::Node::Leaf(block::Leaf::Heading { .. })) => {
2038                    // All headings ids have to be obtained initially, as references can appear
2039                    // before the heading. Additionally, determining the id requires inline parsing
2040                    // as formatting must be removed.
2041                    //
2042                    // We choose to parse all headers twice instead of caching them.
2043                    let attrs = Attributes::from_iter(attr_prev.iter().flat_map(|sp| {
2044                        Attributes::try_from(&src[sp.clone()]).expect("should be valid")
2045                    }));
2046                    let id_override = attrs.get_value("id").map(|s| s.to_string());
2047
2048                    let mut id_auto = String::new();
2049                    let mut text = String::new();
2050                    let mut last_whitespace = true;
2051                    inline_parser.reset();
2052                    let mut last_end = 0;
2053                    loop {
2054                        let span_inline = blocks.next().and_then(|e| {
2055                            if matches!(e.kind, block::EventKind::Inline) {
2056                                last_end = e.span.end;
2057                                Some(e.span.clone())
2058                            } else {
2059                                None
2060                            }
2061                        });
2062                        inline_parser.feed_line(
2063                            span_inline.as_ref().cloned().unwrap_or(last_end..last_end),
2064                            span_inline.is_none(),
2065                        );
2066                        inline_parser.for_each(|ev| match ev.kind {
2067                            inline::EventKind::Str => {
2068                                text.push_str(&src[ev.span.clone()]);
2069                                let mut chars = src[ev.span].chars().peekable();
2070                                while let Some(c) = chars.next() {
2071                                    if c.is_ascii_whitespace() {
2072                                        while chars.peek().is_some_and(|c| c.is_ascii_whitespace())
2073                                        {
2074                                            chars.next();
2075                                        }
2076                                        if !last_whitespace {
2077                                            last_whitespace = true;
2078                                            id_auto.push('-');
2079                                        }
2080                                    } else if !c.is_ascii_punctuation() || matches!(c, '-' | '_') {
2081                                        id_auto.push(c);
2082                                        last_whitespace = false;
2083                                    }
2084                                }
2085                            }
2086                            inline::EventKind::Atom(inline::Atom::Softbreak) => {
2087                                text.push(' ');
2088                                id_auto.push('-');
2089                            }
2090                            _ => {}
2091                        });
2092                        if span_inline.is_none() {
2093                            break;
2094                        }
2095                    }
2096                    id_auto.drain(id_auto.trim_end_matches('-').len()..);
2097
2098                    // ensure id unique
2099                    if used_ids.contains::<str>(&id_auto) || id_auto.is_empty() {
2100                        if id_auto.is_empty() {
2101                            id_auto.push('s');
2102                        }
2103                        let mut num = 1;
2104                        id_auto.push('-');
2105                        let i_num = id_auto.len();
2106                        write!(id_auto, "{}", num).unwrap();
2107                        while used_ids.contains::<str>(&id_auto) {
2108                            num += 1;
2109                            id_auto.drain(i_num..);
2110                            write!(id_auto, "{}", num).unwrap();
2111                        }
2112                    }
2113
2114                    used_ids.insert(id_auto.clone());
2115                    headings.push(Heading {
2116                        location: e.span.start as u32,
2117                        id_auto,
2118                        text,
2119                        id_override,
2120                    });
2121                }
2122                block::EventKind::Atom(block::Atom::Attributes) => {
2123                    attr_prev.push(e.span.clone());
2124                }
2125                block::EventKind::Enter(..)
2126                | block::EventKind::Exit(block::Node::Container(block::Container::Section {
2127                    ..
2128                })) => {}
2129                _ => {
2130                    attr_prev = Vec::new();
2131                }
2132            }
2133        }
2134
2135        let mut headings_lex = (0..headings.len()).collect::<Vec<_>>();
2136        headings_lex.sort_by_key(|i| &headings[*i].text);
2137
2138        Self {
2139            link_definitions,
2140            headings,
2141            headings_lex,
2142        }
2143    }
2144
2145    fn heading_id(&self, i: usize) -> &str {
2146        let h = &self.headings[i];
2147        h.id_override.as_ref().unwrap_or(&h.id_auto)
2148    }
2149
2150    fn heading_id_by_location(&self, location: u32) -> Option<&str> {
2151        self.headings
2152            .binary_search_by_key(&location, |h| h.location)
2153            .ok()
2154            .map(|i| self.heading_id(i))
2155    }
2156
2157    fn heading_id_by_tag(&self, tag: &str) -> Option<&str> {
2158        self.headings_lex
2159            .binary_search_by_key(&tag, |i| &self.headings[*i].text)
2160            .ok()
2161            .map(|i| self.heading_id(self.headings_lex[i]))
2162    }
2163}
2164
2165impl<'s> Parser<'s> {
2166    #[must_use]
2167    pub fn new(src: &'s str) -> Self {
2168        let blocks = block::parse(src);
2169        let mut inline_parser = inline::Parser::new(src);
2170        let pre_pass = PrePass::new(src, blocks.iter(), &mut inline_parser);
2171
2172        Self {
2173            src,
2174            blocks: blocks.into_iter().peekable(),
2175            pre_pass,
2176            block_attributes: None,
2177            table_head_row: false,
2178            verbatim: false,
2179            inline_parser,
2180        }
2181    }
2182
2183    /// Turn the [`Parser`] into an iterator of tuples, each with an [`Event`] and a start/end byte
2184    /// offset for its corresponding input (as a [`std::ops::Range<usize>`]).
2185    ///
2186    /// Generally, the range of each event does not overlap with any other event and the ranges are
2187    /// in same order as the events are emitted, i.e. the start offset of an event must be greater
2188    /// or equal to the (exclusive) end offset of all events that were emitted before that event.
2189    /// However, there is an exception to this rule:
2190    ///
2191    /// - Caption events are emitted before the table rows while the input for the caption content
2192    ///   is located after the table rows, causing the ranges to be out of order.
2193    ///
2194    /// Characters between events, that are not part of any event range, are typically whitespace
2195    /// but may also consist of unattached attributes or `>` characters from blockquotes.
2196    ///
2197    /// # Examples
2198    ///
2199    /// Start and end events of containers correspond only to the start and end markers for that
2200    /// container, not its inner content:
2201    ///
2202    /// ```
2203    /// # use jotup::*;
2204    /// # use jotup::Event::*;
2205    /// # use jotup::Container::*;
2206    /// let input = "> _hello_ [text](url)\n";
2207    /// assert!(matches!(
2208    ///     Parser::new(input)
2209    ///         .into_offset_iter()
2210    ///         .map(|(e, r)| (&input[r], e))
2211    ///         .collect::<Vec<_>>()
2212    ///         .as_slice(),
2213    ///     &[
2214    ///         (">", Start(Blockquote, ..)),
2215    ///         ("", Start(Paragraph, ..)),
2216    ///         ("_", Start(Emphasis, ..)),
2217    ///         ("hello", Str(..)),
2218    ///         ("_", End),
2219    ///         (" ", Str(..)),
2220    ///         ("[", Start(Link { .. }, ..)),
2221    ///         ("text", Str(..)),
2222    ///         ("](url)", End),
2223    ///         ("", End),
2224    ///         ("", End),
2225    ///     ],
2226    /// ));
2227    /// ```
2228    ///
2229    /// _Block_ attributes that belong to a container are included in the  _start_ event.  _Inline_
2230    /// attributes that belong to a container are included in the _end_ event:
2231    ///
2232    /// ```
2233    /// # use jotup::*;
2234    /// # use jotup::Event::*;
2235    /// # use jotup::Container::*;
2236    /// let input = "
2237    /// {.quote}
2238    /// > [Hello]{lang=en} world!";
2239    /// assert!(matches!(
2240    ///     Parser::new(input)
2241    ///         .into_offset_iter()
2242    ///         .map(|(e, r)| (&input[r], e))
2243    ///         .collect::<Vec<_>>()
2244    ///         .as_slice(),
2245    ///     &[
2246    ///         ("\n", Blankline),
2247    ///         ("{.quote}\n>", Start(Blockquote, ..)),
2248    ///         ("", Start(Paragraph, ..)),
2249    ///         ("[", Start(Span, ..)),
2250    ///         ("Hello", Str(..)),
2251    ///         ("]{lang=en}", End),
2252    ///         (" world!", Str(..)),
2253    ///         ("", End),
2254    ///         ("", End),
2255    ///     ],
2256    /// ));
2257    /// ```
2258    ///
2259    /// Inline events that span multiple lines may contain characters from outer block containers
2260    /// (e.g. `>` characters from blockquotes or whitespace from list items):
2261    ///
2262    /// ```
2263    /// # use jotup::*;
2264    /// # use jotup::Event::*;
2265    /// # use jotup::Container::*;
2266    /// let input = "
2267    /// > [txt](multi
2268    /// > line)";
2269    /// assert!(matches!(
2270    ///     Parser::new(input)
2271    ///         .into_offset_iter()
2272    ///         .map(|(e, r)| (&input[r], e))
2273    ///         .collect::<Vec<_>>()
2274    ///         .as_slice(),
2275    ///     &[
2276    ///         ("\n", Blankline),
2277    ///         (">", Start(Blockquote, ..)),
2278    ///         ("", Start(Paragraph, ..)),
2279    ///         ("[", Start(Link { .. }, ..)),
2280    ///         ("txt", Str(..)),
2281    ///         ("](multi\n> line)", End),
2282    ///         ("", End),
2283    ///         ("", End),
2284    ///     ],
2285    /// ));
2286    /// ```
2287    pub fn into_offset_iter(self) -> OffsetIter<'s> {
2288        OffsetIter { parser: self }
2289    }
2290
2291    fn inline(&mut self) -> Option<(Event<'s>, std::ops::Range<usize>)> {
2292        let next = self.inline_parser.next()?;
2293
2294        let (inline, mut attributes) = match next {
2295            inline::Event {
2296                kind: inline::EventKind::Attributes { attrs, .. },
2297                ..
2298            } => (
2299                self.inline_parser.next(),
2300                self.inline_parser.store_attributes[attrs as usize].clone(),
2301            ),
2302            inline => (Some(inline), Attributes::new()),
2303        };
2304
2305        let event = inline.map(|inline| {
2306            let enter = matches!(inline.kind, inline::EventKind::Enter(_));
2307            let event = match inline.kind {
2308                inline::EventKind::Enter(c) | inline::EventKind::Exit(c) => {
2309                    let t = match c {
2310                        inline::Container::Span => Container::Span,
2311                        inline::Container::Verbatim => Container::Verbatim,
2312                        inline::Container::InlineMath => Container::Math { display: false },
2313                        inline::Container::DisplayMath => Container::Math { display: true },
2314                        inline::Container::RawFormat { format } => Container::RawInline {
2315                            format: format.into(),
2316                        },
2317                        inline::Container::Subscript => Container::Subscript,
2318                        inline::Container::Superscript => Container::Superscript,
2319                        inline::Container::Insert => Container::Insert,
2320                        inline::Container::Delete => Container::Delete,
2321                        inline::Container::Emphasis => Container::Emphasis,
2322                        inline::Container::Strong => Container::Strong,
2323                        inline::Container::Mark => Container::Mark,
2324                        inline::Container::InlineLink(url) => Container::Link(
2325                            self.inline_parser.store_cowstrs[url as usize].clone(),
2326                            LinkType::Span(SpanLinkType::Inline),
2327                        ),
2328                        inline::Container::InlineImage(url) => Container::Image(
2329                            self.inline_parser.store_cowstrs[url as usize].clone(),
2330                            SpanLinkType::Inline,
2331                        ),
2332                        inline::Container::ReferenceLink(tag)
2333                        | inline::Container::ReferenceImage(tag) => {
2334                            let tag = &self.inline_parser.store_cowstrs[tag as usize];
2335                            let link_def =
2336                                self.pre_pass.link_definitions.get(tag.as_ref()).cloned();
2337
2338                            let (url_or_tag, ty) = if let Some((url, mut attrs_def)) = link_def {
2339                                if enter {
2340                                    attrs_def.append(&mut attributes);
2341                                    attributes = attrs_def;
2342                                }
2343                                (url, SpanLinkType::Reference)
2344                            } else {
2345                                self.pre_pass.heading_id_by_tag(tag.as_ref()).map_or_else(
2346                                    || (tag.clone(), SpanLinkType::Unresolved),
2347                                    |id| (format!("#{}", id).into(), SpanLinkType::Reference),
2348                                )
2349                            };
2350
2351                            if matches!(c, inline::Container::ReferenceLink(..)) {
2352                                Container::Link(url_or_tag, LinkType::Span(ty))
2353                            } else {
2354                                Container::Image(url_or_tag, ty)
2355                            }
2356                        }
2357                        inline::Container::Autolink(url) => {
2358                            let ty = if url.contains('@') {
2359                                LinkType::Email
2360                            } else {
2361                                LinkType::AutoLink
2362                            };
2363                            Container::Link(url.into(), ty)
2364                        }
2365                    };
2366                    if enter {
2367                        Event::Start(t, attributes.take())
2368                    } else {
2369                        Event::End
2370                    }
2371                }
2372                inline::EventKind::Atom(a) => match a {
2373                    inline::Atom::FootnoteReference { label } => {
2374                        Event::FootnoteReference(label.into())
2375                    }
2376                    inline::Atom::Symbol(sym) => Event::Symbol(sym.into()),
2377                    inline::Atom::Quote { ty, left } => match (ty, left) {
2378                        (inline::QuoteType::Single, true) => Event::LeftSingleQuote,
2379                        (inline::QuoteType::Single, false) => Event::RightSingleQuote,
2380                        (inline::QuoteType::Double, true) => Event::LeftDoubleQuote,
2381                        (inline::QuoteType::Double, false) => Event::RightDoubleQuote,
2382                    },
2383                    inline::Atom::Ellipsis => Event::Ellipsis,
2384                    inline::Atom::EnDash => Event::EnDash,
2385                    inline::Atom::EmDash => Event::EmDash,
2386                    inline::Atom::Nbsp => Event::NonBreakingSpace,
2387                    inline::Atom::Softbreak => Event::Softbreak,
2388                    inline::Atom::Hardbreak => Event::Hardbreak,
2389                    inline::Atom::Escape => Event::Escape,
2390                },
2391                inline::EventKind::Empty => {
2392                    debug_assert!(!attributes.is_empty());
2393                    Event::Attributes(attributes.take())
2394                }
2395                inline::EventKind::Str => Event::Str(self.src[inline.span.clone()].into()),
2396                inline::EventKind::Attributes { .. } | inline::EventKind::Placeholder => {
2397                    panic!("{:?}", inline)
2398                }
2399            };
2400            (event, inline.span)
2401        });
2402
2403        debug_assert!(
2404            attributes.is_empty(),
2405            "unhandled attributes: {:?}",
2406            attributes
2407        );
2408
2409        event
2410    }
2411
2412    fn block(&mut self) -> Option<(Event<'s>, std::ops::Range<usize>)> {
2413        while let Some(ev) = self.blocks.peek() {
2414            let mut ev_span = ev.span.clone();
2415            let mut pop = true;
2416            let event = match ev.kind {
2417                block::EventKind::Atom(a) => match a {
2418                    block::Atom::Blankline => {
2419                        debug_assert_eq!(self.block_attributes, None);
2420                        Event::Blankline
2421                    }
2422                    block::Atom::ThematicBreak => {
2423                        let attrs = if let Some((attrs, span)) = self.block_attributes.take() {
2424                            ev_span.start = span.start;
2425                            attrs
2426                        } else {
2427                            Attributes::new()
2428                        };
2429                        Event::ThematicBreak(attrs)
2430                    }
2431                    block::Atom::Attributes => {
2432                        let (mut attrs, mut span) = self
2433                            .block_attributes
2434                            .take()
2435                            .unwrap_or_else(|| (Attributes::new(), ev_span.clone()));
2436                        attrs
2437                            .parse(&self.src[ev_span.clone()])
2438                            .expect("should be valid");
2439                        span.end = ev_span.end;
2440                        self.blocks.next().unwrap();
2441                        if matches!(
2442                            self.blocks.peek().map(|e| &e.kind),
2443                            Some(block::EventKind::Atom(block::Atom::Blankline))
2444                        ) {
2445                            return Some((Event::Attributes(attrs), span));
2446                        }
2447                        self.block_attributes = Some((attrs, span));
2448                        continue;
2449                    }
2450                },
2451                block::EventKind::Enter(c) | block::EventKind::Exit(c) => {
2452                    let enter = matches!(ev.kind, block::EventKind::Enter(..));
2453                    let cont = match c {
2454                        block::Node::Leaf(l) => {
2455                            self.inline_parser.reset();
2456                            match l {
2457                                block::Leaf::Paragraph => Container::Paragraph,
2458                                block::Leaf::Heading {
2459                                    level,
2460                                    has_section,
2461                                    pos,
2462                                } => Container::Heading {
2463                                    level,
2464                                    has_section,
2465                                    id: self
2466                                        .pre_pass
2467                                        .heading_id_by_location(pos)
2468                                        .unwrap_or_default()
2469                                        .to_string()
2470                                        .into(),
2471                                },
2472                                block::Leaf::DescriptionTerm => Container::DescriptionTerm,
2473                                block::Leaf::CodeBlock { language } => {
2474                                    self.verbatim = enter;
2475                                    if let Some(format) = language.strip_prefix('=') {
2476                                        Container::RawBlock {
2477                                            format: format.into(),
2478                                        }
2479                                    } else {
2480                                        Container::CodeBlock {
2481                                            language: language.into(),
2482                                        }
2483                                    }
2484                                }
2485                                block::Leaf::TableCell(alignment) => Container::TableCell {
2486                                    alignment,
2487                                    head: self.table_head_row,
2488                                },
2489                                block::Leaf::Caption => Container::Caption,
2490                                block::Leaf::LinkDefinition { label } => {
2491                                    self.verbatim = enter;
2492                                    Container::LinkDefinition {
2493                                        label: label.into(),
2494                                    }
2495                                }
2496                            }
2497                        }
2498                        block::Node::Container(c) => match c {
2499                            block::Container::Blockquote => Container::Blockquote,
2500                            block::Container::Div { class } => Container::Div {
2501                                class: class.into(),
2502                            },
2503                            block::Container::Footnote { label } => Container::Footnote {
2504                                label: label.into(),
2505                            },
2506                            block::Container::List { ty, tight } => {
2507                                if matches!(ty, block::ListType::Description) {
2508                                    Container::DescriptionList
2509                                } else {
2510                                    let kind = match ty {
2511                                        block::ListType::Unordered(c) => ListKind::Unordered(
2512                                            c.try_into().expect("should be bullet character"),
2513                                        ),
2514                                        block::ListType::Task(c) => ListKind::Task(
2515                                            c.try_into().expect("should be bullet character"),
2516                                        ),
2517                                        block::ListType::Ordered(
2518                                            block::ListNumber { numbering, value },
2519                                            style,
2520                                        ) => ListKind::Ordered {
2521                                            numbering,
2522                                            style,
2523                                            start: value,
2524                                        },
2525                                        block::ListType::Description => unreachable!(),
2526                                    };
2527                                    Container::List { kind, tight }
2528                                }
2529                            }
2530                            block::Container::ListItem(kind) => match kind {
2531                                block::ListItemKind::Task { checked } => {
2532                                    Container::TaskListItem { checked }
2533                                }
2534                                block::ListItemKind::Description => Container::DescriptionDetails,
2535                                block::ListItemKind::List => Container::ListItem,
2536                            },
2537                            block::Container::Table => Container::Table,
2538                            block::Container::TableRow { head } => {
2539                                if enter {
2540                                    self.table_head_row = head;
2541                                }
2542                                Container::TableRow { head }
2543                            }
2544                            block::Container::Section { pos } => Container::Section {
2545                                id: self
2546                                    .pre_pass
2547                                    .heading_id_by_location(pos)
2548                                    .unwrap_or_default()
2549                                    .to_string()
2550                                    .into(),
2551                            },
2552                        },
2553                    };
2554                    if enter {
2555                        let attrs = if let Some((attrs, span)) = self.block_attributes.take() {
2556                            ev_span.start = span.start;
2557                            attrs
2558                        } else {
2559                            Attributes::new()
2560                        };
2561                        Event::Start(cont, attrs)
2562                    } else if let Some((attrs, sp)) = self.block_attributes.take() {
2563                        pop = false;
2564                        ev_span = sp;
2565                        Event::Attributes(attrs)
2566                    } else {
2567                        Event::End
2568                    }
2569                }
2570                block::EventKind::Inline => {
2571                    if self.verbatim {
2572                        Event::Str(self.src[ev_span.clone()].into())
2573                    } else {
2574                        self.blocks.next().unwrap();
2575                        self.inline_parser.feed_line(
2576                            ev_span.clone(),
2577                            !matches!(
2578                                self.blocks.peek().map(|e| &e.kind),
2579                                Some(block::EventKind::Inline),
2580                            ),
2581                        );
2582                        return self.next_span();
2583                    }
2584                }
2585                block::EventKind::Stale => {
2586                    self.blocks.next().unwrap();
2587                    continue;
2588                }
2589            };
2590            if pop {
2591                self.blocks.next().unwrap();
2592            }
2593            return Some((event, ev_span));
2594        }
2595        None
2596    }
2597
2598    fn next_span(&mut self) -> Option<(Event<'s>, std::ops::Range<usize>)> {
2599        self.inline().or_else(|| self.block()).or_else(|| {
2600            self.block_attributes
2601                .take()
2602                .map(|(attrs, span)| (Event::Attributes(attrs), span))
2603        })
2604    }
2605}
2606
2607impl<'s> Iterator for Parser<'s> {
2608    type Item = Event<'s>;
2609
2610    fn next(&mut self) -> Option<Self::Item> {
2611        self.next_span().map(|(e, _)| e)
2612    }
2613}
2614
2615/// An iterator that is identical to a [`Parser`], except that it also emits the location of each
2616/// event within the input.
2617///
2618/// See the documentation of [`Parser::into_offset_iter`] for more information.
2619pub struct OffsetIter<'s> {
2620    parser: Parser<'s>,
2621}
2622
2623impl<'s> Iterator for OffsetIter<'s> {
2624    type Item = (Event<'s>, std::ops::Range<usize>);
2625
2626    fn next(&mut self) -> Option<Self::Item> {
2627        self.parser.next_span()
2628    }
2629}
2630
2631#[cfg(test)]
2632mod test {
2633    use super::OrderedListNumbering::*;
2634
2635    #[test]
2636    fn numbering_alpha() {
2637        assert_eq!(AlphaLower.parse_number("a"), 1);
2638        assert_eq!(AlphaUpper.parse_number("B"), 2);
2639        assert_eq!(AlphaUpper.parse_number("Z"), 26);
2640        assert_eq!(AlphaLower.parse_number("aa"), 27);
2641    }
2642}