Skip to main content

pulldown_cmark/
parse.rs

1// Copyright 2017 Google Inc. All rights reserved.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19// THE SOFTWARE.
20
21//! Tree-based two pass parser.
22
23use core::cell::Cell;
24use std::cmp::{max, min};
25use std::collections::{HashMap, VecDeque};
26use std::iter::FusedIterator;
27use std::num::NonZeroUsize;
28use std::ops::{Index, Range};
29
30use unicase::UniCase;
31
32use crate::firstpass::run_first_pass;
33use crate::linklabel::{scan_link_label_rest, FootnoteLabel, LinkLabel, ReferenceLabel};
34use crate::scanners::*;
35use crate::strings::CowStr;
36use crate::tree::{Tree, TreeIndex};
37use crate::{
38    Alignment, BlockQuoteKind, CodeBlockKind, Event, HeadingLevel, LinkType, MetadataBlockKind,
39    Options, Tag, TagEnd,
40};
41
42// Allowing arbitrary depth nested parentheses inside link destinations
43// can create denial of service vulnerabilities if we're not careful.
44// The simplest countermeasure is to limit their depth, which is
45// explicitly allowed by the spec as long as the limit is at least 3:
46// https://spec.commonmark.org/0.29/#link-destination
47pub(crate) const LINK_MAX_NESTED_PARENS: usize = 32;
48
49#[derive(Debug, Default, Clone)]
50pub(crate) struct Item {
51    pub start: usize,
52    pub end: usize,
53    pub body: ItemBody,
54}
55
56#[derive(Debug, PartialEq, Clone, Default)]
57pub(crate) enum ItemBody {
58    // These are possible inline items, need to be resolved in second pass.
59
60    // repeats, can_open, can_close
61    MaybeEmphasis(usize, bool, bool),
62    // can_open, can_close, brace context
63    MaybeMath(bool, bool, u8),
64    // quote byte, can_open, can_close
65    MaybeSmartQuote(u8, bool, bool),
66    MaybeCode(usize, bool), // number of backticks, preceded by backslash
67    MaybeHtml,
68    MaybeLinkOpen,
69    // bool indicates whether or not the preceding section could be a reference
70    MaybeLinkClose(bool),
71    MaybeImage,
72
73    // These are inline items after resolution.
74    Emphasis,
75    Strong,
76    Strikethrough,
77    Superscript,
78    Subscript,
79    Math(CowIndex, bool), // true for display math
80    Code(CowIndex),
81    Link(LinkIndex),
82    Image(LinkIndex),
83    FootnoteReference(CowIndex),
84    TaskListMarker(bool), // true for checked
85
86    // These are also inline items.
87    InlineHtml,
88    OwnedInlineHtml(CowIndex),
89    SynthesizeText(CowIndex),
90    SynthesizeChar(char),
91    Html,
92    Text {
93        backslash_escaped: bool,
94    },
95    SoftBreak,
96    // true = is backlash
97    HardBreak(bool),
98
99    // Dummy node at the top of the tree - should not be used otherwise!
100    #[default]
101    Root,
102
103    // These are block items.
104    Paragraph,
105    TightParagraph,
106    Rule,
107    Heading(HeadingLevel, Option<HeadingIndex>), // heading level
108    FencedCodeBlock(CowIndex),
109    IndentCodeBlock,
110    HtmlBlock,
111    BlockQuote(Option<BlockQuoteKind>),
112    // is_tight|last_line empty, list character, list start index
113    List(Cell<ListFlags>, u8, u64),
114    ListItem(usize), // indent level
115    FootnoteDefinition(CowIndex),
116    MetadataBlock(MetadataBlockKind),
117
118    // Definition lists
119    DefinitionList(Cell<ListFlags>),
120    // gets turned into either a paragraph or a definition list title,
121    // depending on whether there's a definition after it
122    MaybeDefinitionListTitle,
123    DefinitionListTitle,
124    DefinitionListDefinition(usize),
125
126    // Tables
127    Table(AlignmentIndex),
128    TableHead,
129    TableRow,
130    TableCell,
131}
132
133impl ItemBody {
134    fn is_maybe_inline(&self) -> bool {
135        use ItemBody::*;
136        matches!(
137            *self,
138            MaybeEmphasis(..)
139                | MaybeMath(..)
140                | MaybeSmartQuote(..)
141                | MaybeCode(..)
142                | MaybeHtml
143                | MaybeLinkOpen
144                | MaybeLinkClose(..)
145                | MaybeImage
146        )
147    }
148    fn is_inline(&self) -> bool {
149        use ItemBody::*;
150        matches!(
151            *self,
152            MaybeEmphasis(..)
153                | MaybeMath(..)
154                | MaybeSmartQuote(..)
155                | MaybeCode(..)
156                | MaybeHtml
157                | MaybeLinkOpen
158                | MaybeLinkClose(..)
159                | MaybeImage
160                | Emphasis
161                | Strong
162                | Strikethrough
163                | Math(..)
164                | Code(..)
165                | Link(..)
166                | Image(..)
167                | FootnoteReference(..)
168                | TaskListMarker(..)
169                | InlineHtml
170                | OwnedInlineHtml(..)
171                | SynthesizeText(..)
172                | SynthesizeChar(..)
173                | Html
174                | Text { .. }
175                | SoftBreak
176                | HardBreak(..)
177        )
178    }
179}
180
181bitflags::bitflags! {
182    /// Option struct containing flags for enabling extra features
183    /// that are not part of the CommonMark spec.
184    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
185    pub(crate) struct ListFlags: u8 {
186        const IS_TIGHT = 1;
187        const LAST_LINE_EMPTY = 1 << 1;
188    }
189}
190
191#[derive(Debug)]
192pub struct BrokenLink<'a> {
193    pub span: std::ops::Range<usize>,
194    pub link_type: LinkType,
195    pub reference: CowStr<'a>,
196}
197
198/// Markdown event iterator.
199pub struct Parser<'input, F = DefaultBrokenLinkCallback> {
200    text: &'input str,
201    options: Options,
202    tree: Tree<Item>,
203    allocs: Allocations<'input>,
204    broken_link_callback: Option<F>,
205    html_scan_guard: HtmlScanGuard,
206
207    // https://github.com/pulldown-cmark/pulldown-cmark/issues/844
208    // Consider this example:
209    //
210    //     [x]: xxx...
211    //     [x]
212    //     [x]
213    //     [x]
214    //
215    // Which expands to this HTML:
216    //
217    //     <a href="xxx...">x</a>
218    //     <a href="xxx...">x</a>
219    //     <a href="xxx...">x</a>
220    //
221    // This is quadratic growth, because it's filling in the area of a square.
222    // To prevent this, track how much it's expanded and limit it.
223    link_ref_expansion_limit: usize,
224
225    // used by inline passes. store them here for reuse
226    inline_stack: InlineStack,
227    link_stack: LinkStack,
228    wikilink_stack: LinkStack,
229    code_delims: CodeDelims,
230    math_delims: MathDelims,
231}
232
233impl<'input, F> std::fmt::Debug for Parser<'input, F> {
234    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
235        // Only print the fields that have public types.
236        f.debug_struct("Parser")
237            .field("text", &self.text)
238            .field("options", &self.options)
239            .field(
240                "broken_link_callback",
241                &self.broken_link_callback.as_ref().map(|_| ..),
242            )
243            .finish()
244    }
245}
246
247impl<'a> BrokenLink<'a> {
248    /// Moves the link into version with a static lifetime.
249    ///
250    /// The `reference` member is cloned to a Boxed or Inline version.
251    pub fn into_static(self) -> BrokenLink<'static> {
252        BrokenLink {
253            span: self.span.clone(),
254            link_type: self.link_type,
255            reference: self.reference.into_string().into(),
256        }
257    }
258}
259
260impl<'input> Parser<'input, DefaultBrokenLinkCallback> {
261    /// Creates a new event iterator for a markdown string without any options enabled.
262    pub fn new(text: &'input str) -> Self {
263        Self::new_ext(text, Options::empty())
264    }
265
266    /// Creates a new event iterator for a markdown string with given options.
267    pub fn new_ext(text: &'input str, options: Options) -> Self {
268        Self::new_with_broken_link_callback(text, options, None)
269    }
270}
271
272impl<'input, F: BrokenLinkCallback<'input>> Parser<'input, F> {
273    /// In case the parser encounters any potential links that have a broken
274    /// reference (e.g `[foo]` when there is no `[foo]: ` entry at the bottom)
275    /// the provided callback will be called with the reference name,
276    /// and the returned pair will be used as the link URL and title if it is not
277    /// `None`.
278    pub fn new_with_broken_link_callback(
279        text: &'input str,
280        options: Options,
281        broken_link_callback: Option<F>,
282    ) -> Self {
283        let (mut tree, allocs) = run_first_pass(text, options);
284        tree.reset();
285        let inline_stack = Default::default();
286        let link_stack = Default::default();
287        let wikilink_stack = Default::default();
288        let html_scan_guard = Default::default();
289        Parser {
290            text,
291            options,
292            tree,
293            allocs,
294            broken_link_callback,
295            inline_stack,
296            link_stack,
297            wikilink_stack,
298            html_scan_guard,
299            // always allow 100KiB
300            link_ref_expansion_limit: text.len().max(100_000),
301            code_delims: CodeDelims::new(),
302            math_delims: MathDelims::new(),
303        }
304    }
305
306    /// Returns a reference to the internal `RefDefs` object, which provides access
307    /// to the internal map of reference definitions.
308    pub fn reference_definitions(&self) -> &RefDefs<'_> {
309        &self.allocs.refdefs
310    }
311
312    /// Use a link label to fetch a type, url, and title.
313    ///
314    /// This function enforces the [`link_ref_expansion_limit`].
315    /// If it returns Some, it also consumes some of the fuel.
316    /// If we're out of fuel, it immediately returns None.
317    ///
318    /// The URL and title are found in the [`RefDefs`] map.
319    /// If they're not there, and a callback was provided by the user,
320    /// the [`broken_link_callback`] will be invoked and given the opportunity
321    /// to provide a fallback.
322    ///
323    /// The link type (that's "link" or "image") depends on the usage site, and
324    /// is provided by the caller of this function.
325    /// This function returns a new one because, if it has to invoke a callback
326    /// to find the information, the link type is [mapped to an unknown type].
327    ///
328    /// [mapped to an unknown type]: crate::LinkType::to_unknown
329    /// [`link_ref_expansion_limit`]: Self::link_ref_expansion_limit
330    /// [`broken_link_callback`]: Self::broken_link_callback
331    fn fetch_link_type_url_title(
332        &mut self,
333        link_label: CowStr<'input>,
334        span: Range<usize>,
335        link_type: LinkType,
336    ) -> Option<(LinkType, CowStr<'input>, CowStr<'input>)> {
337        if self.link_ref_expansion_limit == 0 {
338            return None;
339        }
340
341        let (link_type, url, title) = self
342            .allocs
343            .refdefs
344            .get(link_label.as_ref())
345            .map(|matching_def| {
346                // found a matching definition!
347                let title = matching_def
348                    .title
349                    .as_ref()
350                    .cloned()
351                    .unwrap_or_else(|| "".into());
352                let url = matching_def.dest.clone();
353                (link_type, url, title)
354            })
355            .or_else(|| {
356                match self.broken_link_callback.as_mut() {
357                    Some(callback) => {
358                        // Construct a BrokenLink struct, which will be passed to the callback
359                        let broken_link = BrokenLink {
360                            span,
361                            link_type,
362                            reference: link_label,
363                        };
364
365                        callback
366                            .handle_broken_link(broken_link)
367                            .map(|(url, title)| (link_type.to_unknown(), url, title))
368                    }
369                    None => None,
370                }
371            })?;
372
373        // Limit expansion from link references.
374        // This isn't a problem for footnotes, because multiple references to the same one
375        // reuse the same node, but links/images get their HREF/SRC copied.
376        self.link_ref_expansion_limit = self
377            .link_ref_expansion_limit
378            .saturating_sub(url.len() + title.len());
379
380        Some((link_type, url, title))
381    }
382
383    /// Handle inline markup.
384    ///
385    /// When the parser encounters any item indicating potential inline markup, all
386    /// inline markup passes are run on the remainder of the chain.
387    ///
388    /// Note: there's some potential for optimization here, but that's future work.
389    fn handle_inline(&mut self) {
390        self.handle_inline_pass1();
391        self.handle_emphasis_and_hard_break();
392    }
393
394    /// Handle inline HTML, code spans, and links.
395    ///
396    /// This function handles both inline HTML and code spans, because they have
397    /// the same precedence. It also handles links, even though they have lower
398    /// precedence, because the URL of links must not be processed.
399    fn handle_inline_pass1(&mut self) {
400        let mut cur = self.tree.cur();
401        let mut prev = None;
402
403        let block_end = self.tree[self.tree.peek_up().unwrap()].item.end;
404        let block_text = &self.text[..block_end];
405
406        while let Some(mut cur_ix) = cur {
407            match self.tree[cur_ix].item.body {
408                ItemBody::MaybeHtml => {
409                    let next = self.tree[cur_ix].next;
410                    let autolink = if let Some(next_ix) = next {
411                        scan_autolink(block_text, self.tree[next_ix].item.start)
412                    } else {
413                        None
414                    };
415
416                    if let Some((ix, uri, link_type)) = autolink {
417                        let node = scan_nodes_to_ix(&self.tree, next, ix);
418                        let text_node = self.tree.create_node(Item {
419                            start: self.tree[cur_ix].item.start + 1,
420                            end: ix - 1,
421                            body: ItemBody::Text {
422                                backslash_escaped: false,
423                            },
424                        });
425                        let link_ix =
426                            self.allocs
427                                .allocate_link(link_type, uri, "".into(), "".into());
428                        self.tree[cur_ix].item.body = ItemBody::Link(link_ix);
429                        self.tree[cur_ix].item.end = ix;
430                        self.tree[cur_ix].next = node;
431                        self.tree[cur_ix].child = Some(text_node);
432                        prev = cur;
433                        cur = node;
434                        if let Some(node_ix) = cur {
435                            self.tree[node_ix].item.start = max(self.tree[node_ix].item.start, ix);
436                        }
437                        continue;
438                    } else {
439                        let inline_html = next.and_then(|next_ix| {
440                            self.scan_inline_html(
441                                block_text.as_bytes(),
442                                self.tree[next_ix].item.start,
443                            )
444                        });
445                        if let Some((span, ix)) = inline_html {
446                            let node = scan_nodes_to_ix(&self.tree, next, ix);
447                            self.tree[cur_ix].item.body = if !span.is_empty() {
448                                let converted_string =
449                                    String::from_utf8(span).expect("invalid utf8");
450                                ItemBody::OwnedInlineHtml(
451                                    self.allocs.allocate_cow(converted_string.into()),
452                                )
453                            } else {
454                                ItemBody::InlineHtml
455                            };
456                            self.tree[cur_ix].item.end = ix;
457                            self.tree[cur_ix].next = node;
458                            prev = cur;
459                            cur = node;
460                            if let Some(node_ix) = cur {
461                                self.tree[node_ix].item.start =
462                                    max(self.tree[node_ix].item.start, ix);
463                            }
464                            continue;
465                        }
466                    }
467                    self.tree[cur_ix].item.body = ItemBody::Text {
468                        backslash_escaped: false,
469                    };
470                }
471                ItemBody::MaybeMath(can_open, _can_close, brace_context) => {
472                    if !can_open {
473                        self.tree[cur_ix].item.body = ItemBody::Text {
474                            backslash_escaped: false,
475                        };
476                        prev = cur;
477                        cur = self.tree[cur_ix].next;
478                        continue;
479                    }
480                    let is_display = self.tree[cur_ix].next.map_or(false, |next_ix| {
481                        matches!(
482                            self.tree[next_ix].item.body,
483                            ItemBody::MaybeMath(_can_open, _can_close, _brace_context)
484                        )
485                    });
486                    let result = if self.math_delims.is_populated() {
487                        // we have previously scanned all math environment delimiters,
488                        // so we can reuse that work
489                        self.math_delims
490                            .find(&self.tree, cur_ix, is_display, brace_context)
491                    } else {
492                        // we haven't previously scanned all math delimiters,
493                        // so walk the AST
494                        let mut scan = self.tree[cur_ix].next;
495                        if is_display {
496                            // a display delimiter, `$$`, is actually two delimiters
497                            // skip the second one
498                            scan = self.tree[scan.unwrap()].next;
499                        }
500                        let mut invalid = false;
501                        while let Some(scan_ix) = scan {
502                            if let ItemBody::MaybeMath(_can_open, can_close, delim_brace_context) =
503                                self.tree[scan_ix].item.body
504                            {
505                                let delim_is_display =
506                                    self.tree[scan_ix].next.map_or(false, |next_ix| {
507                                        matches!(
508                                            self.tree[next_ix].item.body,
509                                            ItemBody::MaybeMath(
510                                                _can_open,
511                                                _can_close,
512                                                _brace_context
513                                            )
514                                        )
515                                    });
516                                if !invalid && delim_brace_context == brace_context {
517                                    if (!is_display && can_close)
518                                        || (is_display && delim_is_display)
519                                    {
520                                        // This will skip ahead past everything we
521                                        // just inserted. Needed for correctness to
522                                        // ensure that a new scan is done after this item.
523                                        self.math_delims.clear();
524                                        break;
525                                    } else {
526                                        // Math cannot contain $, so the current item
527                                        // is invalid. Keep scanning to fill math_delims.
528                                        invalid = true;
529                                    }
530                                }
531                                self.math_delims.insert(
532                                    delim_is_display,
533                                    delim_brace_context,
534                                    scan_ix,
535                                    can_close,
536                                );
537                            }
538                            scan = self.tree[scan_ix].next;
539                        }
540                        scan
541                    };
542
543                    if let Some(scan_ix) = result {
544                        self.make_math_span(cur_ix, scan_ix);
545                    } else {
546                        self.tree[cur_ix].item.body = ItemBody::Text {
547                            backslash_escaped: false,
548                        };
549                    }
550                }
551                ItemBody::MaybeCode(mut search_count, preceded_by_backslash) => {
552                    if preceded_by_backslash {
553                        search_count -= 1;
554                        if search_count == 0 {
555                            self.tree[cur_ix].item.body = ItemBody::Text {
556                                backslash_escaped: false,
557                            };
558                            prev = cur;
559                            cur = self.tree[cur_ix].next;
560                            continue;
561                        }
562                    }
563
564                    if self.code_delims.is_populated() {
565                        // we have previously scanned all codeblock delimiters,
566                        // so we can reuse that work
567                        if let Some(scan_ix) = self.code_delims.find(cur_ix, search_count) {
568                            self.make_code_span(cur_ix, scan_ix, preceded_by_backslash);
569                        } else {
570                            self.tree[cur_ix].item.body = ItemBody::Text {
571                                backslash_escaped: false,
572                            };
573                        }
574                    } else {
575                        // we haven't previously scanned all codeblock delimiters,
576                        // so walk the AST
577                        let mut scan = if search_count > 0 {
578                            self.tree[cur_ix].next
579                        } else {
580                            None
581                        };
582                        while let Some(scan_ix) = scan {
583                            if let ItemBody::MaybeCode(delim_count, _) =
584                                self.tree[scan_ix].item.body
585                            {
586                                if search_count == delim_count {
587                                    self.make_code_span(cur_ix, scan_ix, preceded_by_backslash);
588                                    self.code_delims.clear();
589                                    break;
590                                } else {
591                                    self.code_delims.insert(delim_count, scan_ix);
592                                }
593                            }
594                            scan = self.tree[scan_ix].next;
595                        }
596                        if scan.is_none() {
597                            self.tree[cur_ix].item.body = ItemBody::Text {
598                                backslash_escaped: false,
599                            };
600                        }
601                    }
602                }
603                ItemBody::MaybeLinkOpen => {
604                    self.tree[cur_ix].item.body = ItemBody::Text {
605                        backslash_escaped: false,
606                    };
607                    let link_open_doubled = self.tree[cur_ix]
608                        .next
609                        .map(|ix| self.tree[ix].item.body == ItemBody::MaybeLinkOpen)
610                        .unwrap_or(false);
611                    if self.options.contains(Options::ENABLE_WIKILINKS) && link_open_doubled {
612                        self.wikilink_stack.push(LinkStackEl {
613                            node: cur_ix,
614                            ty: LinkStackTy::Link,
615                        });
616                    }
617                    self.link_stack.push(LinkStackEl {
618                        node: cur_ix,
619                        ty: LinkStackTy::Link,
620                    });
621                }
622                ItemBody::MaybeImage => {
623                    self.tree[cur_ix].item.body = ItemBody::Text {
624                        backslash_escaped: false,
625                    };
626                    let link_open_doubled = self.tree[cur_ix]
627                        .next
628                        .map(|ix| self.tree[ix].item.body == ItemBody::MaybeLinkOpen)
629                        .unwrap_or(false);
630                    if self.options.contains(Options::ENABLE_WIKILINKS) && link_open_doubled {
631                        self.wikilink_stack.push(LinkStackEl {
632                            node: cur_ix,
633                            ty: LinkStackTy::Image,
634                        });
635                    }
636                    self.link_stack.push(LinkStackEl {
637                        node: cur_ix,
638                        ty: LinkStackTy::Image,
639                    });
640                }
641                ItemBody::MaybeLinkClose(could_be_ref) => {
642                    self.tree[cur_ix].item.body = ItemBody::Text {
643                        backslash_escaped: false,
644                    };
645                    let tos_link = self.link_stack.pop();
646                    if self.options.contains(Options::ENABLE_WIKILINKS)
647                        && self.tree[cur_ix]
648                            .next
649                            .map(|ix| {
650                                matches!(self.tree[ix].item.body, ItemBody::MaybeLinkClose(..))
651                            })
652                            .unwrap_or(false)
653                    {
654                        if let Some(node) = self.handle_wikilink(block_text, cur_ix, prev) {
655                            prev = Some(node);
656                            cur = self.tree[node].next;
657                            continue;
658                        }
659                    }
660                    if let Some(tos) = tos_link {
661                        // skip rendering if already in a link, unless its an
662                        // image
663                        if tos.ty != LinkStackTy::Image
664                            && matches!(
665                                self.tree[self.tree.peek_up().unwrap()].item.body,
666                                ItemBody::Link(..)
667                            )
668                        {
669                            continue;
670                        }
671                        if tos.ty == LinkStackTy::Disabled {
672                            continue;
673                        }
674                        let next = self.tree[cur_ix].next;
675                        if let Some((next_ix, url, title)) =
676                            self.scan_inline_link(block_text, self.tree[cur_ix].item.end, next)
677                        {
678                            let next_node = scan_nodes_to_ix(&self.tree, next, next_ix);
679                            if let Some(prev_ix) = prev {
680                                self.tree[prev_ix].next = None;
681                            }
682                            cur = Some(tos.node);
683                            cur_ix = tos.node;
684                            let link_ix =
685                                self.allocs
686                                    .allocate_link(LinkType::Inline, url, title, "".into());
687                            self.tree[cur_ix].item.body = if tos.ty == LinkStackTy::Image {
688                                ItemBody::Image(link_ix)
689                            } else {
690                                ItemBody::Link(link_ix)
691                            };
692                            self.tree[cur_ix].child = self.tree[cur_ix].next;
693                            self.tree[cur_ix].next = next_node;
694                            self.tree[cur_ix].item.end = next_ix;
695                            if let Some(next_node_ix) = next_node {
696                                self.tree[next_node_ix].item.start =
697                                    max(self.tree[next_node_ix].item.start, next_ix);
698                            }
699
700                            if tos.ty == LinkStackTy::Link {
701                                self.disable_all_links();
702                            }
703                        } else {
704                            // ok, so its not an inline link. maybe it is a reference
705                            // to a defined link?
706                            let scan_result =
707                                scan_reference(&self.tree, block_text, next, self.options);
708                            let (node_after_link, link_type) = match scan_result {
709                                // [label][reference]
710                                RefScan::LinkLabel(_, end_ix) => {
711                                    // Toggle reference viability of the last closing bracket,
712                                    // so that we can skip it on future iterations in case
713                                    // it fails in this one. In particular, we won't call
714                                    // the broken link callback twice on one reference.
715                                    let reference_close_node = if let Some(node) =
716                                        scan_nodes_to_ix(&self.tree, next, end_ix - 1)
717                                    {
718                                        node
719                                    } else {
720                                        continue;
721                                    };
722                                    self.tree[reference_close_node].item.body =
723                                        ItemBody::MaybeLinkClose(false);
724                                    let next_node = self.tree[reference_close_node].next;
725
726                                    (next_node, LinkType::Reference)
727                                }
728                                // [reference][]
729                                RefScan::Collapsed(next_node) => {
730                                    // This reference has already been tried, and it's not
731                                    // valid. Skip it.
732                                    if !could_be_ref {
733                                        continue;
734                                    }
735                                    (next_node, LinkType::Collapsed)
736                                }
737                                // [shortcut]
738                                //
739                                // [shortcut]: /blah
740                                RefScan::Failed | RefScan::UnexpectedFootnote => {
741                                    if !could_be_ref {
742                                        continue;
743                                    }
744                                    (next, LinkType::Shortcut)
745                                }
746                            };
747
748                            // FIXME: references and labels are mixed in the naming of variables
749                            // below. Disambiguate!
750
751                            // (label, source_ix end)
752                            let label: Option<(ReferenceLabel<'input>, usize)> = match scan_result {
753                                RefScan::LinkLabel(l, end_ix) => {
754                                    Some((ReferenceLabel::Link(l), end_ix))
755                                }
756                                RefScan::Collapsed(..)
757                                | RefScan::Failed
758                                | RefScan::UnexpectedFootnote => {
759                                    // No label? maybe it is a shortcut reference
760                                    let label_start = self.tree[tos.node].item.end - 1;
761                                    let label_end = self.tree[cur_ix].item.end;
762                                    scan_link_label(
763                                        &self.tree,
764                                        &self.text[label_start..label_end],
765                                        self.options,
766                                    )
767                                    .map(|(ix, label)| (label, label_start + ix))
768                                    .filter(|(_, end)| *end == label_end)
769                                }
770                            };
771
772                            let id = match &label {
773                                Some(
774                                    (ReferenceLabel::Link(l), _) | (ReferenceLabel::Footnote(l), _),
775                                ) => l.clone(),
776                                None => "".into(),
777                            };
778
779                            // see if it's a footnote reference
780                            if let Some((ReferenceLabel::Footnote(l), end)) = label {
781                                let footref = self.allocs.allocate_cow(l);
782                                if let Some(def) = self
783                                    .allocs
784                                    .footdefs
785                                    .get_mut(self.allocs.cows[footref.0].to_owned())
786                                {
787                                    def.use_count += 1;
788                                }
789                                if !self.options.has_gfm_footnotes()
790                                    || self.allocs.footdefs.contains(&self.allocs.cows[footref.0])
791                                {
792                                    // If this came from a MaybeImage, then the `!` prefix
793                                    // isn't part of the footnote reference.
794                                    let footnote_ix = if tos.ty == LinkStackTy::Image {
795                                        self.tree[tos.node].next = Some(cur_ix);
796                                        self.tree[tos.node].child = None;
797                                        self.tree[tos.node].item.body =
798                                            ItemBody::SynthesizeChar('!');
799                                        self.tree[cur_ix].item.start =
800                                            self.tree[tos.node].item.start + 1;
801                                        self.tree[tos.node].item.end =
802                                            self.tree[tos.node].item.start + 1;
803                                        cur_ix
804                                    } else {
805                                        tos.node
806                                    };
807                                    // use `next` instead of `node_after_link` because
808                                    // node_after_link is calculated for a [collapsed][] link,
809                                    // which footnotes don't support.
810                                    self.tree[footnote_ix].next = next;
811                                    self.tree[footnote_ix].child = None;
812                                    self.tree[footnote_ix].item.body =
813                                        ItemBody::FootnoteReference(footref);
814                                    self.tree[footnote_ix].item.end = end;
815                                    prev = Some(footnote_ix);
816                                    cur = next;
817                                    self.link_stack.clear();
818                                    continue;
819                                }
820                            } else if let Some((ReferenceLabel::Link(link_label), end)) = label {
821                                if let Some((def_link_type, url, title)) = self
822                                    .fetch_link_type_url_title(
823                                        link_label,
824                                        (self.tree[tos.node].item.start)..end,
825                                        link_type,
826                                    )
827                                {
828                                    let link_ix =
829                                        self.allocs.allocate_link(def_link_type, url, title, id);
830                                    self.tree[tos.node].item.body = if tos.ty == LinkStackTy::Image
831                                    {
832                                        ItemBody::Image(link_ix)
833                                    } else {
834                                        ItemBody::Link(link_ix)
835                                    };
836                                    let label_node = self.tree[tos.node].next;
837
838                                    // lets do some tree surgery to add the link to the tree
839                                    // 1st: skip the label node and close node
840                                    self.tree[tos.node].next = node_after_link;
841
842                                    // then, if it exists, add the label node as a child to the link node
843                                    if label_node != cur {
844                                        self.tree[tos.node].child = label_node;
845
846                                        // finally: disconnect list of children
847                                        if let Some(prev_ix) = prev {
848                                            self.tree[prev_ix].next = None;
849                                        }
850                                    }
851
852                                    self.tree[tos.node].item.end = end;
853
854                                    // set up cur so next node will be node_after_link
855                                    cur = Some(tos.node);
856                                    cur_ix = tos.node;
857
858                                    if tos.ty == LinkStackTy::Link {
859                                        self.disable_all_links();
860                                    }
861                                }
862                            }
863                        }
864                    }
865                }
866                _ => {}
867            }
868            prev = cur;
869            cur = self.tree[cur_ix].next;
870        }
871        self.link_stack.clear();
872        self.wikilink_stack.clear();
873        self.code_delims.clear();
874        self.math_delims.clear();
875    }
876
877    /// Handles a wikilink.
878    ///
879    /// This function may bail early in case the link is malformed, so this
880    /// acts as a control flow guard. Returns the link node if a wikilink was
881    /// found and created.
882    fn handle_wikilink(
883        &mut self,
884        block_text: &'input str,
885        cur_ix: TreeIndex,
886        prev: Option<TreeIndex>,
887    ) -> Option<TreeIndex> {
888        let next_ix = self.tree[cur_ix].next.unwrap();
889        // this is a wikilink closing delim, try popping from
890        // the wikilink stack
891        if let Some(tos) = self.wikilink_stack.pop() {
892            if tos.ty == LinkStackTy::Disabled {
893                return None;
894            }
895            // fetches the beginning of the wikilink body
896            let Some(body_node) = self.tree[tos.node].next.and_then(|ix| self.tree[ix].next) else {
897                // skip if no next node exists, like at end of input
898                return None;
899            };
900            let start_ix = self.tree[body_node].item.start;
901            let end_ix = self.tree[cur_ix].item.start;
902            // bail early in case the link is malformed: on some inputs the
903            // body can begin at or after the closing delimiter, which would
904            // otherwise underflow the length below and invert the slice.
905            if end_ix <= start_ix {
906                return None;
907            }
908            let wikilink = match scan_wikilink_pipe(
909                block_text,
910                start_ix, // bounded by closing tag
911                end_ix - start_ix,
912            ) {
913                Some((rest, wikitext)) => {
914                    // bail early if the wikiname would be empty
915                    if wikitext.is_empty() {
916                        return None;
917                    }
918                    // [[WikiName|rest]]
919                    if rest >= end_ix {
920                        // Empty display text: the `|` is immediately followed
921                        // by `]]`. Create a zero-length synthetic node so the
922                        // anchor has no content, rather than accidentally
923                        // capturing the closing `]` delimiter.
924                        let body_node = self.tree.create_node(Item {
925                            start: rest,
926                            end: rest,
927                            body: ItemBody::Text {
928                                backslash_escaped: false,
929                            },
930                        });
931                        Some((true, body_node, wikitext))
932                    } else {
933                        let body_node = scan_nodes_to_ix(&self.tree, Some(body_node), rest);
934                        if let Some(body_node) = body_node {
935                            // break node so passes can actually format
936                            // the display text
937                            self.tree[body_node].item.start = rest;
938
939                            Some((true, body_node, wikitext))
940                        } else {
941                            None
942                        }
943                    }
944                }
945                None => {
946                    let wikitext = &block_text[start_ix..end_ix];
947                    // bail early if the wikiname would be empty
948                    if wikitext.is_empty() {
949                        return None;
950                    }
951                    let body_node = self.tree.create_node(Item {
952                        start: start_ix,
953                        end: end_ix,
954                        body: ItemBody::Text {
955                            backslash_escaped: false,
956                        },
957                    });
958                    Some((false, body_node, wikitext))
959                }
960            };
961
962            if let Some((has_pothole, body_node, wikiname)) = wikilink {
963                let link_ix = self.allocs.allocate_link(
964                    LinkType::WikiLink { has_pothole },
965                    wikiname.into(),
966                    "".into(),
967                    "".into(),
968                );
969                if let Some(prev_ix) = prev {
970                    self.tree[prev_ix].next = None;
971                }
972                if tos.ty == LinkStackTy::Image {
973                    self.tree[tos.node].item.body = ItemBody::Image(link_ix);
974                } else {
975                    self.tree[tos.node].item.body = ItemBody::Link(link_ix);
976                }
977                self.tree[tos.node].child = Some(body_node);
978                self.tree[tos.node].next = self.tree[next_ix].next;
979                self.tree[tos.node].item.end = end_ix + 2;
980                self.disable_all_links();
981                // The deepest wikilink takes precedence. `disable_all_links`
982                // leaves image openers alone, so drop the enclosing `![[` too,
983                // or each of them would rescan this body as its own target.
984                self.wikilink_stack.clear();
985                return Some(tos.node);
986            }
987        }
988
989        None
990    }
991
992    fn handle_emphasis_and_hard_break(&mut self) {
993        let mut prev = None;
994        let mut prev_ix: TreeIndex;
995        let mut cur = self.tree.cur();
996
997        let mut single_quote_open: Option<TreeIndex> = None;
998        let mut double_quote_open: bool = false;
999
1000        while let Some(mut cur_ix) = cur {
1001            match self.tree[cur_ix].item.body {
1002                ItemBody::MaybeEmphasis(mut count, can_open, can_close) => {
1003                    let run_length = count;
1004                    let c = self.text.as_bytes()[self.tree[cur_ix].item.start];
1005                    let both = can_open && can_close;
1006                    if can_close {
1007                        while let Some(el) =
1008                            self.inline_stack
1009                                .find_match(&mut self.tree, c, run_length, both)
1010                        {
1011                            // have a match!
1012                            if let Some(prev_ix) = prev {
1013                                self.tree[prev_ix].next = None;
1014                            }
1015                            let match_count = min(count, el.count);
1016                            // start, end are tree node indices
1017                            let mut end = cur_ix - 1;
1018                            let mut start = el.start + el.count;
1019
1020                            // work from the inside out
1021                            while start > el.start + el.count - match_count {
1022                                let inc = if start > el.start + el.count - match_count + 1 {
1023                                    2
1024                                } else {
1025                                    1
1026                                };
1027                                let ty = if c == b'~' {
1028                                    if inc == 2 {
1029                                        if self.options.contains(Options::ENABLE_STRIKETHROUGH) {
1030                                            ItemBody::Strikethrough
1031                                        } else {
1032                                            ItemBody::Text {
1033                                                backslash_escaped: false,
1034                                            }
1035                                        }
1036                                    } else {
1037                                        if self.options.contains(Options::ENABLE_SUBSCRIPT) {
1038                                            ItemBody::Subscript
1039                                        } else if self
1040                                            .options
1041                                            .contains(Options::ENABLE_STRIKETHROUGH)
1042                                        {
1043                                            ItemBody::Strikethrough
1044                                        } else {
1045                                            ItemBody::Text {
1046                                                backslash_escaped: false,
1047                                            }
1048                                        }
1049                                    }
1050                                } else if c == b'^' {
1051                                    if self.options.contains(Options::ENABLE_SUPERSCRIPT) {
1052                                        ItemBody::Superscript
1053                                    } else {
1054                                        ItemBody::Text {
1055                                            backslash_escaped: false,
1056                                        }
1057                                    }
1058                                } else if inc == 2 {
1059                                    ItemBody::Strong
1060                                } else {
1061                                    ItemBody::Emphasis
1062                                };
1063
1064                                let root = start - inc;
1065                                end = end + inc;
1066                                self.tree[root].item.body = ty;
1067                                self.tree[root].item.end = self.tree[end].item.end;
1068                                self.tree[root].child = Some(start);
1069                                self.tree[root].next = None;
1070                                start = root;
1071                            }
1072
1073                            // set next for top most emph level
1074                            prev_ix = el.start + el.count - match_count;
1075                            prev = Some(prev_ix);
1076                            cur = self.tree[cur_ix + match_count - 1].next;
1077                            self.tree[prev_ix].next = cur;
1078
1079                            if el.count > match_count {
1080                                self.inline_stack.push(InlineEl {
1081                                    start: el.start,
1082                                    count: el.count - match_count,
1083                                    run_length: el.run_length,
1084                                    c: el.c,
1085                                    both: el.both,
1086                                })
1087                            }
1088                            count -= match_count;
1089                            if count > 0 {
1090                                cur_ix = cur.unwrap();
1091                            } else {
1092                                break;
1093                            }
1094                        }
1095                    }
1096                    if count > 0 {
1097                        if can_open {
1098                            self.inline_stack.push(InlineEl {
1099                                start: cur_ix,
1100                                run_length,
1101                                count,
1102                                c,
1103                                both,
1104                            });
1105                        } else {
1106                            for i in 0..count {
1107                                self.tree[cur_ix + i].item.body = ItemBody::Text {
1108                                    backslash_escaped: false,
1109                                };
1110                            }
1111                        }
1112                        prev_ix = cur_ix + count - 1;
1113                        prev = Some(prev_ix);
1114                        cur = self.tree[prev_ix].next;
1115                    }
1116                }
1117                ItemBody::MaybeSmartQuote(c, can_open, can_close) => {
1118                    self.tree[cur_ix].item.body = match c {
1119                        b'\'' => {
1120                            if let (Some(open_ix), true) = (single_quote_open, can_close) {
1121                                self.tree[open_ix].item.body = ItemBody::SynthesizeChar('‘');
1122                                single_quote_open = None;
1123                            } else if can_open {
1124                                single_quote_open = Some(cur_ix);
1125                            }
1126                            ItemBody::SynthesizeChar('’')
1127                        }
1128                        _ /* double quote */ => {
1129                            if can_close && double_quote_open {
1130                                double_quote_open = false;
1131                                ItemBody::SynthesizeChar('”')
1132                            } else {
1133                                if can_open && !double_quote_open {
1134                                    double_quote_open = true;
1135                                }
1136                                ItemBody::SynthesizeChar('“')
1137                            }
1138                        }
1139                    };
1140                    prev = cur;
1141                    cur = self.tree[cur_ix].next;
1142                }
1143                ItemBody::HardBreak(true) => {
1144                    if self.tree[cur_ix].next.is_none() {
1145                        self.tree[cur_ix].item.body = ItemBody::SynthesizeChar('\\');
1146                    }
1147                    prev = cur;
1148                    cur = self.tree[cur_ix].next;
1149                }
1150                _ => {
1151                    prev = cur;
1152                    cur = self.tree[cur_ix].next;
1153                }
1154            }
1155        }
1156        self.inline_stack.pop_all(&mut self.tree);
1157    }
1158
1159    fn disable_all_links(&mut self) {
1160        self.link_stack.disable_all_links();
1161        self.wikilink_stack.disable_all_links();
1162    }
1163
1164    /// Returns next byte index, url and title.
1165    fn scan_inline_link(
1166        &self,
1167        underlying: &'input str,
1168        mut ix: usize,
1169        node: Option<TreeIndex>,
1170    ) -> Option<(usize, CowStr<'input>, CowStr<'input>)> {
1171        if underlying.as_bytes().get(ix) != Some(&b'(') {
1172            return None;
1173        }
1174        ix += 1;
1175
1176        let scan_separator = |ix: &mut usize| {
1177            *ix += scan_while(&underlying.as_bytes()[*ix..], is_ascii_whitespace_no_nl);
1178            if let Some(bl) = scan_eol(&underlying.as_bytes()[*ix..]) {
1179                *ix += bl;
1180                *ix += skip_container_prefixes(
1181                    &self.tree,
1182                    &underlying.as_bytes()[*ix..],
1183                    self.options,
1184                );
1185            }
1186            *ix += scan_while(&underlying.as_bytes()[*ix..], is_ascii_whitespace_no_nl);
1187        };
1188
1189        scan_separator(&mut ix);
1190
1191        let (dest_length, dest) = scan_link_dest(underlying, ix, LINK_MAX_NESTED_PARENS)?;
1192        let dest = unescape(dest, self.tree.is_in_table());
1193        ix += dest_length;
1194
1195        scan_separator(&mut ix);
1196
1197        let title = if let Some((bytes_scanned, t)) = self.scan_link_title(underlying, ix, node) {
1198            ix += bytes_scanned;
1199            scan_separator(&mut ix);
1200            t
1201        } else {
1202            "".into()
1203        };
1204        if underlying.as_bytes().get(ix) != Some(&b')') {
1205            return None;
1206        }
1207        ix += 1;
1208
1209        Some((ix, dest, title))
1210    }
1211
1212    // returns (bytes scanned, title cow)
1213    fn scan_link_title(
1214        &self,
1215        text: &'input str,
1216        start_ix: usize,
1217        node: Option<TreeIndex>,
1218    ) -> Option<(usize, CowStr<'input>)> {
1219        let bytes = text.as_bytes();
1220        let open = match bytes.get(start_ix) {
1221            Some(b @ b'\'') | Some(b @ b'\"') | Some(b @ b'(') => *b,
1222            _ => return None,
1223        };
1224        let close = if open == b'(' { b')' } else { open };
1225
1226        let mut title = String::new();
1227        let mut mark = start_ix + 1;
1228        let mut i = start_ix + 1;
1229
1230        while i < bytes.len() {
1231            let c = bytes[i];
1232
1233            if c == close {
1234                let cow = if mark == 1 {
1235                    (i - start_ix + 1, text[mark..i].into())
1236                } else {
1237                    title.push_str(&text[mark..i]);
1238                    (i - start_ix + 1, title.into())
1239                };
1240
1241                return Some(cow);
1242            }
1243            if c == open {
1244                return None;
1245            }
1246
1247            if c == b'\n' || c == b'\r' {
1248                if let Some(node_ix) = scan_nodes_to_ix(&self.tree, node, i + 1) {
1249                    if self.tree[node_ix].item.start > i {
1250                        title.push_str(&text[mark..i]);
1251                        title.push('\n');
1252                        i = self.tree[node_ix].item.start;
1253                        mark = i;
1254                        continue;
1255                    }
1256                }
1257            }
1258            if c == b'&' {
1259                if let (n, Some(value)) = scan_entity(&bytes[i..]) {
1260                    title.push_str(&text[mark..i]);
1261                    title.push_str(&value);
1262                    i += n;
1263                    mark = i;
1264                    continue;
1265                }
1266            }
1267            if self.tree.is_in_table()
1268                && c == b'\\'
1269                && i + 2 < bytes.len()
1270                && bytes[i + 1] == b'\\'
1271                && bytes[i + 2] == b'|'
1272            {
1273                // this runs if there are an even number of pipes in a table
1274                // if it's odd, then it gets parsed as normal
1275                title.push_str(&text[mark..i]);
1276                i += 2;
1277                mark = i;
1278            }
1279            if c == b'\\' && i + 1 < bytes.len() && is_ascii_punctuation(bytes[i + 1]) {
1280                title.push_str(&text[mark..i]);
1281                i += 1;
1282                mark = i;
1283            }
1284
1285            i += 1;
1286        }
1287
1288        None
1289    }
1290
1291    fn make_math_span(&mut self, open: TreeIndex, mut close: TreeIndex) {
1292        let start_is_display = self.tree[open].next.filter(|&next_ix| {
1293            next_ix != close
1294                && matches!(
1295                    self.tree[next_ix].item.body,
1296                    ItemBody::MaybeMath(_can_open, _can_close, _brace_context)
1297                )
1298        });
1299        let end_is_display = self.tree[close].next.filter(|&next_ix| {
1300            matches!(
1301                self.tree[next_ix].item.body,
1302                ItemBody::MaybeMath(_can_open, _can_close, _brace_context)
1303            )
1304        });
1305        let is_display = start_is_display.is_some() && end_is_display.is_some();
1306        if is_display {
1307            // This unwrap() can't panic, because if the next variable were None, end_is_display would be None
1308            close = self.tree[close].next.unwrap();
1309            self.tree[open].next = Some(close);
1310            self.tree[open].item.end += 1;
1311            self.tree[close].item.start -= 1;
1312        } else {
1313            if self.tree[open].item.end == self.tree[close].item.start {
1314                // inline math spans cannot be empty
1315                self.tree[open].item.body = ItemBody::Text {
1316                    backslash_escaped: false,
1317                };
1318                return;
1319            }
1320            self.tree[open].next = Some(close);
1321        }
1322        let span_start = self.tree[open].item.end;
1323        let span_end = self.tree[close].item.start;
1324
1325        let spanned_text = &self.text[span_start..span_end];
1326        let spanned_bytes = spanned_text.as_bytes();
1327        let mut buf: Option<String> = None;
1328
1329        let mut start_ix = 0;
1330        let mut ix = 0;
1331        while ix < spanned_bytes.len() {
1332            let c = spanned_bytes[ix];
1333            if c == b'\r' || c == b'\n' {
1334                ix += 1;
1335                let buf = buf.get_or_insert_with(|| String::with_capacity(spanned_bytes.len()));
1336                buf.push_str(&spanned_text[start_ix..ix]);
1337                ix += skip_container_prefixes(&self.tree, &spanned_bytes[ix..], self.options);
1338                start_ix = ix;
1339            } else if c == b'\\'
1340                && spanned_bytes.get(ix + 1) == Some(&b'|')
1341                && self.tree.is_in_table()
1342            {
1343                let buf = buf.get_or_insert_with(|| String::with_capacity(spanned_bytes.len()));
1344                buf.push_str(&spanned_text[start_ix..ix]);
1345                buf.push('|');
1346                ix += 2;
1347                start_ix = ix;
1348            } else {
1349                ix += 1;
1350            }
1351        }
1352
1353        let cow = if let Some(mut buf) = buf {
1354            buf.push_str(&spanned_text[start_ix..]);
1355            buf.into()
1356        } else {
1357            spanned_text.into()
1358        };
1359
1360        self.tree[open].item.body = ItemBody::Math(self.allocs.allocate_cow(cow), is_display);
1361        self.tree[open].item.end = self.tree[close].item.end;
1362        self.tree[open].next = self.tree[close].next;
1363    }
1364
1365    /// Make a code span.
1366    ///
1367    /// Both `open` and `close` are matching MaybeCode items.
1368    fn make_code_span(&mut self, open: TreeIndex, close: TreeIndex, preceding_backslash: bool) {
1369        let span_start = self.tree[open].item.end;
1370        let span_end = self.tree[close].item.start;
1371        let mut buf: Option<String> = None;
1372
1373        let spanned_text = &self.text[span_start..span_end];
1374        let spanned_bytes = spanned_text.as_bytes();
1375        let mut start_ix = 0;
1376        let mut ix = 0;
1377        while ix < spanned_bytes.len() {
1378            let c = spanned_bytes[ix];
1379            if c == b'\r' || c == b'\n' {
1380                let buf = buf.get_or_insert_with(|| String::with_capacity(spanned_bytes.len()));
1381                buf.push_str(&spanned_text[start_ix..ix]);
1382                buf.push(' ');
1383                ix += 1;
1384                ix += skip_container_prefixes(&self.tree, &spanned_bytes[ix..], self.options);
1385                start_ix = ix;
1386            } else if c == b'\\'
1387                && spanned_bytes.get(ix + 1) == Some(&b'|')
1388                && self.tree.is_in_table()
1389            {
1390                let buf = buf.get_or_insert_with(|| String::with_capacity(spanned_bytes.len()));
1391                buf.push_str(&spanned_text[start_ix..ix]);
1392                buf.push('|');
1393                ix += 2;
1394                start_ix = ix;
1395            } else {
1396                ix += 1;
1397            }
1398        }
1399
1400        let (opening, closing, all_spaces) = {
1401            let s = if let Some(buf) = &mut buf {
1402                buf.push_str(&spanned_text[start_ix..]);
1403                &buf[..]
1404            } else {
1405                spanned_text
1406            };
1407            (
1408                s.as_bytes().first() == Some(&b' '),
1409                s.as_bytes().last() == Some(&b' '),
1410                s.bytes().all(|b| b == b' '),
1411            )
1412        };
1413
1414        let cow: CowStr<'input> = if !all_spaces && opening && closing {
1415            if let Some(mut buf) = buf {
1416                if !buf.is_empty() {
1417                    buf.remove(0);
1418                    buf.pop();
1419                }
1420                buf.into()
1421            } else {
1422                spanned_text[1..(spanned_text.len() - 1).max(1)].into()
1423            }
1424        } else if let Some(buf) = buf {
1425            buf.into()
1426        } else {
1427            spanned_text.into()
1428        };
1429
1430        if preceding_backslash {
1431            self.tree[open].item.body = ItemBody::Text {
1432                backslash_escaped: true,
1433            };
1434            self.tree[open].item.end = self.tree[open].item.start + 1;
1435            self.tree[open].next = Some(close);
1436            self.tree[close].item.body = ItemBody::Code(self.allocs.allocate_cow(cow));
1437            self.tree[close].item.start = self.tree[open].item.start + 1;
1438        } else {
1439            self.tree[open].item.body = ItemBody::Code(self.allocs.allocate_cow(cow));
1440            self.tree[open].item.end = self.tree[close].item.end;
1441            self.tree[open].next = self.tree[close].next;
1442        }
1443    }
1444
1445    /// On success, returns a buffer containing the inline html and byte offset.
1446    /// When no bytes were skipped, the buffer will be empty and the html can be
1447    /// represented as a subslice of the input string.
1448    fn scan_inline_html(&mut self, bytes: &[u8], ix: usize) -> Option<(Vec<u8>, usize)> {
1449        let c = *bytes.get(ix)?;
1450        if c == b'!' {
1451            Some((
1452                vec![],
1453                scan_inline_html_comment(bytes, ix + 1, &mut self.html_scan_guard)?,
1454            ))
1455        } else if c == b'?' {
1456            Some((
1457                vec![],
1458                scan_inline_html_processing(bytes, ix + 1, &mut self.html_scan_guard)?,
1459            ))
1460        } else {
1461            let (span, i) = scan_html_block_inner(
1462                // Subtract 1 to include the < character
1463                &bytes[(ix - 1)..],
1464                Some(&|bytes| skip_container_prefixes(&self.tree, bytes, self.options)),
1465            )?;
1466            Some((span, i + ix - 1))
1467        }
1468    }
1469
1470    /// Consumes the event iterator and produces an iterator that produces
1471    /// `(Event, Range)` pairs, where the `Range` value maps to the corresponding
1472    /// range in the markdown source.
1473    pub fn into_offset_iter(self) -> OffsetIter<'input, F> {
1474        OffsetIter { inner: self }
1475    }
1476}
1477
1478/// Returns number of containers scanned.
1479pub(crate) fn scan_containers(
1480    tree: &Tree<Item>,
1481    line_start: &mut LineStart<'_>,
1482    options: Options,
1483    update_flags: bool,
1484) -> usize {
1485    let mut i = 0;
1486    let mut spine_iter = tree.walk_spine();
1487    for &node_ix in &mut spine_iter {
1488        match tree[node_ix].item.body {
1489            // This fix is lifted, almost verbatim,
1490            // from https://github.com/commonmark/cmark/pull/520/changes
1491            ItemBody::List(ref list_flags, _, _) | ItemBody::DefinitionList(ref list_flags)
1492                if update_flags =>
1493            {
1494                // Avoid quadratic behavior caused by deeply nested lists
1495                // for each blank line.
1496                if line_start.is_at_eol() {
1497                    if list_flags.get().contains(ListFlags::LAST_LINE_EMPTY)
1498                        && line_start.remaining_space() == 0
1499                    {
1500                        // Finish early if we encounter multiple blank lines.
1501                        // If the previous line was blank, then we know that this
1502                        // container has no children unless those children
1503                        // use indented lines to nest (in other words, blockquote
1504                        // and blockquote-like children aren't nested in here).
1505                        // Therefore, every child of this list will pass.
1506                        return tree.spine_len();
1507                    } else {
1508                        list_flags.set(list_flags.get() | ListFlags::LAST_LINE_EMPTY);
1509                    }
1510                } else {
1511                    list_flags.set(list_flags.get() & !ListFlags::LAST_LINE_EMPTY);
1512                }
1513            }
1514            ItemBody::BlockQuote(..) => {
1515                let save = line_start.clone();
1516                let _ = line_start.scan_space(3);
1517                if !line_start.scan_blockquote_marker() {
1518                    *line_start = save;
1519                    break;
1520                }
1521            }
1522            ItemBody::ListItem(indent) => {
1523                let save = line_start.clone();
1524                if !line_start.scan_space(indent) && !line_start.is_at_eol() {
1525                    *line_start = save;
1526                    break;
1527                }
1528            }
1529            ItemBody::DefinitionListDefinition(indent) => {
1530                let save = line_start.clone();
1531                if !line_start.scan_space(indent) && !line_start.is_at_eol() {
1532                    *line_start = save;
1533                    break;
1534                }
1535            }
1536            ItemBody::FootnoteDefinition(..) if options.has_gfm_footnotes() => {
1537                let save = line_start.clone();
1538                if !line_start.scan_space(4) && !line_start.is_at_eol() {
1539                    *line_start = save;
1540                    break;
1541                }
1542            }
1543            _ => (),
1544        }
1545        i += 1;
1546    }
1547    for &node_ix in &mut spine_iter {
1548        if let ItemBody::List(ref list_flags, _, _) | ItemBody::DefinitionList(ref list_flags) =
1549            tree[node_ix].item.body
1550        {
1551            list_flags.set(list_flags.get() & !ListFlags::LAST_LINE_EMPTY);
1552        }
1553    }
1554    i
1555}
1556pub(crate) fn skip_container_prefixes(tree: &Tree<Item>, bytes: &[u8], options: Options) -> usize {
1557    let mut line_start = LineStart::new(bytes);
1558    let _ = scan_containers(tree, &mut line_start, options, false);
1559    line_start.bytes_scanned()
1560}
1561
1562impl Tree<Item> {
1563    pub(crate) fn append_text(&mut self, start: usize, end: usize, backslash_escaped: bool) {
1564        if end > start {
1565            if let Some(ix) = self.cur() {
1566                if matches!(self[ix].item.body, ItemBody::Text { .. }) && self[ix].item.end == start
1567                {
1568                    self[ix].item.end = end;
1569                    return;
1570                }
1571            }
1572            self.append(Item {
1573                start,
1574                end,
1575                body: ItemBody::Text { backslash_escaped },
1576            });
1577        }
1578    }
1579    /// Returns true if the current node is inside a table.
1580    ///
1581    /// If `cur` is an ItemBody::Table, it would return false,
1582    /// but since the `TableRow` and `TableHead` and `TableCell`
1583    /// are children of the table, anything doing inline parsing
1584    /// doesn't need to care about that.
1585    pub(crate) fn is_in_table(&self) -> bool {
1586        fn might_be_in_table(item: &Item) -> bool {
1587            item.body.is_inline()
1588                || matches!(item.body, |ItemBody::TableHead| ItemBody::TableRow
1589                    | ItemBody::TableCell)
1590        }
1591        for &ix in self.walk_spine().rev() {
1592            if matches!(self[ix].item.body, ItemBody::Table(_)) {
1593                return true;
1594            }
1595            if !might_be_in_table(&self[ix].item) {
1596                return false;
1597            }
1598        }
1599        false
1600    }
1601}
1602
1603#[derive(Copy, Clone, Debug)]
1604struct InlineEl {
1605    /// offset of tree node
1606    start: TreeIndex,
1607    /// number of delimiters available for matching
1608    count: usize,
1609    /// length of the run that these delimiters came from
1610    run_length: usize,
1611    /// b'*', b'_', or b'~'
1612    c: u8,
1613    /// can both open and close
1614    both: bool,
1615}
1616
1617#[derive(Debug, Clone, Default)]
1618struct InlineStack {
1619    stack: Vec<InlineEl>,
1620    // Lower bounds for matching indices in the stack. For example
1621    // a strikethrough delimiter will never match with any element
1622    // in the stack with index smaller than
1623    // `lower_bounds[InlineStack::TILDES]`.
1624    lower_bounds: [usize; 9],
1625}
1626
1627impl InlineStack {
1628    /// These are indices into the lower bounds array.
1629    /// Not both refers to the property that the delimiter can not both
1630    /// be opener as a closer.
1631    const UNDERSCORE_NOT_BOTH: usize = 0;
1632    const ASTERISK_NOT_BOTH: usize = 1;
1633    const ASTERISK_BASE: usize = 2;
1634    const TILDES: usize = 5;
1635    const UNDERSCORE_BASE: usize = 6;
1636
1637    fn pop_all(&mut self, tree: &mut Tree<Item>) {
1638        for el in self.stack.drain(..) {
1639            for i in 0..el.count {
1640                tree[el.start + i].item.body = ItemBody::Text {
1641                    backslash_escaped: false,
1642                };
1643            }
1644        }
1645        self.lower_bounds = [0; 9];
1646    }
1647
1648    fn get_lowerbound(&self, c: u8, count: usize, both: bool) -> usize {
1649        if c == b'_' {
1650            let mod3_lower = self.lower_bounds[InlineStack::UNDERSCORE_BASE + count % 3];
1651            if both {
1652                mod3_lower
1653            } else {
1654                min(
1655                    mod3_lower,
1656                    self.lower_bounds[InlineStack::UNDERSCORE_NOT_BOTH],
1657                )
1658            }
1659        } else if c == b'*' {
1660            let mod3_lower = self.lower_bounds[InlineStack::ASTERISK_BASE + count % 3];
1661            if both {
1662                mod3_lower
1663            } else {
1664                min(
1665                    mod3_lower,
1666                    self.lower_bounds[InlineStack::ASTERISK_NOT_BOTH],
1667                )
1668            }
1669        } else {
1670            self.lower_bounds[InlineStack::TILDES]
1671        }
1672    }
1673
1674    fn set_lowerbound(&mut self, c: u8, count: usize, both: bool, new_bound: usize) {
1675        if c == b'_' {
1676            self.lower_bounds[InlineStack::UNDERSCORE_BASE + count % 3] = new_bound;
1677            if !both {
1678                self.lower_bounds[InlineStack::UNDERSCORE_NOT_BOTH] = new_bound;
1679            }
1680        } else if c == b'*' {
1681            self.lower_bounds[InlineStack::ASTERISK_BASE + count % 3] = new_bound;
1682            if !both {
1683                self.lower_bounds[InlineStack::ASTERISK_NOT_BOTH] = new_bound;
1684            }
1685        } else {
1686            self.lower_bounds[InlineStack::TILDES] = new_bound;
1687        }
1688    }
1689
1690    fn truncate(&mut self, new_bound: usize) {
1691        self.stack.truncate(new_bound);
1692        for lower_bound in &mut self.lower_bounds {
1693            if *lower_bound > new_bound {
1694                *lower_bound = new_bound;
1695            }
1696        }
1697    }
1698
1699    fn find_match(
1700        &mut self,
1701        tree: &mut Tree<Item>,
1702        c: u8,
1703        run_length: usize,
1704        both: bool,
1705    ) -> Option<InlineEl> {
1706        let lowerbound = min(self.stack.len(), self.get_lowerbound(c, run_length, both));
1707        let res = self.stack[lowerbound..]
1708            .iter()
1709            .cloned()
1710            .enumerate()
1711            .rfind(|(_, el)| {
1712                if c == b'~' && run_length != el.run_length {
1713                    return false;
1714                }
1715                el.c == c
1716                    && (!both && !el.both
1717                        || (run_length + el.run_length) % 3 != 0
1718                        || run_length % 3 == 0)
1719            });
1720
1721        if let Some((matching_ix, matching_el)) = res {
1722            let matching_ix = matching_ix + lowerbound;
1723            for el in &self.stack[(matching_ix + 1)..] {
1724                for i in 0..el.count {
1725                    tree[el.start + i].item.body = ItemBody::Text {
1726                        backslash_escaped: false,
1727                    };
1728                }
1729            }
1730            self.truncate(matching_ix);
1731            Some(matching_el)
1732        } else {
1733            self.set_lowerbound(c, run_length, both, self.stack.len());
1734            None
1735        }
1736    }
1737
1738    fn trim_lower_bound(&mut self, ix: usize) {
1739        self.lower_bounds[ix] = self.lower_bounds[ix].min(self.stack.len());
1740    }
1741
1742    fn push(&mut self, el: InlineEl) {
1743        if el.c == b'~' {
1744            self.trim_lower_bound(InlineStack::TILDES);
1745        }
1746        self.stack.push(el)
1747    }
1748}
1749
1750#[derive(Debug, Clone)]
1751enum RefScan<'a> {
1752    // label, source ix of label end
1753    LinkLabel(CowStr<'a>, usize),
1754    // contains next node index
1755    Collapsed(Option<TreeIndex>),
1756    UnexpectedFootnote,
1757    Failed,
1758}
1759
1760/// Skips forward within a block to a node which spans (ends inclusive) the given
1761/// index into the source.
1762fn scan_nodes_to_ix(
1763    tree: &Tree<Item>,
1764    mut node: Option<TreeIndex>,
1765    ix: usize,
1766) -> Option<TreeIndex> {
1767    while let Some(node_ix) = node {
1768        if tree[node_ix].item.end <= ix {
1769            node = tree[node_ix].next;
1770        } else {
1771            break;
1772        }
1773    }
1774    node
1775}
1776
1777/// Scans an inline link label, which cannot be interrupted.
1778/// Returns number of bytes (including brackets) and label on success.
1779fn scan_link_label<'text>(
1780    tree: &Tree<Item>,
1781    text: &'text str,
1782    options: Options,
1783) -> Option<(usize, ReferenceLabel<'text>)> {
1784    let bytes = text.as_bytes();
1785    if bytes.len() < 2 || bytes[0] != b'[' {
1786        return None;
1787    }
1788    let linebreak_handler = |bytes: &[u8]| Some(skip_container_prefixes(tree, bytes, options));
1789    if options.contains(Options::ENABLE_FOOTNOTES)
1790        && b'^' == bytes[1]
1791        && bytes.get(2) != Some(&b']')
1792    {
1793        let linebreak_handler: &dyn Fn(&[u8]) -> Option<usize> = if options.has_gfm_footnotes() {
1794            &|_| None
1795        } else {
1796            &linebreak_handler
1797        };
1798        if let Some((byte_index, cow)) =
1799            scan_link_label_rest(&text[2..], linebreak_handler, tree.is_in_table())
1800        {
1801            return Some((byte_index + 2, ReferenceLabel::Footnote(cow)));
1802        }
1803    }
1804    let (byte_index, cow) =
1805        scan_link_label_rest(&text[1..], &linebreak_handler, tree.is_in_table())?;
1806    Some((byte_index + 1, ReferenceLabel::Link(cow)))
1807}
1808
1809fn scan_reference<'b>(
1810    tree: &Tree<Item>,
1811    text: &'b str,
1812    cur: Option<TreeIndex>,
1813    options: Options,
1814) -> RefScan<'b> {
1815    let cur_ix = match cur {
1816        None => return RefScan::Failed,
1817        Some(cur_ix) => cur_ix,
1818    };
1819    let start = tree[cur_ix].item.start;
1820    let tail = &text.as_bytes()[start..];
1821
1822    if tail.starts_with(b"[]") {
1823        // TODO: this unwrap is sus and should be looked at closer
1824        let closing_node = tree[cur_ix].next.unwrap();
1825        RefScan::Collapsed(tree[closing_node].next)
1826    } else {
1827        let label = scan_link_label(tree, &text[start..], options);
1828        match label {
1829            Some((ix, ReferenceLabel::Link(label))) => RefScan::LinkLabel(label, start + ix),
1830            Some((_ix, ReferenceLabel::Footnote(_label))) => RefScan::UnexpectedFootnote,
1831            None => RefScan::Failed,
1832        }
1833    }
1834}
1835
1836#[derive(Clone, Default)]
1837struct LinkStack {
1838    inner: Vec<LinkStackEl>,
1839    disabled_ix: usize,
1840}
1841
1842impl LinkStack {
1843    fn push(&mut self, el: LinkStackEl) {
1844        self.inner.push(el);
1845    }
1846
1847    fn pop(&mut self) -> Option<LinkStackEl> {
1848        let el = self.inner.pop();
1849        self.disabled_ix = std::cmp::min(self.disabled_ix, self.inner.len());
1850        el
1851    }
1852
1853    fn clear(&mut self) {
1854        self.inner.clear();
1855        self.disabled_ix = 0;
1856    }
1857
1858    fn disable_all_links(&mut self) {
1859        for el in &mut self.inner[self.disabled_ix..] {
1860            if el.ty == LinkStackTy::Link {
1861                el.ty = LinkStackTy::Disabled;
1862            }
1863        }
1864        self.disabled_ix = self.inner.len();
1865    }
1866}
1867
1868#[derive(Clone, Debug)]
1869struct LinkStackEl {
1870    node: TreeIndex,
1871    ty: LinkStackTy,
1872}
1873
1874#[derive(PartialEq, Clone, Debug)]
1875enum LinkStackTy {
1876    Link,
1877    Image,
1878    Disabled,
1879}
1880
1881/// Contains the destination URL, title and source span of a reference definition.
1882#[derive(Clone, Debug)]
1883pub struct LinkDef<'a> {
1884    pub dest: CowStr<'a>,
1885    pub title: Option<CowStr<'a>>,
1886    pub span: Range<usize>,
1887}
1888
1889impl<'a> LinkDef<'a> {
1890    pub fn into_static(self) -> LinkDef<'static> {
1891        LinkDef {
1892            dest: self.dest.into_static(),
1893            title: self.title.map(|s| s.into_static()),
1894            span: self.span,
1895        }
1896    }
1897}
1898
1899/// Contains the destination URL, title and source span of a reference definition.
1900#[derive(Clone, Debug)]
1901pub struct FootnoteDef {
1902    pub use_count: usize,
1903}
1904
1905/// Tracks tree indices of code span delimiters of each length. It should prevent
1906/// quadratic scanning behaviours by providing (amortized) constant time lookups.
1907struct CodeDelims {
1908    inner: HashMap<usize, VecDeque<TreeIndex>>,
1909    seen_first: bool,
1910}
1911
1912impl CodeDelims {
1913    fn new() -> Self {
1914        Self {
1915            inner: Default::default(),
1916            seen_first: false,
1917        }
1918    }
1919
1920    fn insert(&mut self, count: usize, ix: TreeIndex) {
1921        if self.seen_first {
1922            self.inner.entry(count).or_default().push_back(ix);
1923        } else {
1924            // Skip the first insert, since that delimiter will always
1925            // be an opener and not a closer.
1926            self.seen_first = true;
1927        }
1928    }
1929
1930    fn is_populated(&self) -> bool {
1931        !self.inner.is_empty()
1932    }
1933
1934    fn find(&mut self, open_ix: TreeIndex, count: usize) -> Option<TreeIndex> {
1935        while let Some(ix) = self.inner.get_mut(&count)?.pop_front() {
1936            if ix > open_ix {
1937                return Some(ix);
1938            }
1939        }
1940        None
1941    }
1942
1943    fn clear(&mut self) {
1944        self.inner.clear();
1945        self.seen_first = false;
1946    }
1947}
1948
1949/// Tracks brace contexts and delimiter length for math delimiters.
1950/// Provides amortized constant-time lookups.
1951struct MathDelims {
1952    inner: HashMap<u8, VecDeque<(TreeIndex, bool, bool)>>,
1953}
1954
1955impl MathDelims {
1956    fn new() -> Self {
1957        Self {
1958            inner: Default::default(),
1959        }
1960    }
1961
1962    fn insert(
1963        &mut self,
1964        delim_is_display: bool,
1965        brace_context: u8,
1966        ix: TreeIndex,
1967        can_close: bool,
1968    ) {
1969        self.inner
1970            .entry(brace_context)
1971            .or_default()
1972            .push_back((ix, can_close, delim_is_display));
1973    }
1974
1975    fn is_populated(&self) -> bool {
1976        !self.inner.is_empty()
1977    }
1978
1979    fn find(
1980        &mut self,
1981        tree: &Tree<Item>,
1982        open_ix: TreeIndex,
1983        is_display: bool,
1984        brace_context: u8,
1985    ) -> Option<TreeIndex> {
1986        while let Some((ix, can_close, delim_is_display)) =
1987            self.inner.get_mut(&brace_context)?.pop_front()
1988        {
1989            if ix <= open_ix || (is_display && tree[open_ix].next == Some(ix)) {
1990                continue;
1991            }
1992            let can_close = can_close && tree[open_ix].item.end != tree[ix].item.start;
1993            if (!is_display && can_close) || (is_display && delim_is_display) {
1994                return Some(ix);
1995            }
1996            // if we can't use it, leave it in the queue as a tombstone for the next
1997            // thing that tries to match it
1998            self.inner
1999                .get_mut(&brace_context)?
2000                .push_front((ix, can_close, delim_is_display));
2001            break;
2002        }
2003        None
2004    }
2005
2006    fn clear(&mut self) {
2007        self.inner.clear();
2008    }
2009}
2010
2011#[derive(Copy, Clone, PartialEq, Eq, Debug)]
2012pub(crate) struct LinkIndex(usize);
2013
2014#[derive(Copy, Clone, PartialEq, Eq, Debug)]
2015pub(crate) struct CowIndex(usize);
2016
2017#[derive(Copy, Clone, PartialEq, Eq, Debug)]
2018pub(crate) struct AlignmentIndex(usize);
2019
2020#[derive(Copy, Clone, PartialEq, Eq, Debug)]
2021pub(crate) struct HeadingIndex(NonZeroUsize);
2022
2023#[derive(Clone)]
2024pub(crate) struct Allocations<'a> {
2025    pub refdefs: RefDefs<'a>,
2026    pub footdefs: FootnoteDefs<'a>,
2027    links: Vec<(LinkType, CowStr<'a>, CowStr<'a>, CowStr<'a>)>,
2028    cows: Vec<CowStr<'a>>,
2029    alignments: Vec<Vec<Alignment>>,
2030    headings: Vec<HeadingAttributes<'a>>,
2031}
2032
2033/// Used by the heading attributes extension.
2034#[derive(Clone)]
2035pub(crate) struct HeadingAttributes<'a> {
2036    pub id: Option<CowStr<'a>>,
2037    pub classes: Vec<CowStr<'a>>,
2038    pub attrs: Vec<(CowStr<'a>, Option<CowStr<'a>>)>,
2039}
2040
2041/// Keeps track of the reference definitions defined in the document.
2042#[derive(Clone, Default, Debug)]
2043pub struct RefDefs<'input>(pub(crate) HashMap<LinkLabel<'input>, LinkDef<'input>>);
2044
2045/// Keeps track of the footnote definitions defined in the document.
2046#[derive(Clone, Default, Debug)]
2047pub struct FootnoteDefs<'input>(pub(crate) HashMap<FootnoteLabel<'input>, FootnoteDef>);
2048
2049impl<'input, 'b, 's> RefDefs<'input>
2050where
2051    's: 'b,
2052{
2053    /// Performs a lookup on reference label using unicode case folding.
2054    pub fn get(&'s self, key: &'b str) -> Option<&'b LinkDef<'input>> {
2055        self.0.get(&UniCase::new(key.into()))
2056    }
2057
2058    /// Provides an iterator over all the document's reference definitions.
2059    pub fn iter(&'s self) -> impl Iterator<Item = (&'s str, &'s LinkDef<'input>)> {
2060        self.0.iter().map(|(k, v)| (k.as_ref(), v))
2061    }
2062}
2063
2064impl<'input, 'b, 's> FootnoteDefs<'input>
2065where
2066    's: 'b,
2067{
2068    /// Performs a lookup on reference label using unicode case folding.
2069    pub fn contains(&'s self, key: &'b str) -> bool {
2070        self.0.contains_key(&UniCase::new(key.into()))
2071    }
2072    /// Performs a lookup on reference label using unicode case folding.
2073    pub fn get_mut(&'s mut self, key: CowStr<'input>) -> Option<&'s mut FootnoteDef> {
2074        self.0.get_mut(&UniCase::new(key))
2075    }
2076}
2077
2078impl<'a> Allocations<'a> {
2079    pub fn new() -> Self {
2080        Self {
2081            refdefs: RefDefs::default(),
2082            footdefs: FootnoteDefs::default(),
2083            links: Vec::with_capacity(128),
2084            cows: Vec::new(),
2085            alignments: Vec::new(),
2086            headings: Vec::new(),
2087        }
2088    }
2089
2090    pub fn allocate_cow(&mut self, cow: CowStr<'a>) -> CowIndex {
2091        let ix = self.cows.len();
2092        self.cows.push(cow);
2093        CowIndex(ix)
2094    }
2095
2096    pub fn allocate_link(
2097        &mut self,
2098        ty: LinkType,
2099        url: CowStr<'a>,
2100        title: CowStr<'a>,
2101        id: CowStr<'a>,
2102    ) -> LinkIndex {
2103        let ix = self.links.len();
2104        self.links.push((ty, url, title, id));
2105        LinkIndex(ix)
2106    }
2107
2108    pub fn allocate_alignment(&mut self, alignment: Vec<Alignment>) -> AlignmentIndex {
2109        let ix = self.alignments.len();
2110        self.alignments.push(alignment);
2111        AlignmentIndex(ix)
2112    }
2113
2114    pub fn allocate_heading(&mut self, attrs: HeadingAttributes<'a>) -> HeadingIndex {
2115        let ix = self.headings.len();
2116        self.headings.push(attrs);
2117        // This won't panic. `self.headings.len()` can't be `usize::MAX` since
2118        // such a long Vec cannot fit in memory.
2119        let ix_nonzero = NonZeroUsize::new(ix.wrapping_add(1)).expect("too many headings");
2120        HeadingIndex(ix_nonzero)
2121    }
2122
2123    pub fn take_cow(&mut self, ix: CowIndex) -> CowStr<'a> {
2124        std::mem::replace(&mut self.cows[ix.0], "".into())
2125    }
2126
2127    pub fn take_link(&mut self, ix: LinkIndex) -> (LinkType, CowStr<'a>, CowStr<'a>, CowStr<'a>) {
2128        let default_link = (LinkType::ShortcutUnknown, "".into(), "".into(), "".into());
2129        std::mem::replace(&mut self.links[ix.0], default_link)
2130    }
2131
2132    pub fn take_alignment(&mut self, ix: AlignmentIndex) -> Vec<Alignment> {
2133        std::mem::take(&mut self.alignments[ix.0])
2134    }
2135}
2136
2137impl<'a> Index<CowIndex> for Allocations<'a> {
2138    type Output = CowStr<'a>;
2139
2140    fn index(&self, ix: CowIndex) -> &Self::Output {
2141        self.cows.index(ix.0)
2142    }
2143}
2144
2145impl<'a> Index<LinkIndex> for Allocations<'a> {
2146    type Output = (LinkType, CowStr<'a>, CowStr<'a>, CowStr<'a>);
2147
2148    fn index(&self, ix: LinkIndex) -> &Self::Output {
2149        self.links.index(ix.0)
2150    }
2151}
2152
2153impl<'a> Index<AlignmentIndex> for Allocations<'a> {
2154    type Output = Vec<Alignment>;
2155
2156    fn index(&self, ix: AlignmentIndex) -> &Self::Output {
2157        self.alignments.index(ix.0)
2158    }
2159}
2160
2161impl<'a> Index<HeadingIndex> for Allocations<'a> {
2162    type Output = HeadingAttributes<'a>;
2163
2164    fn index(&self, ix: HeadingIndex) -> &Self::Output {
2165        self.headings.index(ix.0.get() - 1)
2166    }
2167}
2168
2169/// A struct containing information on the reachability of certain inline HTML
2170/// elements. In particular, for cdata elements (`<![CDATA[`), processing
2171/// elements (`<?`) and declarations (`<!DECLARATION`). The respectives usizes
2172/// represent the indices before which a scan will always fail and can hence
2173/// be skipped.
2174#[derive(Clone, Default)]
2175pub(crate) struct HtmlScanGuard {
2176    pub cdata: usize,
2177    pub processing: usize,
2178    pub declaration: usize,
2179    pub comment: usize,
2180}
2181
2182/// Trait for broken link callbacks.
2183///
2184/// See [Parser::new_with_broken_link_callback].
2185/// Automatically implemented for closures with the appropriate signature.
2186pub trait BrokenLinkCallback<'input> {
2187    fn handle_broken_link(
2188        &mut self,
2189        link: BrokenLink<'input>,
2190    ) -> Option<(CowStr<'input>, CowStr<'input>)>;
2191}
2192
2193impl<'input, T> BrokenLinkCallback<'input> for T
2194where
2195    T: FnMut(BrokenLink<'input>) -> Option<(CowStr<'input>, CowStr<'input>)>,
2196{
2197    fn handle_broken_link(
2198        &mut self,
2199        link: BrokenLink<'input>,
2200    ) -> Option<(CowStr<'input>, CowStr<'input>)> {
2201        self(link)
2202    }
2203}
2204
2205impl<'input> BrokenLinkCallback<'input> for Box<dyn BrokenLinkCallback<'input>> {
2206    fn handle_broken_link(
2207        &mut self,
2208        link: BrokenLink<'input>,
2209    ) -> Option<(CowStr<'input>, CowStr<'input>)> {
2210        (**self).handle_broken_link(link)
2211    }
2212}
2213
2214/// Broken link callback that does nothing.
2215#[derive(Debug)]
2216pub struct DefaultBrokenLinkCallback;
2217
2218impl<'input> BrokenLinkCallback<'input> for DefaultBrokenLinkCallback {
2219    fn handle_broken_link(
2220        &mut self,
2221        _link: BrokenLink<'input>,
2222    ) -> Option<(CowStr<'input>, CowStr<'input>)> {
2223        None
2224    }
2225}
2226
2227/// Markdown event and source range iterator.
2228///
2229/// Generates tuples where the first element is the markdown event and the second
2230/// is a the corresponding range in the source string.
2231///
2232/// Constructed from a `Parser` using its
2233/// [`into_offset_iter`](struct.Parser.html#method.into_offset_iter) method.
2234#[derive(Debug)]
2235pub struct OffsetIter<'a, F = DefaultBrokenLinkCallback> {
2236    inner: Parser<'a, F>,
2237}
2238
2239impl<'a, F: BrokenLinkCallback<'a>> OffsetIter<'a, F> {
2240    /// Returns a reference to the internal reference definition tracker.
2241    pub fn reference_definitions(&self) -> &RefDefs<'_> {
2242        self.inner.reference_definitions()
2243    }
2244}
2245
2246impl<'a, F: BrokenLinkCallback<'a>> Iterator for OffsetIter<'a, F> {
2247    type Item = (Event<'a>, Range<usize>);
2248
2249    fn next(&mut self) -> Option<Self::Item> {
2250        match self.inner.tree.cur() {
2251            None => {
2252                let ix = self.inner.tree.pop()?;
2253                let ix = if matches!(self.inner.tree[ix].item.body, ItemBody::TightParagraph) {
2254                    // tight paragraphs emit nothing
2255                    self.inner.tree.next_sibling(ix);
2256                    return self.next();
2257                } else {
2258                    ix
2259                };
2260                let tag_end = body_to_tag_end(&self.inner.tree[ix].item.body);
2261                self.inner.tree.next_sibling(ix);
2262                let span = self.inner.tree[ix].item.start..self.inner.tree[ix].item.end;
2263                debug_assert!(span.start <= span.end);
2264                Some((Event::End(tag_end), span))
2265            }
2266            Some(cur_ix) => {
2267                let cur_ix =
2268                    if matches!(self.inner.tree[cur_ix].item.body, ItemBody::TightParagraph) {
2269                        // tight paragraphs emit nothing
2270                        self.inner.tree.push();
2271                        self.inner.tree.cur().unwrap()
2272                    } else {
2273                        cur_ix
2274                    };
2275                if self.inner.tree[cur_ix].item.body.is_maybe_inline() {
2276                    self.inner.handle_inline();
2277                }
2278
2279                let item = self.inner.tree[cur_ix].item.clone();
2280                let event = item_to_event(&item, self.inner.text, &mut self.inner.allocs);
2281                if let Event::Start(..) = event {
2282                    self.inner.tree.push();
2283                } else {
2284                    self.inner.tree.next_sibling(cur_ix);
2285                }
2286                debug_assert!(item.start <= item.end);
2287                Some((event, item.start..item.end))
2288            }
2289        }
2290    }
2291}
2292
2293fn body_to_tag_end(body: &ItemBody) -> TagEnd {
2294    match *body {
2295        ItemBody::Paragraph => TagEnd::Paragraph,
2296        ItemBody::Emphasis => TagEnd::Emphasis,
2297        ItemBody::Superscript => TagEnd::Superscript,
2298        ItemBody::Subscript => TagEnd::Subscript,
2299        ItemBody::Strong => TagEnd::Strong,
2300        ItemBody::Strikethrough => TagEnd::Strikethrough,
2301        ItemBody::Link(..) => TagEnd::Link,
2302        ItemBody::Image(..) => TagEnd::Image,
2303        ItemBody::Heading(level, _) => TagEnd::Heading(level),
2304        ItemBody::IndentCodeBlock | ItemBody::FencedCodeBlock(..) => TagEnd::CodeBlock,
2305        ItemBody::BlockQuote(kind) => TagEnd::BlockQuote(kind),
2306        ItemBody::HtmlBlock => TagEnd::HtmlBlock,
2307        ItemBody::List(_, c, _) => {
2308            let is_ordered = c == b'.' || c == b')';
2309            TagEnd::List(is_ordered)
2310        }
2311        ItemBody::ListItem(_) => TagEnd::Item,
2312        ItemBody::TableHead => TagEnd::TableHead,
2313        ItemBody::TableCell => TagEnd::TableCell,
2314        ItemBody::TableRow => TagEnd::TableRow,
2315        ItemBody::Table(..) => TagEnd::Table,
2316        ItemBody::FootnoteDefinition(..) => TagEnd::FootnoteDefinition,
2317        ItemBody::MetadataBlock(kind) => TagEnd::MetadataBlock(kind),
2318        ItemBody::DefinitionList(_) => TagEnd::DefinitionList,
2319        ItemBody::DefinitionListTitle => TagEnd::DefinitionListTitle,
2320        ItemBody::DefinitionListDefinition(_) => TagEnd::DefinitionListDefinition,
2321        _ => panic!("unexpected item body {:?}", body),
2322    }
2323}
2324
2325fn item_to_event<'a>(item: &Item, text: &'a str, allocs: &mut Allocations<'a>) -> Event<'a> {
2326    let tag = match item.body {
2327        ItemBody::Text { .. } => return Event::Text(text[item.start..item.end].into()),
2328        ItemBody::Code(cow_ix) => return Event::Code(allocs.take_cow(cow_ix)),
2329        ItemBody::SynthesizeText(cow_ix) => return Event::Text(allocs.take_cow(cow_ix)),
2330        ItemBody::SynthesizeChar(c) => return Event::Text(c.into()),
2331        ItemBody::HtmlBlock => Tag::HtmlBlock,
2332        ItemBody::Html => return Event::Html(text[item.start..item.end].into()),
2333        ItemBody::InlineHtml => return Event::InlineHtml(text[item.start..item.end].into()),
2334        ItemBody::OwnedInlineHtml(cow_ix) => return Event::InlineHtml(allocs.take_cow(cow_ix)),
2335        ItemBody::SoftBreak => return Event::SoftBreak,
2336        ItemBody::HardBreak(_) => return Event::HardBreak,
2337        ItemBody::FootnoteReference(cow_ix) => {
2338            return Event::FootnoteReference(allocs.take_cow(cow_ix))
2339        }
2340        ItemBody::TaskListMarker(checked) => return Event::TaskListMarker(checked),
2341        ItemBody::Rule => return Event::Rule,
2342        ItemBody::Paragraph => Tag::Paragraph,
2343        ItemBody::Emphasis => Tag::Emphasis,
2344        ItemBody::Superscript => Tag::Superscript,
2345        ItemBody::Subscript => Tag::Subscript,
2346        ItemBody::Strong => Tag::Strong,
2347        ItemBody::Strikethrough => Tag::Strikethrough,
2348        ItemBody::Link(link_ix) => {
2349            let (link_type, dest_url, title, id) = allocs.take_link(link_ix);
2350            Tag::Link {
2351                link_type,
2352                dest_url,
2353                title,
2354                id,
2355            }
2356        }
2357        ItemBody::Image(link_ix) => {
2358            let (link_type, dest_url, title, id) = allocs.take_link(link_ix);
2359            Tag::Image {
2360                link_type,
2361                dest_url,
2362                title,
2363                id,
2364            }
2365        }
2366        ItemBody::Heading(level, Some(heading_ix)) => {
2367            let HeadingAttributes { id, classes, attrs } = allocs.index(heading_ix);
2368            Tag::Heading {
2369                level,
2370                id: id.clone(),
2371                classes: classes.clone(),
2372                attrs: attrs.clone(),
2373            }
2374        }
2375        ItemBody::Heading(level, None) => Tag::Heading {
2376            level,
2377            id: None,
2378            classes: Vec::new(),
2379            attrs: Vec::new(),
2380        },
2381        ItemBody::FencedCodeBlock(cow_ix) => {
2382            Tag::CodeBlock(CodeBlockKind::Fenced(allocs.take_cow(cow_ix)))
2383        }
2384        ItemBody::IndentCodeBlock => Tag::CodeBlock(CodeBlockKind::Indented),
2385        ItemBody::BlockQuote(kind) => Tag::BlockQuote(kind),
2386        ItemBody::List(_, c, listitem_start) => {
2387            if c == b'.' || c == b')' {
2388                Tag::List(Some(listitem_start))
2389            } else {
2390                Tag::List(None)
2391            }
2392        }
2393        ItemBody::ListItem(_) => Tag::Item,
2394        ItemBody::TableHead => Tag::TableHead,
2395        ItemBody::TableCell => Tag::TableCell,
2396        ItemBody::TableRow => Tag::TableRow,
2397        ItemBody::Table(alignment_ix) => Tag::Table(allocs.take_alignment(alignment_ix)),
2398        ItemBody::FootnoteDefinition(cow_ix) => Tag::FootnoteDefinition(allocs.take_cow(cow_ix)),
2399        ItemBody::MetadataBlock(kind) => Tag::MetadataBlock(kind),
2400        ItemBody::Math(cow_ix, is_display) => {
2401            return if is_display {
2402                Event::DisplayMath(allocs.take_cow(cow_ix))
2403            } else {
2404                Event::InlineMath(allocs.take_cow(cow_ix))
2405            }
2406        }
2407        ItemBody::DefinitionList(_) => Tag::DefinitionList,
2408        ItemBody::DefinitionListTitle => Tag::DefinitionListTitle,
2409        ItemBody::DefinitionListDefinition(_) => Tag::DefinitionListDefinition,
2410        _ => panic!("unexpected item body {:?}", item.body),
2411    };
2412
2413    Event::Start(tag)
2414}
2415
2416impl<'a, F: BrokenLinkCallback<'a>> Iterator for Parser<'a, F> {
2417    type Item = Event<'a>;
2418
2419    fn next(&mut self) -> Option<Event<'a>> {
2420        match self.tree.cur() {
2421            None => {
2422                let ix = self.tree.pop()?;
2423                let ix = if matches!(self.tree[ix].item.body, ItemBody::TightParagraph) {
2424                    // tight paragraphs emit nothing
2425                    self.tree.next_sibling(ix);
2426                    return self.next();
2427                } else {
2428                    ix
2429                };
2430                let tag_end = body_to_tag_end(&self.tree[ix].item.body);
2431                self.tree.next_sibling(ix);
2432                Some(Event::End(tag_end))
2433            }
2434            Some(cur_ix) => {
2435                let maybe_cur_ix =
2436                    if matches!(self.tree[cur_ix].item.body, ItemBody::TightParagraph) {
2437                        // tight paragraphs emit nothing
2438                        self.tree.push();
2439                        self.tree.cur()
2440                    } else {
2441                        Some(cur_ix)
2442                    };
2443                if let Some(cur_ix) = maybe_cur_ix {
2444                    if self.tree[cur_ix].item.body.is_maybe_inline() {
2445                        self.handle_inline();
2446                    }
2447
2448                    let item = self.tree[cur_ix].item.clone();
2449                    let event = item_to_event(&item, self.text, &mut self.allocs);
2450                    if let Event::Start(ref _tag) = event {
2451                        self.tree.push();
2452                    } else {
2453                        self.tree.next_sibling(cur_ix);
2454                    }
2455                    Some(event)
2456                } else {
2457                    None
2458                }
2459            }
2460        }
2461    }
2462}
2463
2464impl<'a, F: BrokenLinkCallback<'a>> FusedIterator for Parser<'a, F> {}
2465
2466#[cfg(test)]
2467mod test {
2468    use super::*;
2469    use crate::tree::Node;
2470
2471    // TODO: move these tests to tests/html.rs?
2472
2473    fn parser_with_extensions(text: &str) -> Parser<'_> {
2474        let mut opts = Options::empty();
2475        opts.insert(Options::ENABLE_TABLES);
2476        opts.insert(Options::ENABLE_FOOTNOTES);
2477        opts.insert(Options::ENABLE_STRIKETHROUGH);
2478        opts.insert(Options::ENABLE_SUPERSCRIPT);
2479        opts.insert(Options::ENABLE_SUBSCRIPT);
2480        opts.insert(Options::ENABLE_TASKLISTS);
2481
2482        Parser::new_ext(text, opts)
2483    }
2484
2485    #[test]
2486    #[cfg(target_pointer_width = "64")]
2487    fn node_size() {
2488        let node_size = std::mem::size_of::<Node<Item>>();
2489        assert_eq!(48, node_size);
2490    }
2491
2492    #[test]
2493    #[cfg(target_pointer_width = "64")]
2494    fn body_size() {
2495        let body_size = std::mem::size_of::<ItemBody>();
2496        assert_eq!(16, body_size);
2497    }
2498
2499    #[test]
2500    fn single_open_fish_bracket() {
2501        // dont crash
2502        assert_eq!(3, Parser::new("<").count());
2503    }
2504
2505    #[test]
2506    fn lone_hashtag() {
2507        // dont crash
2508        assert_eq!(2, Parser::new("#").count());
2509    }
2510
2511    #[test]
2512    fn lots_of_backslashes() {
2513        // dont crash
2514        Parser::new("\\\\\r\r").count();
2515        Parser::new("\\\r\r\\.\\\\\r\r\\.\\").count();
2516    }
2517
2518    #[test]
2519    fn issue_1095() {
2520        let s = "- [n]:Z\r\n\t\t";
2521        let parser = Parser::new_ext(s, Options::all());
2522        for _ in parser {}
2523    }
2524
2525    #[test]
2526    fn issue_1084() {
2527        let markdown_input = "* [ ] ~![=?\\*\x0c\x00\x00  \x0d* [  1=1\x00\x0d<!]:[=?\\\x0d\x0c\n* [ ] \x0d\x0c%    ";
2528
2529        let mut options = Options::empty();
2530        options.insert(Options::ENABLE_TASKLISTS);
2531        options.insert(Options::ENABLE_STRIKETHROUGH);
2532
2533        let parser = Parser::new_ext(markdown_input, options);
2534        for _ in parser {}
2535    }
2536
2537    #[test]
2538    fn issue_1030() {
2539        let mut opts = Options::empty();
2540        opts.insert(Options::ENABLE_WIKILINKS);
2541
2542        let parser = Parser::new_ext("For a new ferrari, [[Wikientry|click here]]!", opts);
2543
2544        let offsets = parser
2545            .into_offset_iter()
2546            .map(|(_ev, range)| range)
2547            .collect::<Vec<_>>();
2548        let expected_offsets = vec![
2549            (0..44),  // Paragraph START
2550            (0..19),  // `For a new ferrari, `
2551            (19..43), // Wikilink START
2552            (31..41), // `click here`
2553            (19..43), // Wikilink END
2554            (43..44), // `!`
2555            (0..44),  // Paragraph END
2556        ];
2557        assert_eq!(offsets, expected_offsets);
2558    }
2559
2560    #[test]
2561    fn issue_320() {
2562        // dont crash
2563        parser_with_extensions(":\r\t> |\r:\r\t> |\r").count();
2564    }
2565
2566    #[test]
2567    fn issue_319() {
2568        // dont crash
2569        parser_with_extensions("|\r-]([^|\r-]([^").count();
2570        parser_with_extensions("|\r\r=][^|\r\r=][^car").count();
2571    }
2572
2573    #[test]
2574    fn issue_303() {
2575        // dont crash
2576        parser_with_extensions("[^\r\ra]").count();
2577        parser_with_extensions("\r\r]Z[^\x00\r\r]Z[^\x00").count();
2578    }
2579
2580    #[test]
2581    fn issue_313() {
2582        // dont crash
2583        parser_with_extensions("*]0[^\r\r*]0[^").count();
2584        parser_with_extensions("[^\r> `][^\r> `][^\r> `][").count();
2585    }
2586
2587    #[test]
2588    fn issue_311() {
2589        // dont crash
2590        parser_with_extensions("\\\u{0d}-\u{09}\\\u{0d}-\u{09}").count();
2591    }
2592
2593    #[test]
2594    fn issue_283() {
2595        let input = std::str::from_utf8(b"\xf0\x9b\xb2\x9f<td:^\xf0\x9b\xb2\x9f").unwrap();
2596        // dont crash
2597        parser_with_extensions(input).count();
2598    }
2599
2600    #[test]
2601    fn issue_289() {
2602        // dont crash
2603        parser_with_extensions("> - \\\n> - ").count();
2604        parser_with_extensions("- \n\n").count();
2605    }
2606
2607    #[test]
2608    fn issue_306() {
2609        // dont crash
2610        parser_with_extensions("*\r_<__*\r_<__*\r_<__*\r_<__").count();
2611    }
2612
2613    #[test]
2614    fn issue_305() {
2615        // dont crash
2616        parser_with_extensions("_6**6*_*").count();
2617    }
2618
2619    #[test]
2620    fn another_emphasis_panic() {
2621        parser_with_extensions("*__#_#__*").count();
2622    }
2623
2624    #[test]
2625    fn offset_iter() {
2626        let event_offsets: Vec<_> = Parser::new("*hello* world")
2627            .into_offset_iter()
2628            .map(|(_ev, range)| range)
2629            .collect();
2630        let expected_offsets = vec![(0..13), (0..7), (1..6), (0..7), (7..13), (0..13)];
2631        assert_eq!(expected_offsets, event_offsets);
2632    }
2633
2634    #[test]
2635    fn issue_1142_deflist_offset_is_char_boundary() {
2636        // Title `%`, definition whose text is combining acute U+0301, then a
2637        // TAB-indented second definition. The second definition's start used
2638        // to land inside the combining character.
2639        let doc = "%\n:\u{301}\n\t:";
2640        let mut opts = Options::empty();
2641        opts.insert(Options::ENABLE_DEFINITION_LIST);
2642        for (_event, range) in Parser::new_ext(doc, opts).into_offset_iter() {
2643            let _ = &doc[range];
2644        }
2645    }
2646
2647    #[test]
2648    fn reference_link_offsets() {
2649        let range =
2650            Parser::new("# H1\n[testing][Some reference]\n\n[Some reference]: https://github.com")
2651                .into_offset_iter()
2652                .filter_map(|(ev, range)| match ev {
2653                    Event::Start(
2654                        Tag::Link {
2655                            link_type: LinkType::Reference,
2656                            ..
2657                        },
2658                        ..,
2659                    ) => Some(range),
2660                    _ => None,
2661                })
2662                .next()
2663                .unwrap();
2664        assert_eq!(5..30, range);
2665    }
2666
2667    #[test]
2668    fn footnote_offsets() {
2669        let range = parser_with_extensions("Testing this[^1] out.\n\n[^1]: Footnote.")
2670            .into_offset_iter()
2671            .filter_map(|(ev, range)| match ev {
2672                Event::FootnoteReference(..) => Some(range),
2673                _ => None,
2674            })
2675            .next()
2676            .unwrap();
2677        assert_eq!(12..16, range);
2678    }
2679
2680    #[test]
2681    fn footnote_offsets_exclamation() {
2682        let mut immediately_before_footnote = None;
2683        let range = parser_with_extensions("Testing this![^1] out.\n\n[^1]: Footnote.")
2684            .into_offset_iter()
2685            .filter_map(|(ev, range)| match ev {
2686                Event::FootnoteReference(..) => Some(range),
2687                _ => {
2688                    immediately_before_footnote = Some((ev, range));
2689                    None
2690                }
2691            })
2692            .next()
2693            .unwrap();
2694        assert_eq!(13..17, range);
2695        if let (Event::Text(exclamation), range_exclamation) =
2696            immediately_before_footnote.as_ref().unwrap()
2697        {
2698            assert_eq!("!", &exclamation[..]);
2699            assert_eq!(&(12..13), range_exclamation);
2700        } else {
2701            panic!("what came first, then? {immediately_before_footnote:?}");
2702        }
2703    }
2704
2705    #[test]
2706    fn table_offset() {
2707        let markdown = "a\n\nTesting|This|Outtt\n--|:--:|--:\nSome Data|Other data|asdf";
2708        let event_offset = parser_with_extensions(markdown)
2709            .into_offset_iter()
2710            .map(|(_ev, range)| range)
2711            .nth(3)
2712            .unwrap();
2713        let expected_offset = 3..59;
2714        assert_eq!(expected_offset, event_offset);
2715    }
2716
2717    #[test]
2718    fn table_cell_span() {
2719        let markdown = "a|b|c\n--|--|--\na|  |c";
2720        let event_offset = parser_with_extensions(markdown)
2721            .into_offset_iter()
2722            .filter_map(|(ev, span)| match ev {
2723                Event::Start(Tag::TableCell) => Some(span),
2724                _ => None,
2725            })
2726            .nth(4)
2727            .unwrap();
2728        let expected_offset_start = "a|b|c\n--|--|--\na|".len();
2729        assert_eq!(
2730            expected_offset_start..(expected_offset_start + 2),
2731            event_offset
2732        );
2733    }
2734
2735    #[test]
2736    fn offset_iter_issue_378() {
2737        let event_offsets: Vec<_> = Parser::new("a [b](c) d")
2738            .into_offset_iter()
2739            .map(|(_ev, range)| range)
2740            .collect();
2741        let expected_offsets = vec![(0..10), (0..2), (2..8), (3..4), (2..8), (8..10), (0..10)];
2742        assert_eq!(expected_offsets, event_offsets);
2743    }
2744
2745    #[test]
2746    fn offset_iter_issue_404() {
2747        let event_offsets: Vec<_> = Parser::new("###\n")
2748            .into_offset_iter()
2749            .map(|(_ev, range)| range)
2750            .collect();
2751        let expected_offsets = vec![(0..4), (0..4)];
2752        assert_eq!(expected_offsets, event_offsets);
2753    }
2754
2755    // FIXME: add this one regression suite
2756    #[cfg(feature = "html")]
2757    #[test]
2758    fn link_def_at_eof() {
2759        let test_str = "[My site][world]\n\n[world]: https://vincentprouillet.com";
2760        let expected = "<p><a href=\"https://vincentprouillet.com\">My site</a></p>\n";
2761
2762        let mut buf = String::new();
2763        crate::html::push_html(&mut buf, Parser::new(test_str));
2764        assert_eq!(expected, buf);
2765    }
2766
2767    #[cfg(feature = "html")]
2768    #[test]
2769    fn no_footnote_refs_without_option() {
2770        let test_str = "a [^a]\n\n[^a]: yolo";
2771        let expected = "<p>a <a href=\"yolo\">^a</a></p>\n";
2772
2773        let mut buf = String::new();
2774        crate::html::push_html(&mut buf, Parser::new(test_str));
2775        assert_eq!(expected, buf);
2776    }
2777
2778    #[cfg(feature = "html")]
2779    #[test]
2780    fn ref_def_at_eof() {
2781        let test_str = "[test]:\\";
2782        let expected = "";
2783
2784        let mut buf = String::new();
2785        crate::html::push_html(&mut buf, Parser::new(test_str));
2786        assert_eq!(expected, buf);
2787    }
2788
2789    #[cfg(feature = "html")]
2790    #[test]
2791    fn ref_def_cr_lf() {
2792        let test_str = "[a]: /u\r\n\n[a]";
2793        let expected = "<p><a href=\"/u\">a</a></p>\n";
2794
2795        let mut buf = String::new();
2796        crate::html::push_html(&mut buf, Parser::new(test_str));
2797        assert_eq!(expected, buf);
2798    }
2799
2800    #[cfg(feature = "html")]
2801    #[test]
2802    fn no_dest_refdef() {
2803        let test_str = "[a]:";
2804        let expected = "<p>[a]:</p>\n";
2805
2806        let mut buf = String::new();
2807        crate::html::push_html(&mut buf, Parser::new(test_str));
2808        assert_eq!(expected, buf);
2809    }
2810
2811    #[test]
2812    fn broken_links_called_only_once() {
2813        for &(markdown, expected) in &[
2814            ("See also [`g()`][crate::g].", 1),
2815            ("See also [`g()`][crate::g][].", 1),
2816            ("[brokenlink1] some other node [brokenlink2]", 2),
2817        ] {
2818            let mut times_called = 0;
2819            let callback = &mut |_broken_link: BrokenLink| {
2820                times_called += 1;
2821                None
2822            };
2823            let parser =
2824                Parser::new_with_broken_link_callback(markdown, Options::empty(), Some(callback));
2825            for _ in parser {}
2826            assert_eq!(times_called, expected);
2827        }
2828    }
2829
2830    #[test]
2831    fn simple_broken_link_callback() {
2832        let test_str = "This is a link w/o def: [hello][world]";
2833        let mut callback = |broken_link: BrokenLink| {
2834            assert_eq!("world", broken_link.reference.as_ref());
2835            assert_eq!(&test_str[broken_link.span], "[hello][world]");
2836            let url = "YOLO".into();
2837            let title = "SWAG".to_owned().into();
2838            Some((url, title))
2839        };
2840        let parser =
2841            Parser::new_with_broken_link_callback(test_str, Options::empty(), Some(&mut callback));
2842        let mut link_tag_count = 0;
2843        for (typ, url, title, id) in parser.filter_map(|event| match event {
2844            Event::Start(tag) => match tag {
2845                Tag::Link {
2846                    link_type,
2847                    dest_url,
2848                    title,
2849                    id,
2850                } => Some((link_type, dest_url, title, id)),
2851                _ => None,
2852            },
2853            _ => None,
2854        }) {
2855            link_tag_count += 1;
2856            assert_eq!(typ, LinkType::ReferenceUnknown);
2857            assert_eq!(url.as_ref(), "YOLO");
2858            assert_eq!(title.as_ref(), "SWAG");
2859            assert_eq!(id.as_ref(), "world");
2860        }
2861        assert!(link_tag_count > 0);
2862    }
2863
2864    #[test]
2865    fn code_block_kind_check_fenced() {
2866        let parser = Parser::new("hello\n```test\ntadam\n```");
2867        let mut found = 0;
2868        for (ev, _range) in parser.into_offset_iter() {
2869            match ev {
2870                Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(syntax))) => {
2871                    assert_eq!(syntax.as_ref(), "test");
2872                    found += 1;
2873                }
2874                _ => {}
2875            }
2876        }
2877        assert_eq!(found, 1);
2878    }
2879
2880    #[test]
2881    fn code_block_kind_check_indented() {
2882        let parser = Parser::new("hello\n\n    ```test\n    tadam\nhello");
2883        let mut found = 0;
2884        for (ev, _range) in parser.into_offset_iter() {
2885            match ev {
2886                Event::Start(Tag::CodeBlock(CodeBlockKind::Indented)) => {
2887                    found += 1;
2888                }
2889                _ => {}
2890            }
2891        }
2892        assert_eq!(found, 1);
2893    }
2894
2895    #[test]
2896    fn ref_defs() {
2897        let input = r###"[a B c]: http://example.com
2898[another]: https://google.com
2899
2900text
2901
2902[final ONE]: http://wikipedia.org
2903"###;
2904        let mut parser = Parser::new(input);
2905
2906        assert!(parser.reference_definitions().get("a b c").is_some());
2907        assert!(parser.reference_definitions().get("nope").is_none());
2908
2909        if let Some(_event) = parser.next() {
2910            // testing keys with shorter lifetimes than parser and its input
2911            let s = "final one".to_owned();
2912            let link_def = parser.reference_definitions().get(&s).unwrap();
2913            let span = &input[link_def.span.clone()];
2914            assert_eq!(span, "[final ONE]: http://wikipedia.org");
2915        }
2916    }
2917
2918    #[test]
2919    fn common_lifetime_patterns_allowed<'b>() {
2920        let temporary_str = String::from("xyz");
2921
2922        // NOTE: this is a limitation of Rust, it doesn't allow putting lifetime parameters on the closure itself.
2923        // Hack it by attaching the lifetime to the test function instead.
2924        // TODO: why is the `'b` lifetime required at all? Changing it to `'_` breaks things :(
2925        let mut closure = |link: BrokenLink<'b>| Some(("#".into(), link.reference));
2926
2927        fn function(link: BrokenLink<'_>) -> Option<(CowStr<'_>, CowStr<'_>)> {
2928            Some(("#".into(), link.reference))
2929        }
2930
2931        for _ in Parser::new_with_broken_link_callback(
2932            "static lifetime",
2933            Options::empty(),
2934            Some(&mut closure),
2935        ) {}
2936        /* This fails to compile. Because the closure can't say `for <'a> fn(BrokenLink<'a>) ->
2937         * CowStr<'a>` and has to use the enclosing `'b` lifetime parameter, `temporary_str` lives
2938         * shorter than `'b`. I think this is unlikely to occur in real life, and if it does, the
2939         * fix is simple: move it out to a function that allows annotating the lifetimes.
2940         */
2941        //for _ in Parser::new_with_broken_link_callback(&temporary_str, Options::empty(), Some(&mut callback)) {
2942        //}
2943
2944        for _ in Parser::new_with_broken_link_callback(
2945            "static lifetime",
2946            Options::empty(),
2947            Some(&mut function),
2948        ) {}
2949        for _ in Parser::new_with_broken_link_callback(
2950            &temporary_str,
2951            Options::empty(),
2952            Some(&mut function),
2953        ) {}
2954    }
2955
2956    #[test]
2957    fn inline_html_inside_blockquote() {
2958        // Regression for #960
2959        let input = "> <foo\n> bar>";
2960        let events: Vec<_> = Parser::new(input).collect();
2961        let expected = [
2962            Event::Start(Tag::BlockQuote(None)),
2963            Event::Start(Tag::Paragraph),
2964            Event::InlineHtml(CowStr::Boxed("<foo\nbar>".to_string().into())),
2965            Event::End(TagEnd::Paragraph),
2966            Event::End(TagEnd::BlockQuote(None)),
2967        ];
2968        assert_eq!(&events, &expected);
2969    }
2970
2971    #[test]
2972    fn wikilink_has_pothole() {
2973        let input = "[[foo]] [[bar|baz]]";
2974        let events: Vec<_> = Parser::new_ext(input, Options::ENABLE_WIKILINKS).collect();
2975        let expected = [
2976            Event::Start(Tag::Paragraph),
2977            Event::Start(Tag::Link {
2978                link_type: LinkType::WikiLink { has_pothole: false },
2979                dest_url: CowStr::Borrowed("foo"),
2980                title: CowStr::Borrowed(""),
2981                id: CowStr::Borrowed(""),
2982            }),
2983            Event::Text(CowStr::Borrowed("foo")),
2984            Event::End(TagEnd::Link),
2985            Event::Text(CowStr::Borrowed(" ")),
2986            Event::Start(Tag::Link {
2987                link_type: LinkType::WikiLink { has_pothole: true },
2988                dest_url: CowStr::Borrowed("bar"),
2989                title: CowStr::Borrowed(""),
2990                id: CowStr::Borrowed(""),
2991            }),
2992            Event::Text(CowStr::Borrowed("baz")),
2993            Event::End(TagEnd::Link),
2994            Event::End(TagEnd::Paragraph),
2995        ];
2996        assert_eq!(&events, &expected);
2997    }
2998
2999    #[test]
3000    fn wikilink_no_event_blowup() {
3001        // Regression: handle_wikilink reused an existing tree node as
3002        // the wikilink's child without terminating that node's `next`
3003        // chain.  The `next` chain threaded through the closing `]]`
3004        // into the content after the wikilink, so the EventIter
3005        // visited that tail content both as children (via child ptr)
3006        // and as siblings (via next ptr). Adversarial repeated
3007        // `[[|]]` patterns compounded exponentially.
3008        let one = "[[[[^(\n|]]]]=]]]]]]]]\n".repeat(1);
3009        let eight = "[[[[^(\n|]]]]=]]]]]]]]\n".repeat(8);
3010
3011        let n1 = Parser::new_ext(&one, Options::ENABLE_WIKILINKS).count();
3012        let n8 = Parser::new_ext(&eight, Options::ENABLE_WIKILINKS).count();
3013
3014        assert!(
3015            n8 <= n1 * 20,
3016            "Event count should scale linearly with input length; \
3017             got {n1} events for 1× and {n8} events for 8× ({}× ratio, expected ≤20×)",
3018            n8 / n1.max(1)
3019        );
3020    }
3021
3022    #[test]
3023    fn issue_1154() {
3024        // If a `_` that can only close finds no opener, a `_` of the same length
3025        // that can also open won't find one either, so its bound can go up too.
3026        let mut tree = Tree::with_capacity(4);
3027        let mut stack = InlineStack::default();
3028        for _ in 0..3 {
3029            let start = tree.append(Item {
3030                start: 0,
3031                end: 1,
3032                body: ItemBody::Text {
3033                    backslash_escaped: false,
3034                },
3035            });
3036            stack.push(InlineEl {
3037                start,
3038                count: 1,
3039                run_length: 1,
3040                c: b'*',
3041                both: false,
3042            });
3043        }
3044        assert!(stack.find_match(&mut tree, b'_', 1, false).is_none());
3045        assert_eq!(3, stack.get_lowerbound(b'_', 1, false));
3046        assert_eq!(3, stack.get_lowerbound(b'_', 1, true));
3047    }
3048}