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 return Some(tos.node);
982 }
983 }
984
985 None
986 }
987
988 fn handle_emphasis_and_hard_break(&mut self) {
989 let mut prev = None;
990 let mut prev_ix: TreeIndex;
991 let mut cur = self.tree.cur();
992
993 let mut single_quote_open: Option<TreeIndex> = None;
994 let mut double_quote_open: bool = false;
995
996 while let Some(mut cur_ix) = cur {
997 match self.tree[cur_ix].item.body {
998 ItemBody::MaybeEmphasis(mut count, can_open, can_close) => {
999 let run_length = count;
1000 let c = self.text.as_bytes()[self.tree[cur_ix].item.start];
1001 let both = can_open && can_close;
1002 if can_close {
1003 while let Some(el) =
1004 self.inline_stack
1005 .find_match(&mut self.tree, c, run_length, both)
1006 {
1007 if let Some(prev_ix) = prev {
1009 self.tree[prev_ix].next = None;
1010 }
1011 let match_count = min(count, el.count);
1012 let mut end = cur_ix - 1;
1014 let mut start = el.start + el.count;
1015
1016 while start > el.start + el.count - match_count {
1018 let inc = if start > el.start + el.count - match_count + 1 {
1019 2
1020 } else {
1021 1
1022 };
1023 let ty = if c == b'~' {
1024 if inc == 2 {
1025 if self.options.contains(Options::ENABLE_STRIKETHROUGH) {
1026 ItemBody::Strikethrough
1027 } else {
1028 ItemBody::Text {
1029 backslash_escaped: false,
1030 }
1031 }
1032 } else {
1033 if self.options.contains(Options::ENABLE_SUBSCRIPT) {
1034 ItemBody::Subscript
1035 } else if self
1036 .options
1037 .contains(Options::ENABLE_STRIKETHROUGH)
1038 {
1039 ItemBody::Strikethrough
1040 } else {
1041 ItemBody::Text {
1042 backslash_escaped: false,
1043 }
1044 }
1045 }
1046 } else if c == b'^' {
1047 if self.options.contains(Options::ENABLE_SUPERSCRIPT) {
1048 ItemBody::Superscript
1049 } else {
1050 ItemBody::Text {
1051 backslash_escaped: false,
1052 }
1053 }
1054 } else if inc == 2 {
1055 ItemBody::Strong
1056 } else {
1057 ItemBody::Emphasis
1058 };
1059
1060 let root = start - inc;
1061 end = end + inc;
1062 self.tree[root].item.body = ty;
1063 self.tree[root].item.end = self.tree[end].item.end;
1064 self.tree[root].child = Some(start);
1065 self.tree[root].next = None;
1066 start = root;
1067 }
1068
1069 prev_ix = el.start + el.count - match_count;
1071 prev = Some(prev_ix);
1072 cur = self.tree[cur_ix + match_count - 1].next;
1073 self.tree[prev_ix].next = cur;
1074
1075 if el.count > match_count {
1076 self.inline_stack.push(InlineEl {
1077 start: el.start,
1078 count: el.count - match_count,
1079 run_length: el.run_length,
1080 c: el.c,
1081 both: el.both,
1082 })
1083 }
1084 count -= match_count;
1085 if count > 0 {
1086 cur_ix = cur.unwrap();
1087 } else {
1088 break;
1089 }
1090 }
1091 }
1092 if count > 0 {
1093 if can_open {
1094 self.inline_stack.push(InlineEl {
1095 start: cur_ix,
1096 run_length,
1097 count,
1098 c,
1099 both,
1100 });
1101 } else {
1102 for i in 0..count {
1103 self.tree[cur_ix + i].item.body = ItemBody::Text {
1104 backslash_escaped: false,
1105 };
1106 }
1107 }
1108 prev_ix = cur_ix + count - 1;
1109 prev = Some(prev_ix);
1110 cur = self.tree[prev_ix].next;
1111 }
1112 }
1113 ItemBody::MaybeSmartQuote(c, can_open, can_close) => {
1114 self.tree[cur_ix].item.body = match c {
1115 b'\'' => {
1116 if let (Some(open_ix), true) = (single_quote_open, can_close) {
1117 self.tree[open_ix].item.body = ItemBody::SynthesizeChar('‘');
1118 single_quote_open = None;
1119 } else if can_open {
1120 single_quote_open = Some(cur_ix);
1121 }
1122 ItemBody::SynthesizeChar('’')
1123 }
1124 _ => {
1125 if can_close && double_quote_open {
1126 double_quote_open = false;
1127 ItemBody::SynthesizeChar('”')
1128 } else {
1129 if can_open && !double_quote_open {
1130 double_quote_open = true;
1131 }
1132 ItemBody::SynthesizeChar('“')
1133 }
1134 }
1135 };
1136 prev = cur;
1137 cur = self.tree[cur_ix].next;
1138 }
1139 ItemBody::HardBreak(true) => {
1140 if self.tree[cur_ix].next.is_none() {
1141 self.tree[cur_ix].item.body = ItemBody::SynthesizeChar('\\');
1142 }
1143 prev = cur;
1144 cur = self.tree[cur_ix].next;
1145 }
1146 _ => {
1147 prev = cur;
1148 cur = self.tree[cur_ix].next;
1149 }
1150 }
1151 }
1152 self.inline_stack.pop_all(&mut self.tree);
1153 }
1154
1155 fn disable_all_links(&mut self) {
1156 self.link_stack.disable_all_links();
1157 self.wikilink_stack.disable_all_links();
1158 }
1159
1160 fn scan_inline_link(
1162 &self,
1163 underlying: &'input str,
1164 mut ix: usize,
1165 node: Option<TreeIndex>,
1166 ) -> Option<(usize, CowStr<'input>, CowStr<'input>)> {
1167 if underlying.as_bytes().get(ix) != Some(&b'(') {
1168 return None;
1169 }
1170 ix += 1;
1171
1172 let scan_separator = |ix: &mut usize| {
1173 *ix += scan_while(&underlying.as_bytes()[*ix..], is_ascii_whitespace_no_nl);
1174 if let Some(bl) = scan_eol(&underlying.as_bytes()[*ix..]) {
1175 *ix += bl;
1176 *ix += skip_container_prefixes(
1177 &self.tree,
1178 &underlying.as_bytes()[*ix..],
1179 self.options,
1180 );
1181 }
1182 *ix += scan_while(&underlying.as_bytes()[*ix..], is_ascii_whitespace_no_nl);
1183 };
1184
1185 scan_separator(&mut ix);
1186
1187 let (dest_length, dest) = scan_link_dest(underlying, ix, LINK_MAX_NESTED_PARENS)?;
1188 let dest = unescape(dest, self.tree.is_in_table());
1189 ix += dest_length;
1190
1191 scan_separator(&mut ix);
1192
1193 let title = if let Some((bytes_scanned, t)) = self.scan_link_title(underlying, ix, node) {
1194 ix += bytes_scanned;
1195 scan_separator(&mut ix);
1196 t
1197 } else {
1198 "".into()
1199 };
1200 if underlying.as_bytes().get(ix) != Some(&b')') {
1201 return None;
1202 }
1203 ix += 1;
1204
1205 Some((ix, dest, title))
1206 }
1207
1208 fn scan_link_title(
1210 &self,
1211 text: &'input str,
1212 start_ix: usize,
1213 node: Option<TreeIndex>,
1214 ) -> Option<(usize, CowStr<'input>)> {
1215 let bytes = text.as_bytes();
1216 let open = match bytes.get(start_ix) {
1217 Some(b @ b'\'') | Some(b @ b'\"') | Some(b @ b'(') => *b,
1218 _ => return None,
1219 };
1220 let close = if open == b'(' { b')' } else { open };
1221
1222 let mut title = String::new();
1223 let mut mark = start_ix + 1;
1224 let mut i = start_ix + 1;
1225
1226 while i < bytes.len() {
1227 let c = bytes[i];
1228
1229 if c == close {
1230 let cow = if mark == 1 {
1231 (i - start_ix + 1, text[mark..i].into())
1232 } else {
1233 title.push_str(&text[mark..i]);
1234 (i - start_ix + 1, title.into())
1235 };
1236
1237 return Some(cow);
1238 }
1239 if c == open {
1240 return None;
1241 }
1242
1243 if c == b'\n' || c == b'\r' {
1244 if let Some(node_ix) = scan_nodes_to_ix(&self.tree, node, i + 1) {
1245 if self.tree[node_ix].item.start > i {
1246 title.push_str(&text[mark..i]);
1247 title.push('\n');
1248 i = self.tree[node_ix].item.start;
1249 mark = i;
1250 continue;
1251 }
1252 }
1253 }
1254 if c == b'&' {
1255 if let (n, Some(value)) = scan_entity(&bytes[i..]) {
1256 title.push_str(&text[mark..i]);
1257 title.push_str(&value);
1258 i += n;
1259 mark = i;
1260 continue;
1261 }
1262 }
1263 if self.tree.is_in_table()
1264 && c == b'\\'
1265 && i + 2 < bytes.len()
1266 && bytes[i + 1] == b'\\'
1267 && bytes[i + 2] == b'|'
1268 {
1269 title.push_str(&text[mark..i]);
1272 i += 2;
1273 mark = i;
1274 }
1275 if c == b'\\' && i + 1 < bytes.len() && is_ascii_punctuation(bytes[i + 1]) {
1276 title.push_str(&text[mark..i]);
1277 i += 1;
1278 mark = i;
1279 }
1280
1281 i += 1;
1282 }
1283
1284 None
1285 }
1286
1287 fn make_math_span(&mut self, open: TreeIndex, mut close: TreeIndex) {
1288 let start_is_display = self.tree[open].next.filter(|&next_ix| {
1289 next_ix != close
1290 && matches!(
1291 self.tree[next_ix].item.body,
1292 ItemBody::MaybeMath(_can_open, _can_close, _brace_context)
1293 )
1294 });
1295 let end_is_display = self.tree[close].next.filter(|&next_ix| {
1296 matches!(
1297 self.tree[next_ix].item.body,
1298 ItemBody::MaybeMath(_can_open, _can_close, _brace_context)
1299 )
1300 });
1301 let is_display = start_is_display.is_some() && end_is_display.is_some();
1302 if is_display {
1303 close = self.tree[close].next.unwrap();
1305 self.tree[open].next = Some(close);
1306 self.tree[open].item.end += 1;
1307 self.tree[close].item.start -= 1;
1308 } else {
1309 if self.tree[open].item.end == self.tree[close].item.start {
1310 self.tree[open].item.body = ItemBody::Text {
1312 backslash_escaped: false,
1313 };
1314 return;
1315 }
1316 self.tree[open].next = Some(close);
1317 }
1318 let span_start = self.tree[open].item.end;
1319 let span_end = self.tree[close].item.start;
1320
1321 let spanned_text = &self.text[span_start..span_end];
1322 let spanned_bytes = spanned_text.as_bytes();
1323 let mut buf: Option<String> = None;
1324
1325 let mut start_ix = 0;
1326 let mut ix = 0;
1327 while ix < spanned_bytes.len() {
1328 let c = spanned_bytes[ix];
1329 if c == b'\r' || c == b'\n' {
1330 ix += 1;
1331 let buf = buf.get_or_insert_with(|| String::with_capacity(spanned_bytes.len()));
1332 buf.push_str(&spanned_text[start_ix..ix]);
1333 ix += skip_container_prefixes(&self.tree, &spanned_bytes[ix..], self.options);
1334 start_ix = ix;
1335 } else if c == b'\\'
1336 && spanned_bytes.get(ix + 1) == Some(&b'|')
1337 && self.tree.is_in_table()
1338 {
1339 let buf = buf.get_or_insert_with(|| String::with_capacity(spanned_bytes.len()));
1340 buf.push_str(&spanned_text[start_ix..ix]);
1341 buf.push('|');
1342 ix += 2;
1343 start_ix = ix;
1344 } else {
1345 ix += 1;
1346 }
1347 }
1348
1349 let cow = if let Some(mut buf) = buf {
1350 buf.push_str(&spanned_text[start_ix..]);
1351 buf.into()
1352 } else {
1353 spanned_text.into()
1354 };
1355
1356 self.tree[open].item.body = ItemBody::Math(self.allocs.allocate_cow(cow), is_display);
1357 self.tree[open].item.end = self.tree[close].item.end;
1358 self.tree[open].next = self.tree[close].next;
1359 }
1360
1361 fn make_code_span(&mut self, open: TreeIndex, close: TreeIndex, preceding_backslash: bool) {
1365 let span_start = self.tree[open].item.end;
1366 let span_end = self.tree[close].item.start;
1367 let mut buf: Option<String> = None;
1368
1369 let spanned_text = &self.text[span_start..span_end];
1370 let spanned_bytes = spanned_text.as_bytes();
1371 let mut start_ix = 0;
1372 let mut ix = 0;
1373 while ix < spanned_bytes.len() {
1374 let c = spanned_bytes[ix];
1375 if c == b'\r' || c == b'\n' {
1376 let buf = buf.get_or_insert_with(|| String::with_capacity(spanned_bytes.len()));
1377 buf.push_str(&spanned_text[start_ix..ix]);
1378 buf.push(' ');
1379 ix += 1;
1380 ix += skip_container_prefixes(&self.tree, &spanned_bytes[ix..], self.options);
1381 start_ix = ix;
1382 } else if c == b'\\'
1383 && spanned_bytes.get(ix + 1) == Some(&b'|')
1384 && self.tree.is_in_table()
1385 {
1386 let buf = buf.get_or_insert_with(|| String::with_capacity(spanned_bytes.len()));
1387 buf.push_str(&spanned_text[start_ix..ix]);
1388 buf.push('|');
1389 ix += 2;
1390 start_ix = ix;
1391 } else {
1392 ix += 1;
1393 }
1394 }
1395
1396 let (opening, closing, all_spaces) = {
1397 let s = if let Some(buf) = &mut buf {
1398 buf.push_str(&spanned_text[start_ix..]);
1399 &buf[..]
1400 } else {
1401 spanned_text
1402 };
1403 (
1404 s.as_bytes().first() == Some(&b' '),
1405 s.as_bytes().last() == Some(&b' '),
1406 s.bytes().all(|b| b == b' '),
1407 )
1408 };
1409
1410 let cow: CowStr<'input> = if !all_spaces && opening && closing {
1411 if let Some(mut buf) = buf {
1412 if !buf.is_empty() {
1413 buf.remove(0);
1414 buf.pop();
1415 }
1416 buf.into()
1417 } else {
1418 spanned_text[1..(spanned_text.len() - 1).max(1)].into()
1419 }
1420 } else if let Some(buf) = buf {
1421 buf.into()
1422 } else {
1423 spanned_text.into()
1424 };
1425
1426 if preceding_backslash {
1427 self.tree[open].item.body = ItemBody::Text {
1428 backslash_escaped: true,
1429 };
1430 self.tree[open].item.end = self.tree[open].item.start + 1;
1431 self.tree[open].next = Some(close);
1432 self.tree[close].item.body = ItemBody::Code(self.allocs.allocate_cow(cow));
1433 self.tree[close].item.start = self.tree[open].item.start + 1;
1434 } else {
1435 self.tree[open].item.body = ItemBody::Code(self.allocs.allocate_cow(cow));
1436 self.tree[open].item.end = self.tree[close].item.end;
1437 self.tree[open].next = self.tree[close].next;
1438 }
1439 }
1440
1441 fn scan_inline_html(&mut self, bytes: &[u8], ix: usize) -> Option<(Vec<u8>, usize)> {
1445 let c = *bytes.get(ix)?;
1446 if c == b'!' {
1447 Some((
1448 vec![],
1449 scan_inline_html_comment(bytes, ix + 1, &mut self.html_scan_guard)?,
1450 ))
1451 } else if c == b'?' {
1452 Some((
1453 vec![],
1454 scan_inline_html_processing(bytes, ix + 1, &mut self.html_scan_guard)?,
1455 ))
1456 } else {
1457 let (span, i) = scan_html_block_inner(
1458 &bytes[(ix - 1)..],
1460 Some(&|bytes| skip_container_prefixes(&self.tree, bytes, self.options)),
1461 )?;
1462 Some((span, i + ix - 1))
1463 }
1464 }
1465
1466 pub fn into_offset_iter(self) -> OffsetIter<'input, F> {
1470 OffsetIter { inner: self }
1471 }
1472}
1473
1474pub(crate) fn scan_containers(
1476 tree: &Tree<Item>,
1477 line_start: &mut LineStart<'_>,
1478 options: Options,
1479 update_flags: bool,
1480) -> usize {
1481 let mut i = 0;
1482 let mut spine_iter = tree.walk_spine();
1483 for &node_ix in &mut spine_iter {
1484 match tree[node_ix].item.body {
1485 ItemBody::List(ref list_flags, _, _) | ItemBody::DefinitionList(ref list_flags)
1488 if update_flags =>
1489 {
1490 if line_start.is_at_eol() {
1493 if list_flags.get().contains(ListFlags::LAST_LINE_EMPTY)
1494 && line_start.remaining_space() == 0
1495 {
1496 return tree.spine_len();
1503 } else {
1504 list_flags.set(list_flags.get() | ListFlags::LAST_LINE_EMPTY);
1505 }
1506 } else {
1507 list_flags.set(list_flags.get() & !ListFlags::LAST_LINE_EMPTY);
1508 }
1509 }
1510 ItemBody::BlockQuote(..) => {
1511 let save = line_start.clone();
1512 let _ = line_start.scan_space(3);
1513 if !line_start.scan_blockquote_marker() {
1514 *line_start = save;
1515 break;
1516 }
1517 }
1518 ItemBody::ListItem(indent) => {
1519 let save = line_start.clone();
1520 if !line_start.scan_space(indent) && !line_start.is_at_eol() {
1521 *line_start = save;
1522 break;
1523 }
1524 }
1525 ItemBody::DefinitionListDefinition(indent) => {
1526 let save = line_start.clone();
1527 if !line_start.scan_space(indent) && !line_start.is_at_eol() {
1528 *line_start = save;
1529 break;
1530 }
1531 }
1532 ItemBody::FootnoteDefinition(..) if options.has_gfm_footnotes() => {
1533 let save = line_start.clone();
1534 if !line_start.scan_space(4) && !line_start.is_at_eol() {
1535 *line_start = save;
1536 break;
1537 }
1538 }
1539 _ => (),
1540 }
1541 i += 1;
1542 }
1543 for &node_ix in &mut spine_iter {
1544 if let ItemBody::List(ref list_flags, _, _) | ItemBody::DefinitionList(ref list_flags) =
1545 tree[node_ix].item.body
1546 {
1547 list_flags.set(list_flags.get() & !ListFlags::LAST_LINE_EMPTY);
1548 }
1549 }
1550 i
1551}
1552pub(crate) fn skip_container_prefixes(tree: &Tree<Item>, bytes: &[u8], options: Options) -> usize {
1553 let mut line_start = LineStart::new(bytes);
1554 let _ = scan_containers(tree, &mut line_start, options, false);
1555 line_start.bytes_scanned()
1556}
1557
1558impl Tree<Item> {
1559 pub(crate) fn append_text(&mut self, start: usize, end: usize, backslash_escaped: bool) {
1560 if end > start {
1561 if let Some(ix) = self.cur() {
1562 if matches!(self[ix].item.body, ItemBody::Text { .. }) && self[ix].item.end == start
1563 {
1564 self[ix].item.end = end;
1565 return;
1566 }
1567 }
1568 self.append(Item {
1569 start,
1570 end,
1571 body: ItemBody::Text { backslash_escaped },
1572 });
1573 }
1574 }
1575 pub(crate) fn is_in_table(&self) -> bool {
1582 fn might_be_in_table(item: &Item) -> bool {
1583 item.body.is_inline()
1584 || matches!(item.body, |ItemBody::TableHead| ItemBody::TableRow
1585 | ItemBody::TableCell)
1586 }
1587 for &ix in self.walk_spine().rev() {
1588 if matches!(self[ix].item.body, ItemBody::Table(_)) {
1589 return true;
1590 }
1591 if !might_be_in_table(&self[ix].item) {
1592 return false;
1593 }
1594 }
1595 false
1596 }
1597}
1598
1599#[derive(Copy, Clone, Debug)]
1600struct InlineEl {
1601 start: TreeIndex,
1603 count: usize,
1605 run_length: usize,
1607 c: u8,
1609 both: bool,
1611}
1612
1613#[derive(Debug, Clone, Default)]
1614struct InlineStack {
1615 stack: Vec<InlineEl>,
1616 lower_bounds: [usize; 9],
1621}
1622
1623impl InlineStack {
1624 const UNDERSCORE_NOT_BOTH: usize = 0;
1628 const ASTERISK_NOT_BOTH: usize = 1;
1629 const ASTERISK_BASE: usize = 2;
1630 const TILDES: usize = 5;
1631 const UNDERSCORE_BASE: usize = 6;
1632
1633 fn pop_all(&mut self, tree: &mut Tree<Item>) {
1634 for el in self.stack.drain(..) {
1635 for i in 0..el.count {
1636 tree[el.start + i].item.body = ItemBody::Text {
1637 backslash_escaped: false,
1638 };
1639 }
1640 }
1641 self.lower_bounds = [0; 9];
1642 }
1643
1644 fn get_lowerbound(&self, c: u8, count: usize, both: bool) -> usize {
1645 if c == b'_' {
1646 let mod3_lower = self.lower_bounds[InlineStack::UNDERSCORE_BASE + count % 3];
1647 if both {
1648 mod3_lower
1649 } else {
1650 min(
1651 mod3_lower,
1652 self.lower_bounds[InlineStack::UNDERSCORE_NOT_BOTH],
1653 )
1654 }
1655 } else if c == b'*' {
1656 let mod3_lower = self.lower_bounds[InlineStack::ASTERISK_BASE + count % 3];
1657 if both {
1658 mod3_lower
1659 } else {
1660 min(
1661 mod3_lower,
1662 self.lower_bounds[InlineStack::ASTERISK_NOT_BOTH],
1663 )
1664 }
1665 } else {
1666 self.lower_bounds[InlineStack::TILDES]
1667 }
1668 }
1669
1670 fn set_lowerbound(&mut self, c: u8, count: usize, both: bool, new_bound: usize) {
1671 if c == b'_' {
1672 self.lower_bounds[InlineStack::UNDERSCORE_BASE + count % 3] = new_bound;
1673 if !both {
1674 self.lower_bounds[InlineStack::UNDERSCORE_NOT_BOTH] = new_bound;
1675 }
1676 } else if c == b'*' {
1677 self.lower_bounds[InlineStack::ASTERISK_BASE + count % 3] = new_bound;
1678 if !both {
1679 self.lower_bounds[InlineStack::ASTERISK_NOT_BOTH] = new_bound;
1680 }
1681 } else {
1682 self.lower_bounds[InlineStack::TILDES] = new_bound;
1683 }
1684 }
1685
1686 fn truncate(&mut self, new_bound: usize) {
1687 self.stack.truncate(new_bound);
1688 for lower_bound in &mut self.lower_bounds {
1689 if *lower_bound > new_bound {
1690 *lower_bound = new_bound;
1691 }
1692 }
1693 }
1694
1695 fn find_match(
1696 &mut self,
1697 tree: &mut Tree<Item>,
1698 c: u8,
1699 run_length: usize,
1700 both: bool,
1701 ) -> Option<InlineEl> {
1702 let lowerbound = min(self.stack.len(), self.get_lowerbound(c, run_length, both));
1703 let res = self.stack[lowerbound..]
1704 .iter()
1705 .cloned()
1706 .enumerate()
1707 .rfind(|(_, el)| {
1708 if c == b'~' && run_length != el.run_length {
1709 return false;
1710 }
1711 el.c == c
1712 && (!both && !el.both
1713 || (run_length + el.run_length) % 3 != 0
1714 || run_length % 3 == 0)
1715 });
1716
1717 if let Some((matching_ix, matching_el)) = res {
1718 let matching_ix = matching_ix + lowerbound;
1719 for el in &self.stack[(matching_ix + 1)..] {
1720 for i in 0..el.count {
1721 tree[el.start + i].item.body = ItemBody::Text {
1722 backslash_escaped: false,
1723 };
1724 }
1725 }
1726 self.truncate(matching_ix);
1727 Some(matching_el)
1728 } else {
1729 self.set_lowerbound(c, run_length, both, self.stack.len());
1730 None
1731 }
1732 }
1733
1734 fn trim_lower_bound(&mut self, ix: usize) {
1735 self.lower_bounds[ix] = self.lower_bounds[ix].min(self.stack.len());
1736 }
1737
1738 fn push(&mut self, el: InlineEl) {
1739 if el.c == b'~' {
1740 self.trim_lower_bound(InlineStack::TILDES);
1741 }
1742 self.stack.push(el)
1743 }
1744}
1745
1746#[derive(Debug, Clone)]
1747enum RefScan<'a> {
1748 LinkLabel(CowStr<'a>, usize),
1750 Collapsed(Option<TreeIndex>),
1752 UnexpectedFootnote,
1753 Failed,
1754}
1755
1756fn scan_nodes_to_ix(
1759 tree: &Tree<Item>,
1760 mut node: Option<TreeIndex>,
1761 ix: usize,
1762) -> Option<TreeIndex> {
1763 while let Some(node_ix) = node {
1764 if tree[node_ix].item.end <= ix {
1765 node = tree[node_ix].next;
1766 } else {
1767 break;
1768 }
1769 }
1770 node
1771}
1772
1773fn scan_link_label<'text>(
1776 tree: &Tree<Item>,
1777 text: &'text str,
1778 options: Options,
1779) -> Option<(usize, ReferenceLabel<'text>)> {
1780 let bytes = text.as_bytes();
1781 if bytes.len() < 2 || bytes[0] != b'[' {
1782 return None;
1783 }
1784 let linebreak_handler = |bytes: &[u8]| Some(skip_container_prefixes(tree, bytes, options));
1785 if options.contains(Options::ENABLE_FOOTNOTES)
1786 && b'^' == bytes[1]
1787 && bytes.get(2) != Some(&b']')
1788 {
1789 let linebreak_handler: &dyn Fn(&[u8]) -> Option<usize> = if options.has_gfm_footnotes() {
1790 &|_| None
1791 } else {
1792 &linebreak_handler
1793 };
1794 if let Some((byte_index, cow)) =
1795 scan_link_label_rest(&text[2..], linebreak_handler, tree.is_in_table())
1796 {
1797 return Some((byte_index + 2, ReferenceLabel::Footnote(cow)));
1798 }
1799 }
1800 let (byte_index, cow) =
1801 scan_link_label_rest(&text[1..], &linebreak_handler, tree.is_in_table())?;
1802 Some((byte_index + 1, ReferenceLabel::Link(cow)))
1803}
1804
1805fn scan_reference<'b>(
1806 tree: &Tree<Item>,
1807 text: &'b str,
1808 cur: Option<TreeIndex>,
1809 options: Options,
1810) -> RefScan<'b> {
1811 let cur_ix = match cur {
1812 None => return RefScan::Failed,
1813 Some(cur_ix) => cur_ix,
1814 };
1815 let start = tree[cur_ix].item.start;
1816 let tail = &text.as_bytes()[start..];
1817
1818 if tail.starts_with(b"[]") {
1819 let closing_node = tree[cur_ix].next.unwrap();
1821 RefScan::Collapsed(tree[closing_node].next)
1822 } else {
1823 let label = scan_link_label(tree, &text[start..], options);
1824 match label {
1825 Some((ix, ReferenceLabel::Link(label))) => RefScan::LinkLabel(label, start + ix),
1826 Some((_ix, ReferenceLabel::Footnote(_label))) => RefScan::UnexpectedFootnote,
1827 None => RefScan::Failed,
1828 }
1829 }
1830}
1831
1832#[derive(Clone, Default)]
1833struct LinkStack {
1834 inner: Vec<LinkStackEl>,
1835 disabled_ix: usize,
1836}
1837
1838impl LinkStack {
1839 fn push(&mut self, el: LinkStackEl) {
1840 self.inner.push(el);
1841 }
1842
1843 fn pop(&mut self) -> Option<LinkStackEl> {
1844 let el = self.inner.pop();
1845 self.disabled_ix = std::cmp::min(self.disabled_ix, self.inner.len());
1846 el
1847 }
1848
1849 fn clear(&mut self) {
1850 self.inner.clear();
1851 self.disabled_ix = 0;
1852 }
1853
1854 fn disable_all_links(&mut self) {
1855 for el in &mut self.inner[self.disabled_ix..] {
1856 if el.ty == LinkStackTy::Link {
1857 el.ty = LinkStackTy::Disabled;
1858 }
1859 }
1860 self.disabled_ix = self.inner.len();
1861 }
1862}
1863
1864#[derive(Clone, Debug)]
1865struct LinkStackEl {
1866 node: TreeIndex,
1867 ty: LinkStackTy,
1868}
1869
1870#[derive(PartialEq, Clone, Debug)]
1871enum LinkStackTy {
1872 Link,
1873 Image,
1874 Disabled,
1875}
1876
1877#[derive(Clone, Debug)]
1879pub struct LinkDef<'a> {
1880 pub dest: CowStr<'a>,
1881 pub title: Option<CowStr<'a>>,
1882 pub span: Range<usize>,
1883}
1884
1885impl<'a> LinkDef<'a> {
1886 pub fn into_static(self) -> LinkDef<'static> {
1887 LinkDef {
1888 dest: self.dest.into_static(),
1889 title: self.title.map(|s| s.into_static()),
1890 span: self.span,
1891 }
1892 }
1893}
1894
1895#[derive(Clone, Debug)]
1897pub struct FootnoteDef {
1898 pub use_count: usize,
1899}
1900
1901struct CodeDelims {
1904 inner: HashMap<usize, VecDeque<TreeIndex>>,
1905 seen_first: bool,
1906}
1907
1908impl CodeDelims {
1909 fn new() -> Self {
1910 Self {
1911 inner: Default::default(),
1912 seen_first: false,
1913 }
1914 }
1915
1916 fn insert(&mut self, count: usize, ix: TreeIndex) {
1917 if self.seen_first {
1918 self.inner.entry(count).or_default().push_back(ix);
1919 } else {
1920 self.seen_first = true;
1923 }
1924 }
1925
1926 fn is_populated(&self) -> bool {
1927 !self.inner.is_empty()
1928 }
1929
1930 fn find(&mut self, open_ix: TreeIndex, count: usize) -> Option<TreeIndex> {
1931 while let Some(ix) = self.inner.get_mut(&count)?.pop_front() {
1932 if ix > open_ix {
1933 return Some(ix);
1934 }
1935 }
1936 None
1937 }
1938
1939 fn clear(&mut self) {
1940 self.inner.clear();
1941 self.seen_first = false;
1942 }
1943}
1944
1945struct MathDelims {
1948 inner: HashMap<u8, VecDeque<(TreeIndex, bool, bool)>>,
1949}
1950
1951impl MathDelims {
1952 fn new() -> Self {
1953 Self {
1954 inner: Default::default(),
1955 }
1956 }
1957
1958 fn insert(
1959 &mut self,
1960 delim_is_display: bool,
1961 brace_context: u8,
1962 ix: TreeIndex,
1963 can_close: bool,
1964 ) {
1965 self.inner
1966 .entry(brace_context)
1967 .or_default()
1968 .push_back((ix, can_close, delim_is_display));
1969 }
1970
1971 fn is_populated(&self) -> bool {
1972 !self.inner.is_empty()
1973 }
1974
1975 fn find(
1976 &mut self,
1977 tree: &Tree<Item>,
1978 open_ix: TreeIndex,
1979 is_display: bool,
1980 brace_context: u8,
1981 ) -> Option<TreeIndex> {
1982 while let Some((ix, can_close, delim_is_display)) =
1983 self.inner.get_mut(&brace_context)?.pop_front()
1984 {
1985 if ix <= open_ix || (is_display && tree[open_ix].next == Some(ix)) {
1986 continue;
1987 }
1988 let can_close = can_close && tree[open_ix].item.end != tree[ix].item.start;
1989 if (!is_display && can_close) || (is_display && delim_is_display) {
1990 return Some(ix);
1991 }
1992 self.inner
1995 .get_mut(&brace_context)?
1996 .push_front((ix, can_close, delim_is_display));
1997 break;
1998 }
1999 None
2000 }
2001
2002 fn clear(&mut self) {
2003 self.inner.clear();
2004 }
2005}
2006
2007#[derive(Copy, Clone, PartialEq, Eq, Debug)]
2008pub(crate) struct LinkIndex(usize);
2009
2010#[derive(Copy, Clone, PartialEq, Eq, Debug)]
2011pub(crate) struct CowIndex(usize);
2012
2013#[derive(Copy, Clone, PartialEq, Eq, Debug)]
2014pub(crate) struct AlignmentIndex(usize);
2015
2016#[derive(Copy, Clone, PartialEq, Eq, Debug)]
2017pub(crate) struct HeadingIndex(NonZeroUsize);
2018
2019#[derive(Clone)]
2020pub(crate) struct Allocations<'a> {
2021 pub refdefs: RefDefs<'a>,
2022 pub footdefs: FootnoteDefs<'a>,
2023 links: Vec<(LinkType, CowStr<'a>, CowStr<'a>, CowStr<'a>)>,
2024 cows: Vec<CowStr<'a>>,
2025 alignments: Vec<Vec<Alignment>>,
2026 headings: Vec<HeadingAttributes<'a>>,
2027}
2028
2029#[derive(Clone)]
2031pub(crate) struct HeadingAttributes<'a> {
2032 pub id: Option<CowStr<'a>>,
2033 pub classes: Vec<CowStr<'a>>,
2034 pub attrs: Vec<(CowStr<'a>, Option<CowStr<'a>>)>,
2035}
2036
2037#[derive(Clone, Default, Debug)]
2039pub struct RefDefs<'input>(pub(crate) HashMap<LinkLabel<'input>, LinkDef<'input>>);
2040
2041#[derive(Clone, Default, Debug)]
2043pub struct FootnoteDefs<'input>(pub(crate) HashMap<FootnoteLabel<'input>, FootnoteDef>);
2044
2045impl<'input, 'b, 's> RefDefs<'input>
2046where
2047 's: 'b,
2048{
2049 pub fn get(&'s self, key: &'b str) -> Option<&'b LinkDef<'input>> {
2051 self.0.get(&UniCase::new(key.into()))
2052 }
2053
2054 pub fn iter(&'s self) -> impl Iterator<Item = (&'s str, &'s LinkDef<'input>)> {
2056 self.0.iter().map(|(k, v)| (k.as_ref(), v))
2057 }
2058}
2059
2060impl<'input, 'b, 's> FootnoteDefs<'input>
2061where
2062 's: 'b,
2063{
2064 pub fn contains(&'s self, key: &'b str) -> bool {
2066 self.0.contains_key(&UniCase::new(key.into()))
2067 }
2068 pub fn get_mut(&'s mut self, key: CowStr<'input>) -> Option<&'s mut FootnoteDef> {
2070 self.0.get_mut(&UniCase::new(key))
2071 }
2072}
2073
2074impl<'a> Allocations<'a> {
2075 pub fn new() -> Self {
2076 Self {
2077 refdefs: RefDefs::default(),
2078 footdefs: FootnoteDefs::default(),
2079 links: Vec::with_capacity(128),
2080 cows: Vec::new(),
2081 alignments: Vec::new(),
2082 headings: Vec::new(),
2083 }
2084 }
2085
2086 pub fn allocate_cow(&mut self, cow: CowStr<'a>) -> CowIndex {
2087 let ix = self.cows.len();
2088 self.cows.push(cow);
2089 CowIndex(ix)
2090 }
2091
2092 pub fn allocate_link(
2093 &mut self,
2094 ty: LinkType,
2095 url: CowStr<'a>,
2096 title: CowStr<'a>,
2097 id: CowStr<'a>,
2098 ) -> LinkIndex {
2099 let ix = self.links.len();
2100 self.links.push((ty, url, title, id));
2101 LinkIndex(ix)
2102 }
2103
2104 pub fn allocate_alignment(&mut self, alignment: Vec<Alignment>) -> AlignmentIndex {
2105 let ix = self.alignments.len();
2106 self.alignments.push(alignment);
2107 AlignmentIndex(ix)
2108 }
2109
2110 pub fn allocate_heading(&mut self, attrs: HeadingAttributes<'a>) -> HeadingIndex {
2111 let ix = self.headings.len();
2112 self.headings.push(attrs);
2113 let ix_nonzero = NonZeroUsize::new(ix.wrapping_add(1)).expect("too many headings");
2116 HeadingIndex(ix_nonzero)
2117 }
2118
2119 pub fn take_cow(&mut self, ix: CowIndex) -> CowStr<'a> {
2120 std::mem::replace(&mut self.cows[ix.0], "".into())
2121 }
2122
2123 pub fn take_link(&mut self, ix: LinkIndex) -> (LinkType, CowStr<'a>, CowStr<'a>, CowStr<'a>) {
2124 let default_link = (LinkType::ShortcutUnknown, "".into(), "".into(), "".into());
2125 std::mem::replace(&mut self.links[ix.0], default_link)
2126 }
2127
2128 pub fn take_alignment(&mut self, ix: AlignmentIndex) -> Vec<Alignment> {
2129 std::mem::take(&mut self.alignments[ix.0])
2130 }
2131}
2132
2133impl<'a> Index<CowIndex> for Allocations<'a> {
2134 type Output = CowStr<'a>;
2135
2136 fn index(&self, ix: CowIndex) -> &Self::Output {
2137 self.cows.index(ix.0)
2138 }
2139}
2140
2141impl<'a> Index<LinkIndex> for Allocations<'a> {
2142 type Output = (LinkType, CowStr<'a>, CowStr<'a>, CowStr<'a>);
2143
2144 fn index(&self, ix: LinkIndex) -> &Self::Output {
2145 self.links.index(ix.0)
2146 }
2147}
2148
2149impl<'a> Index<AlignmentIndex> for Allocations<'a> {
2150 type Output = Vec<Alignment>;
2151
2152 fn index(&self, ix: AlignmentIndex) -> &Self::Output {
2153 self.alignments.index(ix.0)
2154 }
2155}
2156
2157impl<'a> Index<HeadingIndex> for Allocations<'a> {
2158 type Output = HeadingAttributes<'a>;
2159
2160 fn index(&self, ix: HeadingIndex) -> &Self::Output {
2161 self.headings.index(ix.0.get() - 1)
2162 }
2163}
2164
2165#[derive(Clone, Default)]
2171pub(crate) struct HtmlScanGuard {
2172 pub cdata: usize,
2173 pub processing: usize,
2174 pub declaration: usize,
2175 pub comment: usize,
2176}
2177
2178pub trait BrokenLinkCallback<'input> {
2183 fn handle_broken_link(
2184 &mut self,
2185 link: BrokenLink<'input>,
2186 ) -> Option<(CowStr<'input>, CowStr<'input>)>;
2187}
2188
2189impl<'input, T> BrokenLinkCallback<'input> for T
2190where
2191 T: FnMut(BrokenLink<'input>) -> Option<(CowStr<'input>, CowStr<'input>)>,
2192{
2193 fn handle_broken_link(
2194 &mut self,
2195 link: BrokenLink<'input>,
2196 ) -> Option<(CowStr<'input>, CowStr<'input>)> {
2197 self(link)
2198 }
2199}
2200
2201impl<'input> BrokenLinkCallback<'input> for Box<dyn BrokenLinkCallback<'input>> {
2202 fn handle_broken_link(
2203 &mut self,
2204 link: BrokenLink<'input>,
2205 ) -> Option<(CowStr<'input>, CowStr<'input>)> {
2206 (**self).handle_broken_link(link)
2207 }
2208}
2209
2210#[derive(Debug)]
2212pub struct DefaultBrokenLinkCallback;
2213
2214impl<'input> BrokenLinkCallback<'input> for DefaultBrokenLinkCallback {
2215 fn handle_broken_link(
2216 &mut self,
2217 _link: BrokenLink<'input>,
2218 ) -> Option<(CowStr<'input>, CowStr<'input>)> {
2219 None
2220 }
2221}
2222
2223#[derive(Debug)]
2231pub struct OffsetIter<'a, F = DefaultBrokenLinkCallback> {
2232 inner: Parser<'a, F>,
2233}
2234
2235impl<'a, F: BrokenLinkCallback<'a>> OffsetIter<'a, F> {
2236 pub fn reference_definitions(&self) -> &RefDefs<'_> {
2238 self.inner.reference_definitions()
2239 }
2240}
2241
2242impl<'a, F: BrokenLinkCallback<'a>> Iterator for OffsetIter<'a, F> {
2243 type Item = (Event<'a>, Range<usize>);
2244
2245 fn next(&mut self) -> Option<Self::Item> {
2246 match self.inner.tree.cur() {
2247 None => {
2248 let ix = self.inner.tree.pop()?;
2249 let ix = if matches!(self.inner.tree[ix].item.body, ItemBody::TightParagraph) {
2250 self.inner.tree.next_sibling(ix);
2252 return self.next();
2253 } else {
2254 ix
2255 };
2256 let tag_end = body_to_tag_end(&self.inner.tree[ix].item.body);
2257 self.inner.tree.next_sibling(ix);
2258 let span = self.inner.tree[ix].item.start..self.inner.tree[ix].item.end;
2259 debug_assert!(span.start <= span.end);
2260 Some((Event::End(tag_end), span))
2261 }
2262 Some(cur_ix) => {
2263 let cur_ix =
2264 if matches!(self.inner.tree[cur_ix].item.body, ItemBody::TightParagraph) {
2265 self.inner.tree.push();
2267 self.inner.tree.cur().unwrap()
2268 } else {
2269 cur_ix
2270 };
2271 if self.inner.tree[cur_ix].item.body.is_maybe_inline() {
2272 self.inner.handle_inline();
2273 }
2274
2275 let item = self.inner.tree[cur_ix].item.clone();
2276 let event = item_to_event(&item, self.inner.text, &mut self.inner.allocs);
2277 if let Event::Start(..) = event {
2278 self.inner.tree.push();
2279 } else {
2280 self.inner.tree.next_sibling(cur_ix);
2281 }
2282 debug_assert!(item.start <= item.end);
2283 Some((event, item.start..item.end))
2284 }
2285 }
2286 }
2287}
2288
2289fn body_to_tag_end(body: &ItemBody) -> TagEnd {
2290 match *body {
2291 ItemBody::Paragraph => TagEnd::Paragraph,
2292 ItemBody::Emphasis => TagEnd::Emphasis,
2293 ItemBody::Superscript => TagEnd::Superscript,
2294 ItemBody::Subscript => TagEnd::Subscript,
2295 ItemBody::Strong => TagEnd::Strong,
2296 ItemBody::Strikethrough => TagEnd::Strikethrough,
2297 ItemBody::Link(..) => TagEnd::Link,
2298 ItemBody::Image(..) => TagEnd::Image,
2299 ItemBody::Heading(level, _) => TagEnd::Heading(level),
2300 ItemBody::IndentCodeBlock | ItemBody::FencedCodeBlock(..) => TagEnd::CodeBlock,
2301 ItemBody::BlockQuote(kind) => TagEnd::BlockQuote(kind),
2302 ItemBody::HtmlBlock => TagEnd::HtmlBlock,
2303 ItemBody::List(_, c, _) => {
2304 let is_ordered = c == b'.' || c == b')';
2305 TagEnd::List(is_ordered)
2306 }
2307 ItemBody::ListItem(_) => TagEnd::Item,
2308 ItemBody::TableHead => TagEnd::TableHead,
2309 ItemBody::TableCell => TagEnd::TableCell,
2310 ItemBody::TableRow => TagEnd::TableRow,
2311 ItemBody::Table(..) => TagEnd::Table,
2312 ItemBody::FootnoteDefinition(..) => TagEnd::FootnoteDefinition,
2313 ItemBody::MetadataBlock(kind) => TagEnd::MetadataBlock(kind),
2314 ItemBody::DefinitionList(_) => TagEnd::DefinitionList,
2315 ItemBody::DefinitionListTitle => TagEnd::DefinitionListTitle,
2316 ItemBody::DefinitionListDefinition(_) => TagEnd::DefinitionListDefinition,
2317 _ => panic!("unexpected item body {:?}", body),
2318 }
2319}
2320
2321fn item_to_event<'a>(item: &Item, text: &'a str, allocs: &mut Allocations<'a>) -> Event<'a> {
2322 let tag = match item.body {
2323 ItemBody::Text { .. } => return Event::Text(text[item.start..item.end].into()),
2324 ItemBody::Code(cow_ix) => return Event::Code(allocs.take_cow(cow_ix)),
2325 ItemBody::SynthesizeText(cow_ix) => return Event::Text(allocs.take_cow(cow_ix)),
2326 ItemBody::SynthesizeChar(c) => return Event::Text(c.into()),
2327 ItemBody::HtmlBlock => Tag::HtmlBlock,
2328 ItemBody::Html => return Event::Html(text[item.start..item.end].into()),
2329 ItemBody::InlineHtml => return Event::InlineHtml(text[item.start..item.end].into()),
2330 ItemBody::OwnedInlineHtml(cow_ix) => return Event::InlineHtml(allocs.take_cow(cow_ix)),
2331 ItemBody::SoftBreak => return Event::SoftBreak,
2332 ItemBody::HardBreak(_) => return Event::HardBreak,
2333 ItemBody::FootnoteReference(cow_ix) => {
2334 return Event::FootnoteReference(allocs.take_cow(cow_ix))
2335 }
2336 ItemBody::TaskListMarker(checked) => return Event::TaskListMarker(checked),
2337 ItemBody::Rule => return Event::Rule,
2338 ItemBody::Paragraph => Tag::Paragraph,
2339 ItemBody::Emphasis => Tag::Emphasis,
2340 ItemBody::Superscript => Tag::Superscript,
2341 ItemBody::Subscript => Tag::Subscript,
2342 ItemBody::Strong => Tag::Strong,
2343 ItemBody::Strikethrough => Tag::Strikethrough,
2344 ItemBody::Link(link_ix) => {
2345 let (link_type, dest_url, title, id) = allocs.take_link(link_ix);
2346 Tag::Link {
2347 link_type,
2348 dest_url,
2349 title,
2350 id,
2351 }
2352 }
2353 ItemBody::Image(link_ix) => {
2354 let (link_type, dest_url, title, id) = allocs.take_link(link_ix);
2355 Tag::Image {
2356 link_type,
2357 dest_url,
2358 title,
2359 id,
2360 }
2361 }
2362 ItemBody::Heading(level, Some(heading_ix)) => {
2363 let HeadingAttributes { id, classes, attrs } = allocs.index(heading_ix);
2364 Tag::Heading {
2365 level,
2366 id: id.clone(),
2367 classes: classes.clone(),
2368 attrs: attrs.clone(),
2369 }
2370 }
2371 ItemBody::Heading(level, None) => Tag::Heading {
2372 level,
2373 id: None,
2374 classes: Vec::new(),
2375 attrs: Vec::new(),
2376 },
2377 ItemBody::FencedCodeBlock(cow_ix) => {
2378 Tag::CodeBlock(CodeBlockKind::Fenced(allocs.take_cow(cow_ix)))
2379 }
2380 ItemBody::IndentCodeBlock => Tag::CodeBlock(CodeBlockKind::Indented),
2381 ItemBody::BlockQuote(kind) => Tag::BlockQuote(kind),
2382 ItemBody::List(_, c, listitem_start) => {
2383 if c == b'.' || c == b')' {
2384 Tag::List(Some(listitem_start))
2385 } else {
2386 Tag::List(None)
2387 }
2388 }
2389 ItemBody::ListItem(_) => Tag::Item,
2390 ItemBody::TableHead => Tag::TableHead,
2391 ItemBody::TableCell => Tag::TableCell,
2392 ItemBody::TableRow => Tag::TableRow,
2393 ItemBody::Table(alignment_ix) => Tag::Table(allocs.take_alignment(alignment_ix)),
2394 ItemBody::FootnoteDefinition(cow_ix) => Tag::FootnoteDefinition(allocs.take_cow(cow_ix)),
2395 ItemBody::MetadataBlock(kind) => Tag::MetadataBlock(kind),
2396 ItemBody::Math(cow_ix, is_display) => {
2397 return if is_display {
2398 Event::DisplayMath(allocs.take_cow(cow_ix))
2399 } else {
2400 Event::InlineMath(allocs.take_cow(cow_ix))
2401 }
2402 }
2403 ItemBody::DefinitionList(_) => Tag::DefinitionList,
2404 ItemBody::DefinitionListTitle => Tag::DefinitionListTitle,
2405 ItemBody::DefinitionListDefinition(_) => Tag::DefinitionListDefinition,
2406 _ => panic!("unexpected item body {:?}", item.body),
2407 };
2408
2409 Event::Start(tag)
2410}
2411
2412impl<'a, F: BrokenLinkCallback<'a>> Iterator for Parser<'a, F> {
2413 type Item = Event<'a>;
2414
2415 fn next(&mut self) -> Option<Event<'a>> {
2416 match self.tree.cur() {
2417 None => {
2418 let ix = self.tree.pop()?;
2419 let ix = if matches!(self.tree[ix].item.body, ItemBody::TightParagraph) {
2420 self.tree.next_sibling(ix);
2422 return self.next();
2423 } else {
2424 ix
2425 };
2426 let tag_end = body_to_tag_end(&self.tree[ix].item.body);
2427 self.tree.next_sibling(ix);
2428 Some(Event::End(tag_end))
2429 }
2430 Some(cur_ix) => {
2431 let maybe_cur_ix =
2432 if matches!(self.tree[cur_ix].item.body, ItemBody::TightParagraph) {
2433 self.tree.push();
2435 self.tree.cur()
2436 } else {
2437 Some(cur_ix)
2438 };
2439 if let Some(cur_ix) = maybe_cur_ix {
2440 if self.tree[cur_ix].item.body.is_maybe_inline() {
2441 self.handle_inline();
2442 }
2443
2444 let item = self.tree[cur_ix].item.clone();
2445 let event = item_to_event(&item, self.text, &mut self.allocs);
2446 if let Event::Start(ref _tag) = event {
2447 self.tree.push();
2448 } else {
2449 self.tree.next_sibling(cur_ix);
2450 }
2451 Some(event)
2452 } else {
2453 None
2454 }
2455 }
2456 }
2457 }
2458}
2459
2460impl<'a, F: BrokenLinkCallback<'a>> FusedIterator for Parser<'a, F> {}
2461
2462#[cfg(test)]
2463mod test {
2464 use super::*;
2465 use crate::tree::Node;
2466
2467 fn parser_with_extensions(text: &str) -> Parser<'_> {
2470 let mut opts = Options::empty();
2471 opts.insert(Options::ENABLE_TABLES);
2472 opts.insert(Options::ENABLE_FOOTNOTES);
2473 opts.insert(Options::ENABLE_STRIKETHROUGH);
2474 opts.insert(Options::ENABLE_SUPERSCRIPT);
2475 opts.insert(Options::ENABLE_SUBSCRIPT);
2476 opts.insert(Options::ENABLE_TASKLISTS);
2477
2478 Parser::new_ext(text, opts)
2479 }
2480
2481 #[test]
2482 #[cfg(target_pointer_width = "64")]
2483 fn node_size() {
2484 let node_size = std::mem::size_of::<Node<Item>>();
2485 assert_eq!(48, node_size);
2486 }
2487
2488 #[test]
2489 #[cfg(target_pointer_width = "64")]
2490 fn body_size() {
2491 let body_size = std::mem::size_of::<ItemBody>();
2492 assert_eq!(16, body_size);
2493 }
2494
2495 #[test]
2496 fn single_open_fish_bracket() {
2497 assert_eq!(3, Parser::new("<").count());
2499 }
2500
2501 #[test]
2502 fn lone_hashtag() {
2503 assert_eq!(2, Parser::new("#").count());
2505 }
2506
2507 #[test]
2508 fn lots_of_backslashes() {
2509 Parser::new("\\\\\r\r").count();
2511 Parser::new("\\\r\r\\.\\\\\r\r\\.\\").count();
2512 }
2513
2514 #[test]
2515 fn issue_1095() {
2516 let s = "- [n]:Z\r\n\t\t";
2517 let parser = Parser::new_ext(s, Options::all());
2518 for _ in parser {}
2519 }
2520
2521 #[test]
2522 fn issue_1084() {
2523 let markdown_input = "* [ ] ~![=?\\*\x0c\x00\x00 \x0d* [ 1=1\x00\x0d<!]:[=?\\\x0d\x0c\n* [ ] \x0d\x0c% ";
2524
2525 let mut options = Options::empty();
2526 options.insert(Options::ENABLE_TASKLISTS);
2527 options.insert(Options::ENABLE_STRIKETHROUGH);
2528
2529 let parser = Parser::new_ext(markdown_input, options);
2530 for _ in parser {}
2531 }
2532
2533 #[test]
2534 fn issue_1030() {
2535 let mut opts = Options::empty();
2536 opts.insert(Options::ENABLE_WIKILINKS);
2537
2538 let parser = Parser::new_ext("For a new ferrari, [[Wikientry|click here]]!", opts);
2539
2540 let offsets = parser
2541 .into_offset_iter()
2542 .map(|(_ev, range)| range)
2543 .collect::<Vec<_>>();
2544 let expected_offsets = vec![
2545 (0..44), (0..19), (19..43), (31..41), (19..43), (43..44), (0..44), ];
2553 assert_eq!(offsets, expected_offsets);
2554 }
2555
2556 #[test]
2557 fn issue_320() {
2558 parser_with_extensions(":\r\t> |\r:\r\t> |\r").count();
2560 }
2561
2562 #[test]
2563 fn issue_319() {
2564 parser_with_extensions("|\r-]([^|\r-]([^").count();
2566 parser_with_extensions("|\r\r=][^|\r\r=][^car").count();
2567 }
2568
2569 #[test]
2570 fn issue_303() {
2571 parser_with_extensions("[^\r\ra]").count();
2573 parser_with_extensions("\r\r]Z[^\x00\r\r]Z[^\x00").count();
2574 }
2575
2576 #[test]
2577 fn issue_313() {
2578 parser_with_extensions("*]0[^\r\r*]0[^").count();
2580 parser_with_extensions("[^\r> `][^\r> `][^\r> `][").count();
2581 }
2582
2583 #[test]
2584 fn issue_311() {
2585 parser_with_extensions("\\\u{0d}-\u{09}\\\u{0d}-\u{09}").count();
2587 }
2588
2589 #[test]
2590 fn issue_283() {
2591 let input = std::str::from_utf8(b"\xf0\x9b\xb2\x9f<td:^\xf0\x9b\xb2\x9f").unwrap();
2592 parser_with_extensions(input).count();
2594 }
2595
2596 #[test]
2597 fn issue_289() {
2598 parser_with_extensions("> - \\\n> - ").count();
2600 parser_with_extensions("- \n\n").count();
2601 }
2602
2603 #[test]
2604 fn issue_306() {
2605 parser_with_extensions("*\r_<__*\r_<__*\r_<__*\r_<__").count();
2607 }
2608
2609 #[test]
2610 fn issue_305() {
2611 parser_with_extensions("_6**6*_*").count();
2613 }
2614
2615 #[test]
2616 fn another_emphasis_panic() {
2617 parser_with_extensions("*__#_#__*").count();
2618 }
2619
2620 #[test]
2621 fn offset_iter() {
2622 let event_offsets: Vec<_> = Parser::new("*hello* world")
2623 .into_offset_iter()
2624 .map(|(_ev, range)| range)
2625 .collect();
2626 let expected_offsets = vec![(0..13), (0..7), (1..6), (0..7), (7..13), (0..13)];
2627 assert_eq!(expected_offsets, event_offsets);
2628 }
2629
2630 #[test]
2631 fn issue_1142_deflist_offset_is_char_boundary() {
2632 let doc = "%\n:\u{301}\n\t:";
2636 let mut opts = Options::empty();
2637 opts.insert(Options::ENABLE_DEFINITION_LIST);
2638 for (_event, range) in Parser::new_ext(doc, opts).into_offset_iter() {
2639 let _ = &doc[range];
2640 }
2641 }
2642
2643 #[test]
2644 fn reference_link_offsets() {
2645 let range =
2646 Parser::new("# H1\n[testing][Some reference]\n\n[Some reference]: https://github.com")
2647 .into_offset_iter()
2648 .filter_map(|(ev, range)| match ev {
2649 Event::Start(
2650 Tag::Link {
2651 link_type: LinkType::Reference,
2652 ..
2653 },
2654 ..,
2655 ) => Some(range),
2656 _ => None,
2657 })
2658 .next()
2659 .unwrap();
2660 assert_eq!(5..30, range);
2661 }
2662
2663 #[test]
2664 fn footnote_offsets() {
2665 let range = parser_with_extensions("Testing this[^1] out.\n\n[^1]: Footnote.")
2666 .into_offset_iter()
2667 .filter_map(|(ev, range)| match ev {
2668 Event::FootnoteReference(..) => Some(range),
2669 _ => None,
2670 })
2671 .next()
2672 .unwrap();
2673 assert_eq!(12..16, range);
2674 }
2675
2676 #[test]
2677 fn footnote_offsets_exclamation() {
2678 let mut immediately_before_footnote = None;
2679 let range = parser_with_extensions("Testing this![^1] out.\n\n[^1]: Footnote.")
2680 .into_offset_iter()
2681 .filter_map(|(ev, range)| match ev {
2682 Event::FootnoteReference(..) => Some(range),
2683 _ => {
2684 immediately_before_footnote = Some((ev, range));
2685 None
2686 }
2687 })
2688 .next()
2689 .unwrap();
2690 assert_eq!(13..17, range);
2691 if let (Event::Text(exclamation), range_exclamation) =
2692 immediately_before_footnote.as_ref().unwrap()
2693 {
2694 assert_eq!("!", &exclamation[..]);
2695 assert_eq!(&(12..13), range_exclamation);
2696 } else {
2697 panic!("what came first, then? {immediately_before_footnote:?}");
2698 }
2699 }
2700
2701 #[test]
2702 fn table_offset() {
2703 let markdown = "a\n\nTesting|This|Outtt\n--|:--:|--:\nSome Data|Other data|asdf";
2704 let event_offset = parser_with_extensions(markdown)
2705 .into_offset_iter()
2706 .map(|(_ev, range)| range)
2707 .nth(3)
2708 .unwrap();
2709 let expected_offset = 3..59;
2710 assert_eq!(expected_offset, event_offset);
2711 }
2712
2713 #[test]
2714 fn table_cell_span() {
2715 let markdown = "a|b|c\n--|--|--\na| |c";
2716 let event_offset = parser_with_extensions(markdown)
2717 .into_offset_iter()
2718 .filter_map(|(ev, span)| match ev {
2719 Event::Start(Tag::TableCell) => Some(span),
2720 _ => None,
2721 })
2722 .nth(4)
2723 .unwrap();
2724 let expected_offset_start = "a|b|c\n--|--|--\na|".len();
2725 assert_eq!(
2726 expected_offset_start..(expected_offset_start + 2),
2727 event_offset
2728 );
2729 }
2730
2731 #[test]
2732 fn offset_iter_issue_378() {
2733 let event_offsets: Vec<_> = Parser::new("a [b](c) d")
2734 .into_offset_iter()
2735 .map(|(_ev, range)| range)
2736 .collect();
2737 let expected_offsets = vec![(0..10), (0..2), (2..8), (3..4), (2..8), (8..10), (0..10)];
2738 assert_eq!(expected_offsets, event_offsets);
2739 }
2740
2741 #[test]
2742 fn offset_iter_issue_404() {
2743 let event_offsets: Vec<_> = Parser::new("###\n")
2744 .into_offset_iter()
2745 .map(|(_ev, range)| range)
2746 .collect();
2747 let expected_offsets = vec![(0..4), (0..4)];
2748 assert_eq!(expected_offsets, event_offsets);
2749 }
2750
2751 #[cfg(feature = "html")]
2753 #[test]
2754 fn link_def_at_eof() {
2755 let test_str = "[My site][world]\n\n[world]: https://vincentprouillet.com";
2756 let expected = "<p><a href=\"https://vincentprouillet.com\">My site</a></p>\n";
2757
2758 let mut buf = String::new();
2759 crate::html::push_html(&mut buf, Parser::new(test_str));
2760 assert_eq!(expected, buf);
2761 }
2762
2763 #[cfg(feature = "html")]
2764 #[test]
2765 fn no_footnote_refs_without_option() {
2766 let test_str = "a [^a]\n\n[^a]: yolo";
2767 let expected = "<p>a <a href=\"yolo\">^a</a></p>\n";
2768
2769 let mut buf = String::new();
2770 crate::html::push_html(&mut buf, Parser::new(test_str));
2771 assert_eq!(expected, buf);
2772 }
2773
2774 #[cfg(feature = "html")]
2775 #[test]
2776 fn ref_def_at_eof() {
2777 let test_str = "[test]:\\";
2778 let expected = "";
2779
2780 let mut buf = String::new();
2781 crate::html::push_html(&mut buf, Parser::new(test_str));
2782 assert_eq!(expected, buf);
2783 }
2784
2785 #[cfg(feature = "html")]
2786 #[test]
2787 fn ref_def_cr_lf() {
2788 let test_str = "[a]: /u\r\n\n[a]";
2789 let expected = "<p><a href=\"/u\">a</a></p>\n";
2790
2791 let mut buf = String::new();
2792 crate::html::push_html(&mut buf, Parser::new(test_str));
2793 assert_eq!(expected, buf);
2794 }
2795
2796 #[cfg(feature = "html")]
2797 #[test]
2798 fn no_dest_refdef() {
2799 let test_str = "[a]:";
2800 let expected = "<p>[a]:</p>\n";
2801
2802 let mut buf = String::new();
2803 crate::html::push_html(&mut buf, Parser::new(test_str));
2804 assert_eq!(expected, buf);
2805 }
2806
2807 #[test]
2808 fn broken_links_called_only_once() {
2809 for &(markdown, expected) in &[
2810 ("See also [`g()`][crate::g].", 1),
2811 ("See also [`g()`][crate::g][].", 1),
2812 ("[brokenlink1] some other node [brokenlink2]", 2),
2813 ] {
2814 let mut times_called = 0;
2815 let callback = &mut |_broken_link: BrokenLink| {
2816 times_called += 1;
2817 None
2818 };
2819 let parser =
2820 Parser::new_with_broken_link_callback(markdown, Options::empty(), Some(callback));
2821 for _ in parser {}
2822 assert_eq!(times_called, expected);
2823 }
2824 }
2825
2826 #[test]
2827 fn simple_broken_link_callback() {
2828 let test_str = "This is a link w/o def: [hello][world]";
2829 let mut callback = |broken_link: BrokenLink| {
2830 assert_eq!("world", broken_link.reference.as_ref());
2831 assert_eq!(&test_str[broken_link.span], "[hello][world]");
2832 let url = "YOLO".into();
2833 let title = "SWAG".to_owned().into();
2834 Some((url, title))
2835 };
2836 let parser =
2837 Parser::new_with_broken_link_callback(test_str, Options::empty(), Some(&mut callback));
2838 let mut link_tag_count = 0;
2839 for (typ, url, title, id) in parser.filter_map(|event| match event {
2840 Event::Start(tag) => match tag {
2841 Tag::Link {
2842 link_type,
2843 dest_url,
2844 title,
2845 id,
2846 } => Some((link_type, dest_url, title, id)),
2847 _ => None,
2848 },
2849 _ => None,
2850 }) {
2851 link_tag_count += 1;
2852 assert_eq!(typ, LinkType::ReferenceUnknown);
2853 assert_eq!(url.as_ref(), "YOLO");
2854 assert_eq!(title.as_ref(), "SWAG");
2855 assert_eq!(id.as_ref(), "world");
2856 }
2857 assert!(link_tag_count > 0);
2858 }
2859
2860 #[test]
2861 fn code_block_kind_check_fenced() {
2862 let parser = Parser::new("hello\n```test\ntadam\n```");
2863 let mut found = 0;
2864 for (ev, _range) in parser.into_offset_iter() {
2865 match ev {
2866 Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(syntax))) => {
2867 assert_eq!(syntax.as_ref(), "test");
2868 found += 1;
2869 }
2870 _ => {}
2871 }
2872 }
2873 assert_eq!(found, 1);
2874 }
2875
2876 #[test]
2877 fn code_block_kind_check_indented() {
2878 let parser = Parser::new("hello\n\n ```test\n tadam\nhello");
2879 let mut found = 0;
2880 for (ev, _range) in parser.into_offset_iter() {
2881 match ev {
2882 Event::Start(Tag::CodeBlock(CodeBlockKind::Indented)) => {
2883 found += 1;
2884 }
2885 _ => {}
2886 }
2887 }
2888 assert_eq!(found, 1);
2889 }
2890
2891 #[test]
2892 fn ref_defs() {
2893 let input = r###"[a B c]: http://example.com
2894[another]: https://google.com
2895
2896text
2897
2898[final ONE]: http://wikipedia.org
2899"###;
2900 let mut parser = Parser::new(input);
2901
2902 assert!(parser.reference_definitions().get("a b c").is_some());
2903 assert!(parser.reference_definitions().get("nope").is_none());
2904
2905 if let Some(_event) = parser.next() {
2906 let s = "final one".to_owned();
2908 let link_def = parser.reference_definitions().get(&s).unwrap();
2909 let span = &input[link_def.span.clone()];
2910 assert_eq!(span, "[final ONE]: http://wikipedia.org");
2911 }
2912 }
2913
2914 #[test]
2915 fn common_lifetime_patterns_allowed<'b>() {
2916 let temporary_str = String::from("xyz");
2917
2918 let mut closure = |link: BrokenLink<'b>| Some(("#".into(), link.reference));
2922
2923 fn function(link: BrokenLink<'_>) -> Option<(CowStr<'_>, CowStr<'_>)> {
2924 Some(("#".into(), link.reference))
2925 }
2926
2927 for _ in Parser::new_with_broken_link_callback(
2928 "static lifetime",
2929 Options::empty(),
2930 Some(&mut closure),
2931 ) {}
2932 for _ in Parser::new_with_broken_link_callback(
2941 "static lifetime",
2942 Options::empty(),
2943 Some(&mut function),
2944 ) {}
2945 for _ in Parser::new_with_broken_link_callback(
2946 &temporary_str,
2947 Options::empty(),
2948 Some(&mut function),
2949 ) {}
2950 }
2951
2952 #[test]
2953 fn inline_html_inside_blockquote() {
2954 let input = "> <foo\n> bar>";
2956 let events: Vec<_> = Parser::new(input).collect();
2957 let expected = [
2958 Event::Start(Tag::BlockQuote(None)),
2959 Event::Start(Tag::Paragraph),
2960 Event::InlineHtml(CowStr::Boxed("<foo\nbar>".to_string().into())),
2961 Event::End(TagEnd::Paragraph),
2962 Event::End(TagEnd::BlockQuote(None)),
2963 ];
2964 assert_eq!(&events, &expected);
2965 }
2966
2967 #[test]
2968 fn wikilink_has_pothole() {
2969 let input = "[[foo]] [[bar|baz]]";
2970 let events: Vec<_> = Parser::new_ext(input, Options::ENABLE_WIKILINKS).collect();
2971 let expected = [
2972 Event::Start(Tag::Paragraph),
2973 Event::Start(Tag::Link {
2974 link_type: LinkType::WikiLink { has_pothole: false },
2975 dest_url: CowStr::Borrowed("foo"),
2976 title: CowStr::Borrowed(""),
2977 id: CowStr::Borrowed(""),
2978 }),
2979 Event::Text(CowStr::Borrowed("foo")),
2980 Event::End(TagEnd::Link),
2981 Event::Text(CowStr::Borrowed(" ")),
2982 Event::Start(Tag::Link {
2983 link_type: LinkType::WikiLink { has_pothole: true },
2984 dest_url: CowStr::Borrowed("bar"),
2985 title: CowStr::Borrowed(""),
2986 id: CowStr::Borrowed(""),
2987 }),
2988 Event::Text(CowStr::Borrowed("baz")),
2989 Event::End(TagEnd::Link),
2990 Event::End(TagEnd::Paragraph),
2991 ];
2992 assert_eq!(&events, &expected);
2993 }
2994
2995 #[test]
2996 fn wikilink_no_event_blowup() {
2997 let one = "[[[[^(\n|]]]]=]]]]]]]]\n".repeat(1);
3005 let eight = "[[[[^(\n|]]]]=]]]]]]]]\n".repeat(8);
3006
3007 let n1 = Parser::new_ext(&one, Options::ENABLE_WIKILINKS).count();
3008 let n8 = Parser::new_ext(&eight, Options::ENABLE_WIKILINKS).count();
3009
3010 assert!(
3011 n8 <= n1 * 20,
3012 "Event count should scale linearly with input length; \
3013 got {n1} events for 1× and {n8} events for 8× ({}× ratio, expected ≤20×)",
3014 n8 / n1.max(1)
3015 );
3016 }
3017
3018 #[test]
3019 fn issue_1154() {
3020 let mut tree = Tree::with_capacity(4);
3023 let mut stack = InlineStack::default();
3024 for _ in 0..3 {
3025 let start = tree.append(Item {
3026 start: 0,
3027 end: 1,
3028 body: ItemBody::Text {
3029 backslash_escaped: false,
3030 },
3031 });
3032 stack.push(InlineEl {
3033 start,
3034 count: 1,
3035 run_length: 1,
3036 c: b'*',
3037 both: false,
3038 });
3039 }
3040 assert!(stack.find_match(&mut tree, b'_', 1, false).is_none());
3041 assert_eq!(3, stack.get_lowerbound(b'_', 1, false));
3042 assert_eq!(3, stack.get_lowerbound(b'_', 1, true));
3043 }
3044}