1use 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
42pub(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 MaybeEmphasis(usize, bool, bool),
62 MaybeMath(bool, bool, u8),
64 MaybeSmartQuote(u8, bool, bool),
66 MaybeCode(usize, bool), MaybeHtml,
68 MaybeLinkOpen,
69 MaybeLinkClose(bool),
71 MaybeImage,
72
73 Emphasis,
75 Strong,
76 Strikethrough,
77 Superscript,
78 Subscript,
79 Math(CowIndex, bool), Code(CowIndex),
81 Link(LinkIndex),
82 Image(LinkIndex),
83 FootnoteReference(CowIndex),
84 TaskListMarker(bool), InlineHtml,
88 OwnedInlineHtml(CowIndex),
89 SynthesizeText(CowIndex),
90 SynthesizeChar(char),
91 Html,
92 Text {
93 backslash_escaped: bool,
94 },
95 SoftBreak,
96 HardBreak(bool),
98
99 #[default]
101 Root,
102
103 Paragraph,
105 TightParagraph,
106 Rule,
107 Heading(HeadingLevel, Option<HeadingIndex>), FencedCodeBlock(CowIndex),
109 IndentCodeBlock,
110 HtmlBlock,
111 BlockQuote(Option<BlockQuoteKind>),
112 List(Cell<ListFlags>, u8, u64),
114 ListItem(usize), FootnoteDefinition(CowIndex),
116 MetadataBlock(MetadataBlockKind),
117
118 DefinitionList(Cell<ListFlags>),
120 MaybeDefinitionListTitle,
123 DefinitionListTitle,
124 DefinitionListDefinition(usize),
125
126 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 #[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
198pub 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 link_ref_expansion_limit: usize,
224
225 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 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 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 pub fn new(text: &'input str) -> Self {
263 Self::new_ext(text, Options::empty())
264 }
265
266 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 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 link_ref_expansion_limit: text.len().max(100_000),
301 code_delims: CodeDelims::new(),
302 math_delims: MathDelims::new(),
303 }
304 }
305
306 pub fn reference_definitions(&self) -> &RefDefs<'_> {
309 &self.allocs.refdefs
310 }
311
312 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 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 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 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 fn handle_inline(&mut self) {
390 self.handle_inline_pass1();
391 self.handle_emphasis_and_hard_break();
392 }
393
394 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 self.math_delims
490 .find(&self.tree, cur_ix, is_display, brace_context)
491 } else {
492 let mut scan = self.tree[cur_ix].next;
495 if is_display {
496 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 self.math_delims.clear();
524 break;
525 } else {
526 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 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 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 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 let scan_result =
707 scan_reference(&self.tree, block_text, next, self.options);
708 let (node_after_link, link_type) = match scan_result {
709 RefScan::LinkLabel(_, end_ix) => {
711 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 RefScan::Collapsed(next_node) => {
730 if !could_be_ref {
733 continue;
734 }
735 (next_node, LinkType::Collapsed)
736 }
737 RefScan::Failed | RefScan::UnexpectedFootnote => {
741 if !could_be_ref {
742 continue;
743 }
744 (next, LinkType::Shortcut)
745 }
746 };
747
748 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 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 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 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 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 self.tree[tos.node].next = node_after_link;
841
842 if label_node != cur {
844 self.tree[tos.node].child = label_node;
845
846 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 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 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 if let Some(tos) = self.wikilink_stack.pop() {
892 if tos.ty == LinkStackTy::Disabled {
893 return None;
894 }
895 let Some(body_node) = self.tree[tos.node].next.and_then(|ix| self.tree[ix].next) else {
897 return None;
899 };
900 let start_ix = self.tree[body_node].item.start;
901 let end_ix = self.tree[cur_ix].item.start;
902 if end_ix <= start_ix {
906 return None;
907 }
908 let wikilink = match scan_wikilink_pipe(
909 block_text,
910 start_ix, end_ix - start_ix,
912 ) {
913 Some((rest, wikitext)) => {
914 if wikitext.is_empty() {
916 return None;
917 }
918 if rest >= end_ix {
920 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 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 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 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 if let Some(prev_ix) = prev {
1013 self.tree[prev_ix].next = None;
1014 }
1015 let match_count = min(count, el.count);
1016 let mut end = cur_ix - 1;
1018 let mut start = el.start + el.count;
1019
1020 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 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 _ => {
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 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 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 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 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 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 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 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 &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 pub fn into_offset_iter(self) -> OffsetIter<'input, F> {
1474 OffsetIter { inner: self }
1475 }
1476}
1477
1478pub(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 ItemBody::List(ref list_flags, _, _) | ItemBody::DefinitionList(ref list_flags)
1492 if update_flags =>
1493 {
1494 if line_start.is_at_eol() {
1497 if list_flags.get().contains(ListFlags::LAST_LINE_EMPTY)
1498 && line_start.remaining_space() == 0
1499 {
1500 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 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 start: TreeIndex,
1607 count: usize,
1609 run_length: usize,
1611 c: u8,
1613 both: bool,
1615}
1616
1617#[derive(Debug, Clone, Default)]
1618struct InlineStack {
1619 stack: Vec<InlineEl>,
1620 lower_bounds: [usize; 9],
1625}
1626
1627impl InlineStack {
1628 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 LinkLabel(CowStr<'a>, usize),
1754 Collapsed(Option<TreeIndex>),
1756 UnexpectedFootnote,
1757 Failed,
1758}
1759
1760fn 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
1777fn 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 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#[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#[derive(Clone, Debug)]
1901pub struct FootnoteDef {
1902 pub use_count: usize,
1903}
1904
1905struct 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 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
1949struct 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 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#[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#[derive(Clone, Default, Debug)]
2043pub struct RefDefs<'input>(pub(crate) HashMap<LinkLabel<'input>, LinkDef<'input>>);
2044
2045#[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 pub fn get(&'s self, key: &'b str) -> Option<&'b LinkDef<'input>> {
2055 self.0.get(&UniCase::new(key.into()))
2056 }
2057
2058 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 pub fn contains(&'s self, key: &'b str) -> bool {
2070 self.0.contains_key(&UniCase::new(key.into()))
2071 }
2072 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 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#[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
2182pub 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#[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#[derive(Debug)]
2235pub struct OffsetIter<'a, F = DefaultBrokenLinkCallback> {
2236 inner: Parser<'a, F>,
2237}
2238
2239impl<'a, F: BrokenLinkCallback<'a>> OffsetIter<'a, F> {
2240 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 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 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 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 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 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 assert_eq!(3, Parser::new("<").count());
2503 }
2504
2505 #[test]
2506 fn lone_hashtag() {
2507 assert_eq!(2, Parser::new("#").count());
2509 }
2510
2511 #[test]
2512 fn lots_of_backslashes() {
2513 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), (0..19), (19..43), (31..41), (19..43), (43..44), (0..44), ];
2557 assert_eq!(offsets, expected_offsets);
2558 }
2559
2560 #[test]
2561 fn issue_320() {
2562 parser_with_extensions(":\r\t> |\r:\r\t> |\r").count();
2564 }
2565
2566 #[test]
2567 fn issue_319() {
2568 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 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 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 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 parser_with_extensions(input).count();
2598 }
2599
2600 #[test]
2601 fn issue_289() {
2602 parser_with_extensions("> - \\\n> - ").count();
2604 parser_with_extensions("- \n\n").count();
2605 }
2606
2607 #[test]
2608 fn issue_306() {
2609 parser_with_extensions("*\r_<__*\r_<__*\r_<__*\r_<__").count();
2611 }
2612
2613 #[test]
2614 fn issue_305() {
2615 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 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 #[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 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 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 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 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 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 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}