1use crate::options::ParserOptions;
2use crate::syntax::{SyntaxKind, SyntaxNode};
3use rowan::GreenNodeBuilder;
4
5use super::block_dispatcher::{
6 BlockContext, BlockDetectionResult, BlockEffect, BlockParserRegistry, BlockQuotePrepared,
7 PreparedBlockMatch,
8};
9use super::blocks::blockquotes;
10use super::blocks::code_blocks;
11use super::blocks::definition_lists;
12use super::blocks::fenced_divs;
13use super::blocks::headings::{
14 emit_atx_heading, emit_setext_heading, emit_setext_heading_body, try_parse_atx_heading,
15 try_parse_setext_heading,
16};
17use super::blocks::horizontal_rules::try_parse_horizontal_rule;
18use super::blocks::line_blocks;
19use super::blocks::lists;
20use super::blocks::paragraphs;
21use super::blocks::raw_blocks::{extract_environment_name, is_inline_math_environment};
22use super::utils::container_stack;
23use super::utils::helpers::{is_blank_line, split_lines_inclusive, strip_newline};
24use super::utils::inline_emission;
25use super::utils::marker_utils;
26use super::utils::text_buffer;
27
28use super::blocks::blockquotes::strip_n_blockquote_markers;
29use super::utils::continuation::ContinuationPolicy;
30use container_stack::{Container, ContainerStack, byte_index_at_column, leading_indent};
31use definition_lists::{emit_definition_marker, emit_term};
32use line_blocks::{parse_line_block, try_parse_line_block_start};
33use lists::{
34 ListItemEmissionInput, ListMarker, is_content_nested_bullet_marker, start_nested_list,
35 try_parse_list_marker,
36};
37use marker_utils::{count_blockquote_markers, parse_blockquote_marker_info};
38use text_buffer::TextBuffer;
39
40const GITHUB_ALERT_MARKERS: [&str; 5] = [
41 "[!TIP]",
42 "[!WARNING]",
43 "[!IMPORTANT]",
44 "[!CAUTION]",
45 "[!NOTE]",
46];
47
48pub struct Parser<'a> {
49 lines: Vec<&'a str>,
50 pos: usize,
51 builder: GreenNodeBuilder<'static>,
52 containers: ContainerStack,
53 config: &'a ParserOptions,
54 block_registry: BlockParserRegistry,
55 after_metadata_block: bool,
59}
60
61impl<'a> Parser<'a> {
62 pub fn new(input: &'a str, config: &'a ParserOptions) -> Self {
63 let lines = split_lines_inclusive(input);
65 Self {
66 lines,
67 pos: 0,
68 builder: GreenNodeBuilder::new(),
69 containers: ContainerStack::new(),
70 config,
71 block_registry: BlockParserRegistry::new(),
72 after_metadata_block: false,
73 }
74 }
75
76 pub fn parse(mut self) -> SyntaxNode {
77 self.parse_document_stack();
78
79 SyntaxNode::new_root(self.builder.finish())
80 }
81
82 fn close_lists_above_indent(&mut self, indent_cols: usize) {
93 while let Some(Container::ListItem { content_col, .. }) = self.containers.last() {
94 if indent_cols >= *content_col {
95 break;
96 }
97 self.close_containers_to(self.containers.depth() - 1);
98 if matches!(self.containers.last(), Some(Container::List { .. })) {
99 self.close_containers_to(self.containers.depth() - 1);
100 }
101 }
102 }
103
104 fn close_containers_to(&mut self, keep: usize) {
107 while self.containers.depth() > keep {
109 match self.containers.stack.last() {
110 Some(Container::ListItem { buffer, .. }) if !buffer.is_empty() => {
112 let buffer_clone = buffer.clone();
114
115 log::trace!(
116 "Closing ListItem with buffer (is_empty={}, segment_count={})",
117 buffer_clone.is_empty(),
118 buffer_clone.segment_count()
119 );
120
121 let parent_list_is_loose = self
125 .containers
126 .stack
127 .iter()
128 .rev()
129 .find_map(|c| match c {
130 Container::List {
131 has_blank_between_items,
132 ..
133 } => Some(*has_blank_between_items),
134 _ => None,
135 })
136 .unwrap_or(false);
137
138 let use_paragraph =
139 parent_list_is_loose || buffer_clone.has_blank_lines_between_content();
140
141 log::trace!(
142 "Emitting ListItem buffer: use_paragraph={} (parent_list_is_loose={}, item_has_blanks={})",
143 use_paragraph,
144 parent_list_is_loose,
145 buffer_clone.has_blank_lines_between_content()
146 );
147
148 self.containers.stack.pop();
150 buffer_clone.emit_as_block(&mut self.builder, use_paragraph, self.config);
152 self.builder.finish_node(); }
154 Some(Container::ListItem { .. }) => {
156 log::trace!("Closing empty ListItem (no buffer content)");
157 self.containers.stack.pop();
159 self.builder.finish_node();
160 }
161 Some(Container::Paragraph {
163 buffer,
164 start_checkpoint,
165 ..
166 }) if !buffer.is_empty() => {
167 let buffer_clone = buffer.clone();
169 let checkpoint = *start_checkpoint;
170 self.containers.stack.pop();
172 self.builder
174 .start_node_at(checkpoint, SyntaxKind::PARAGRAPH.into());
175 buffer_clone.emit_with_inlines(&mut self.builder, self.config);
176 self.builder.finish_node();
177 }
178 Some(Container::Paragraph {
180 start_checkpoint, ..
181 }) => {
182 let checkpoint = *start_checkpoint;
183 self.containers.stack.pop();
185 self.builder
186 .start_node_at(checkpoint, SyntaxKind::PARAGRAPH.into());
187 self.builder.finish_node();
188 }
189 Some(Container::Definition {
191 plain_open: true,
192 plain_buffer,
193 ..
194 }) if !plain_buffer.is_empty() => {
195 let text = plain_buffer.get_accumulated_text();
196 emit_definition_plain_or_heading(&mut self.builder, &text, self.config);
197
198 if let Some(Container::Definition {
200 plain_open,
201 plain_buffer,
202 ..
203 }) = self.containers.stack.last_mut()
204 {
205 plain_buffer.clear();
206 *plain_open = false;
207 }
208
209 self.containers.stack.pop();
211 self.builder.finish_node();
212 }
213 Some(Container::Definition {
215 plain_open: true, ..
216 }) => {
217 if let Some(Container::Definition {
219 plain_open,
220 plain_buffer,
221 ..
222 }) = self.containers.stack.last_mut()
223 {
224 plain_buffer.clear();
225 *plain_open = false;
226 }
227
228 self.containers.stack.pop();
230 self.builder.finish_node();
231 }
232 _ => {
234 self.containers.stack.pop();
235 self.builder.finish_node();
236 }
237 }
238 }
239 }
240
241 fn emit_buffered_plain_if_needed(&mut self) {
244 if let Some(Container::Definition {
246 plain_open: true,
247 plain_buffer,
248 ..
249 }) = self.containers.stack.last()
250 && !plain_buffer.is_empty()
251 {
252 let text = plain_buffer.get_accumulated_text();
253 emit_definition_plain_or_heading(&mut self.builder, &text, self.config);
254 }
255
256 if let Some(Container::Definition {
258 plain_open,
259 plain_buffer,
260 ..
261 }) = self.containers.stack.last_mut()
262 && *plain_open
263 {
264 plain_buffer.clear();
265 *plain_open = false;
266 }
267 }
268
269 fn close_blockquotes_to_depth(&mut self, target_depth: usize) {
274 let mut current = self.current_blockquote_depth();
275 while current > target_depth {
276 while !matches!(self.containers.last(), Some(Container::BlockQuote { .. })) {
277 if self.containers.depth() == 0 {
278 break;
279 }
280 self.close_containers_to(self.containers.depth() - 1);
281 }
282 if matches!(self.containers.last(), Some(Container::BlockQuote { .. })) {
283 self.close_containers_to(self.containers.depth() - 1);
284 current -= 1;
285 } else {
286 break;
287 }
288 }
289 }
290
291 fn active_alert_blockquote_depth(&self) -> Option<usize> {
292 self.containers.stack.iter().rev().find_map(|c| match c {
293 Container::Alert { blockquote_depth } => Some(*blockquote_depth),
294 _ => None,
295 })
296 }
297
298 fn in_active_alert(&self) -> bool {
299 self.active_alert_blockquote_depth().is_some()
300 }
301
302 fn previous_block_requires_blank_before_heading(&self) -> bool {
303 matches!(
304 self.containers.last(),
305 Some(Container::Paragraph { .. })
306 | Some(Container::ListItem { .. })
307 | Some(Container::Definition { .. })
308 | Some(Container::DefinitionItem { .. })
309 | Some(Container::FootnoteDefinition { .. })
310 )
311 }
312
313 fn alert_marker_from_content(content: &str) -> Option<&'static str> {
314 let (without_newline, _) = strip_newline(content);
315 let trimmed = without_newline.trim();
316 GITHUB_ALERT_MARKERS
317 .into_iter()
318 .find(|marker| *marker == trimmed)
319 }
320
321 fn emit_list_item_buffer_if_needed(&mut self) {
324 if let Some(Container::ListItem { buffer, .. }) = self.containers.stack.last_mut()
325 && !buffer.is_empty()
326 {
327 let buffer_clone = buffer.clone();
328 buffer.clear();
329 let use_paragraph = buffer_clone.has_blank_lines_between_content();
330 buffer_clone.emit_as_block(&mut self.builder, use_paragraph, self.config);
331 }
332 }
333
334 fn maybe_open_fenced_code_in_new_list_item(&mut self) {
346 let Some(Container::ListItem {
347 content_col,
348 buffer,
349 ..
350 }) = self.containers.stack.last()
351 else {
352 return;
353 };
354 let content_col = *content_col;
355 let Some(text) = buffer.first_text() else {
356 return;
357 };
358 if buffer.segment_count() != 1 {
359 return;
360 }
361 let text_owned = text.to_string();
362 let Some(fence) = code_blocks::try_parse_fence_open(&text_owned) else {
363 return;
364 };
365 let common_mark_dialect = self.config.dialect == crate::options::Dialect::CommonMark;
366 let has_info = !fence.info_string.trim().is_empty();
367 let bq_depth = self.current_blockquote_depth();
368 let has_matching_closer = self.has_matching_fence_closer(&fence, bq_depth, content_col);
369 if !(has_info || has_matching_closer || common_mark_dialect) {
370 return;
371 }
372 if (fence.fence_char == '`' && !self.config.extensions.backtick_code_blocks)
374 || (fence.fence_char == '~' && !self.config.extensions.fenced_code_blocks)
375 {
376 return;
377 }
378 if let Some(Container::ListItem { buffer, .. }) = self.containers.stack.last_mut() {
379 buffer.clear();
380 }
381 let new_pos = code_blocks::parse_fenced_code_block(
382 &mut self.builder,
383 &self.lines,
384 self.pos,
385 fence,
386 bq_depth,
387 content_col,
388 Some(&text_owned),
389 );
390 self.pos = new_pos.saturating_sub(1);
394 }
395
396 fn maybe_open_indented_code_in_new_list_item(&mut self) {
407 let Some(Container::ListItem {
408 content_col,
409 buffer,
410 marker_only,
411 virtual_marker_space,
412 }) = self.containers.stack.last()
413 else {
414 return;
415 };
416 if *marker_only {
417 return;
418 }
419 if buffer.segment_count() != 1 {
420 return;
421 }
422 let Some(text) = buffer.first_text() else {
423 return;
424 };
425 let content_col = *content_col;
426 let virtual_marker_space = *virtual_marker_space;
427 let text_owned = text.to_string();
428
429 let mut iter = text_owned.split_inclusive('\n');
431 let line_with_nl = iter.next().unwrap_or("").to_string();
432 if iter.next().is_some() {
433 return;
434 }
435
436 let line_no_nl = line_with_nl
437 .strip_suffix("\r\n")
438 .or_else(|| line_with_nl.strip_suffix('\n'))
439 .unwrap_or(&line_with_nl);
440 let nl_suffix = &line_with_nl[line_no_nl.len()..];
441
442 let buffer_start_col = if virtual_marker_space {
443 content_col.saturating_sub(1)
444 } else {
445 content_col
446 };
447
448 let target = content_col + 4;
449 let (cols_walked, ws_bytes) =
450 super::utils::container_stack::leading_indent_from(line_no_nl, buffer_start_col);
451
452 if buffer_start_col + cols_walked < target {
453 return;
454 }
455 if ws_bytes >= line_no_nl.len() {
456 return;
457 }
458
459 if let Some(Container::ListItem { buffer, .. }) = self.containers.stack.last_mut() {
460 buffer.clear();
461 }
462
463 self.builder.start_node(SyntaxKind::CODE_BLOCK.into());
464 self.builder.start_node(SyntaxKind::CODE_CONTENT.into());
465 if ws_bytes > 0 {
466 self.builder
467 .token(SyntaxKind::WHITESPACE.into(), &line_no_nl[..ws_bytes]);
468 }
469 let rest = &line_no_nl[ws_bytes..];
470 if !rest.is_empty() {
471 self.builder.token(SyntaxKind::TEXT.into(), rest);
472 }
473 if !nl_suffix.is_empty() {
474 self.builder.token(SyntaxKind::NEWLINE.into(), nl_suffix);
475 }
476 self.builder.finish_node();
477 self.builder.finish_node();
478 }
479
480 fn has_matching_fence_closer(
481 &self,
482 fence: &code_blocks::FenceInfo,
483 bq_depth: usize,
484 content_col: usize,
485 ) -> bool {
486 for raw_line in self.lines.iter().skip(self.pos + 1) {
487 let (line_bq_depth, inner) = count_blockquote_markers(raw_line);
488 if line_bq_depth < bq_depth {
489 break;
490 }
491 let candidate = if content_col > 0 && !inner.is_empty() {
492 let idx = byte_index_at_column(inner, content_col);
493 if idx <= inner.len() {
494 &inner[idx..]
495 } else {
496 inner
497 }
498 } else {
499 inner
500 };
501 if code_blocks::is_closing_fence(candidate, fence) {
502 return true;
503 }
504 }
505 false
506 }
507
508 fn is_paragraph_open(&self) -> bool {
510 matches!(self.containers.last(), Some(Container::Paragraph { .. }))
511 }
512
513 fn emit_setext_heading_folding_paragraph(
521 &mut self,
522 text_line: &str,
523 underline_line: &str,
524 level: usize,
525 ) {
526 let (buffered_text, checkpoint) = match self.containers.stack.last() {
527 Some(Container::Paragraph {
528 buffer,
529 start_checkpoint,
530 ..
531 }) => (buffer.get_text_for_parsing(), Some(*start_checkpoint)),
532 _ => (String::new(), None),
533 };
534
535 if checkpoint.is_some() {
536 self.containers.stack.pop();
537 }
538
539 let combined_text = if buffered_text.is_empty() {
540 text_line.to_string()
541 } else {
542 format!("{}{}", buffered_text, text_line)
543 };
544
545 let cp = checkpoint.expect(
546 "emit_setext_heading_folding_paragraph requires an open paragraph; \
547 single-line setext should go through the regular dispatcher path",
548 );
549 self.builder.start_node_at(cp, SyntaxKind::HEADING.into());
550 emit_setext_heading_body(
551 &mut self.builder,
552 &combined_text,
553 underline_line,
554 level,
555 self.config,
556 );
557 self.builder.finish_node();
558 }
559
560 fn try_fold_list_item_buffer_into_setext(&mut self, content: &str) -> bool {
578 let Some(Container::ListItem {
579 buffer,
580 content_col,
581 ..
582 }) = self.containers.stack.last()
583 else {
584 return false;
585 };
586 if buffer.segment_count() != 1 {
587 return false;
588 }
589 let Some(text_line) = buffer.first_text() else {
590 return false;
591 };
592
593 let content_col = *content_col;
598 let (underline_indent_cols, _) = leading_indent(content);
599 if underline_indent_cols < content_col {
600 return false;
601 }
602
603 let lines = [text_line, content];
604 let Some((level, _)) = try_parse_setext_heading(&lines, 0) else {
605 return false;
606 };
607
608 let (text_no_newline, _) = strip_newline(text_line);
609 if text_no_newline.trim().is_empty() {
610 return false;
611 }
612 if try_parse_horizontal_rule(text_no_newline).is_some() {
613 return false;
614 }
615
616 let text_owned = text_line.to_string();
617 if let Some(Container::ListItem { buffer, .. }) = self.containers.stack.last_mut() {
618 buffer.clear();
619 }
620 emit_setext_heading(&mut self.builder, &text_owned, content, level, self.config);
621 self.pos += 1;
622 true
623 }
624
625 fn close_paragraph_if_open(&mut self) {
627 if self.is_paragraph_open() {
628 self.close_containers_to(self.containers.depth() - 1);
629 }
630 }
631
632 fn prepare_for_block_element(&mut self) {
635 self.emit_list_item_buffer_if_needed();
636 self.close_paragraph_if_open();
637 }
638
639 fn close_open_footnote_definition(&mut self) {
643 while matches!(
644 self.containers.last(),
645 Some(Container::FootnoteDefinition { .. })
646 ) {
647 self.close_containers_to(self.containers.depth() - 1);
648 }
649 }
650
651 fn handle_footnote_open_effect(
652 &mut self,
653 block_match: &super::block_dispatcher::PreparedBlockMatch,
654 content: &str,
655 ) {
656 let content_start = block_match
657 .payload
658 .as_ref()
659 .and_then(|p| p.downcast_ref::<super::block_dispatcher::FootnoteDefinitionPrepared>())
660 .map(|p| p.content_start)
661 .unwrap_or(0);
662
663 let content_col = 4;
664 self.containers
665 .push(Container::FootnoteDefinition { content_col });
666
667 if content_start == 0 {
668 return;
669 }
670 let first_line_content = &content[content_start..];
671 if first_line_content.trim().is_empty() {
672 let (_, newline_str) = strip_newline(content);
673 if !newline_str.is_empty() {
674 self.builder.token(SyntaxKind::NEWLINE.into(), newline_str);
675 }
676 return;
677 }
678
679 if self.config.extensions.definition_lists
680 && let Some(blank_count) = footnote_first_line_term_lookahead(
681 &self.lines,
682 self.pos,
683 content_col,
684 self.config.extensions.table_captions,
685 )
686 {
687 self.builder.start_node(SyntaxKind::DEFINITION_LIST.into());
688 self.containers.push(Container::DefinitionList {});
689 self.builder.start_node(SyntaxKind::DEFINITION_ITEM.into());
690 self.containers.push(Container::DefinitionItem {});
691 emit_term(&mut self.builder, first_line_content, self.config);
692 for i in 0..blank_count {
693 let blank_pos = self.pos + 1 + i;
694 if blank_pos < self.lines.len() {
695 let blank_line = self.lines[blank_pos];
696 self.builder.start_node(SyntaxKind::BLANK_LINE.into());
697 self.builder
698 .token(SyntaxKind::BLANK_LINE.into(), blank_line);
699 self.builder.finish_node();
700 }
701 }
702 self.pos += blank_count;
703 return;
704 }
705
706 paragraphs::start_paragraph_if_needed(&mut self.containers, &mut self.builder);
707 paragraphs::append_paragraph_line(
708 &mut self.containers,
709 &mut self.builder,
710 first_line_content,
711 self.config,
712 );
713 }
714
715 fn try_lazy_list_continuation(
727 &mut self,
728 block_match: &super::block_dispatcher::PreparedBlockMatch,
729 content: &str,
730 ) -> bool {
731 use super::block_dispatcher::ListPrepared;
732
733 let Some(prepared) = block_match
734 .payload
735 .as_ref()
736 .and_then(|p| p.downcast_ref::<ListPrepared>())
737 else {
738 return false;
739 };
740
741 if prepared.indent_cols < 4 || !lists::in_list(&self.containers) {
742 return false;
743 }
744
745 let current_content_col = paragraphs::current_content_col(&self.containers);
746 if prepared.indent_cols >= current_content_col {
747 return false;
748 }
749
750 if lists::find_matching_list_level(
751 &self.containers,
752 &prepared.marker,
753 prepared.indent_cols,
754 self.config.dialect,
755 )
756 .is_some()
757 {
758 return false;
759 }
760
761 match self.containers.last() {
762 Some(Container::Paragraph { .. }) => {
763 paragraphs::append_paragraph_line(
764 &mut self.containers,
765 &mut self.builder,
766 content,
767 self.config,
768 );
769 true
770 }
771 Some(Container::ListItem { .. }) => {
772 if let Some(Container::ListItem {
773 buffer,
774 marker_only,
775 ..
776 }) = self.containers.stack.last_mut()
777 {
778 buffer.push_text(content);
779 if !content.trim().is_empty() {
780 *marker_only = false;
781 }
782 }
783 true
784 }
785 _ => false,
786 }
787 }
788
789 fn handle_list_open_effect(
790 &mut self,
791 block_match: &super::block_dispatcher::PreparedBlockMatch,
792 content: &str,
793 indent_to_emit: Option<&str>,
794 ) {
795 use super::block_dispatcher::ListPrepared;
796
797 let prepared = block_match
798 .payload
799 .as_ref()
800 .and_then(|p| p.downcast_ref::<ListPrepared>());
801 let Some(prepared) = prepared else {
802 return;
803 };
804
805 if prepared.indent_cols >= 4 && !lists::in_list(&self.containers) {
806 paragraphs::start_paragraph_if_needed(&mut self.containers, &mut self.builder);
807 paragraphs::append_paragraph_line(
808 &mut self.containers,
809 &mut self.builder,
810 content,
811 self.config,
812 );
813 return;
814 }
815
816 if self.is_paragraph_open() {
817 if !block_match.detection.eq(&BlockDetectionResult::Yes) {
818 paragraphs::append_paragraph_line(
819 &mut self.containers,
820 &mut self.builder,
821 content,
822 self.config,
823 );
824 return;
825 }
826 self.close_containers_to(self.containers.depth() - 1);
827 }
828
829 if matches!(
830 self.containers.last(),
831 Some(Container::Definition {
832 plain_open: true,
833 ..
834 })
835 ) {
836 self.emit_buffered_plain_if_needed();
837 }
838
839 let matched_level = lists::find_matching_list_level(
840 &self.containers,
841 &prepared.marker,
842 prepared.indent_cols,
843 self.config.dialect,
844 );
845 let list_item = ListItemEmissionInput {
846 content,
847 marker_len: prepared.marker_len,
848 spaces_after_cols: prepared.spaces_after_cols,
849 spaces_after_bytes: prepared.spaces_after,
850 indent_cols: prepared.indent_cols,
851 indent_bytes: prepared.indent_bytes,
852 virtual_marker_space: prepared.virtual_marker_space,
853 };
854 let current_content_col = paragraphs::current_content_col(&self.containers);
855 let deep_ordered_matched_level = matched_level
856 .and_then(|level| self.containers.stack.get(level).map(|c| (level, c)))
857 .and_then(|(level, container)| match container {
858 Container::List {
859 marker: list_marker,
860 base_indent_cols,
861 ..
862 } if matches!(
863 (&prepared.marker, list_marker),
864 (ListMarker::Ordered(_), ListMarker::Ordered(_))
865 ) && prepared.indent_cols >= 4
866 && *base_indent_cols >= 4
867 && prepared.indent_cols.abs_diff(*base_indent_cols) <= 3 =>
868 {
869 Some(level)
870 }
871 _ => None,
872 });
873
874 if deep_ordered_matched_level.is_none()
875 && current_content_col > 0
876 && prepared.indent_cols >= current_content_col
877 {
878 if let Some(level) = matched_level
879 && let Some(Container::List {
880 base_indent_cols, ..
881 }) = self.containers.stack.get(level)
882 && prepared.indent_cols == *base_indent_cols
883 {
884 let num_parent_lists = self.containers.stack[..level]
885 .iter()
886 .filter(|c| matches!(c, Container::List { .. }))
887 .count();
888
889 if num_parent_lists > 0 {
890 self.close_containers_to(level + 1);
891
892 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
893 self.close_containers_to(self.containers.depth() - 1);
894 }
895 if matches!(self.containers.last(), Some(Container::ListItem { .. })) {
896 self.close_containers_to(self.containers.depth() - 1);
897 }
898
899 if let Some(indent_str) = indent_to_emit {
900 self.builder
901 .token(SyntaxKind::WHITESPACE.into(), indent_str);
902 }
903
904 if let Some(nested_marker) = prepared.nested_marker {
905 lists::add_list_item_with_nested_empty_list(
906 &mut self.containers,
907 &mut self.builder,
908 &list_item,
909 nested_marker,
910 );
911 } else {
912 lists::add_list_item(
913 &mut self.containers,
914 &mut self.builder,
915 &list_item,
916 self.config,
917 );
918 }
919 self.maybe_open_fenced_code_in_new_list_item();
920 self.maybe_open_indented_code_in_new_list_item();
921 return;
922 }
923 }
924
925 self.emit_list_item_buffer_if_needed();
926
927 start_nested_list(
928 &mut self.containers,
929 &mut self.builder,
930 &prepared.marker,
931 &list_item,
932 indent_to_emit,
933 self.config,
934 );
935 self.maybe_open_fenced_code_in_new_list_item();
936 self.maybe_open_indented_code_in_new_list_item();
937 return;
938 }
939
940 if let Some(level) = matched_level {
941 self.close_containers_to(level + 1);
942
943 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
944 self.close_containers_to(self.containers.depth() - 1);
945 }
946 if matches!(self.containers.last(), Some(Container::ListItem { .. })) {
947 self.close_containers_to(self.containers.depth() - 1);
948 }
949
950 if let Some(indent_str) = indent_to_emit {
951 self.builder
952 .token(SyntaxKind::WHITESPACE.into(), indent_str);
953 }
954
955 if let Some(nested_marker) = prepared.nested_marker {
956 lists::add_list_item_with_nested_empty_list(
957 &mut self.containers,
958 &mut self.builder,
959 &list_item,
960 nested_marker,
961 );
962 } else {
963 lists::add_list_item(
964 &mut self.containers,
965 &mut self.builder,
966 &list_item,
967 self.config,
968 );
969 }
970 self.maybe_open_fenced_code_in_new_list_item();
971 self.maybe_open_indented_code_in_new_list_item();
972 return;
973 }
974
975 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
976 self.close_containers_to(self.containers.depth() - 1);
977 }
978 while matches!(
979 self.containers.last(),
980 Some(Container::ListItem { .. } | Container::List { .. })
981 ) {
982 self.close_containers_to(self.containers.depth() - 1);
983 }
984
985 self.builder.start_node(SyntaxKind::LIST.into());
986 if let Some(indent_str) = indent_to_emit {
987 self.builder
988 .token(SyntaxKind::WHITESPACE.into(), indent_str);
989 }
990 self.containers.push(Container::List {
991 marker: prepared.marker.clone(),
992 base_indent_cols: prepared.indent_cols,
993 has_blank_between_items: false,
994 });
995
996 if let Some(nested_marker) = prepared.nested_marker {
997 lists::add_list_item_with_nested_empty_list(
998 &mut self.containers,
999 &mut self.builder,
1000 &list_item,
1001 nested_marker,
1002 );
1003 } else {
1004 lists::add_list_item(
1005 &mut self.containers,
1006 &mut self.builder,
1007 &list_item,
1008 self.config,
1009 );
1010 }
1011 self.maybe_open_fenced_code_in_new_list_item();
1012 self.maybe_open_indented_code_in_new_list_item();
1013 }
1014
1015 fn handle_definition_list_effect(
1016 &mut self,
1017 block_match: &super::block_dispatcher::PreparedBlockMatch,
1018 content: &str,
1019 indent_to_emit: Option<&str>,
1020 ) {
1021 use super::block_dispatcher::DefinitionPrepared;
1022
1023 let prepared = block_match
1024 .payload
1025 .as_ref()
1026 .and_then(|p| p.downcast_ref::<DefinitionPrepared>());
1027 let Some(prepared) = prepared else {
1028 return;
1029 };
1030
1031 match prepared {
1032 DefinitionPrepared::Definition {
1033 marker_char,
1034 indent,
1035 spaces_after,
1036 spaces_after_cols,
1037 has_content,
1038 } => {
1039 self.emit_buffered_plain_if_needed();
1040
1041 while matches!(self.containers.last(), Some(Container::ListItem { .. })) {
1042 self.close_containers_to(self.containers.depth() - 1);
1043 }
1044 while matches!(self.containers.last(), Some(Container::List { .. })) {
1045 self.close_containers_to(self.containers.depth() - 1);
1046 }
1047
1048 if matches!(self.containers.last(), Some(Container::Definition { .. })) {
1049 self.close_containers_to(self.containers.depth() - 1);
1050 }
1051
1052 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1053 self.close_containers_to(self.containers.depth() - 1);
1054 }
1055
1056 if definition_lists::in_definition_list(&self.containers)
1060 && !matches!(
1061 self.containers.last(),
1062 Some(Container::DefinitionItem { .. })
1063 )
1064 {
1065 self.builder.start_node(SyntaxKind::DEFINITION_ITEM.into());
1066 self.containers.push(Container::DefinitionItem {});
1067 }
1068
1069 if !definition_lists::in_definition_list(&self.containers) {
1070 self.builder.start_node(SyntaxKind::DEFINITION_LIST.into());
1071 self.containers.push(Container::DefinitionList {});
1072 }
1073
1074 if !matches!(
1075 self.containers.last(),
1076 Some(Container::DefinitionItem { .. })
1077 ) {
1078 self.builder.start_node(SyntaxKind::DEFINITION_ITEM.into());
1079 self.containers.push(Container::DefinitionItem {});
1080 }
1081
1082 self.builder.start_node(SyntaxKind::DEFINITION.into());
1083
1084 if let Some(indent_str) = indent_to_emit {
1085 self.builder
1086 .token(SyntaxKind::WHITESPACE.into(), indent_str);
1087 }
1088
1089 emit_definition_marker(&mut self.builder, *marker_char, *indent);
1090 let indent_bytes = byte_index_at_column(content, *indent);
1091 if *spaces_after > 0 {
1092 let space_start = indent_bytes + 1;
1093 let space_end = space_start + *spaces_after;
1094 if space_end <= content.len() {
1095 self.builder.token(
1096 SyntaxKind::WHITESPACE.into(),
1097 &content[space_start..space_end],
1098 );
1099 }
1100 }
1101
1102 if !*has_content {
1103 let current_line = self.lines[self.pos];
1104 let (_, newline_str) = strip_newline(current_line);
1105 if !newline_str.is_empty() {
1106 self.builder.token(SyntaxKind::NEWLINE.into(), newline_str);
1107 }
1108 }
1109
1110 let content_col = *indent + 1 + *spaces_after_cols;
1111 let content_start_bytes = indent_bytes + 1 + *spaces_after;
1112 let after_marker_and_spaces = content.get(content_start_bytes..).unwrap_or("");
1113 let mut plain_buffer = TextBuffer::new();
1114 let mut definition_pushed = false;
1115
1116 if *has_content {
1117 let current_line = self.lines[self.pos];
1118 let (trimmed_line, _) = strip_newline(current_line);
1119
1120 let content_start = content_start_bytes.min(trimmed_line.len());
1121 let content_slice = &trimmed_line[content_start..];
1122 let content_line = ¤t_line[content_start_bytes.min(current_line.len())..];
1123
1124 let (blockquote_depth, inner_blockquote_content) =
1125 count_blockquote_markers(content_line);
1126
1127 let should_start_list_from_first_line = self
1128 .lines
1129 .get(self.pos + 1)
1130 .map(|next_line| {
1131 let (next_without_newline, _) = strip_newline(next_line);
1132 if next_without_newline.trim().is_empty() {
1133 return true;
1134 }
1135
1136 let (next_indent_cols, _) = leading_indent(next_without_newline);
1137 next_indent_cols >= content_col
1138 })
1139 .unwrap_or(true);
1140
1141 if blockquote_depth > 0 {
1142 self.containers.push(Container::Definition {
1143 content_col,
1144 plain_open: false,
1145 plain_buffer: TextBuffer::new(),
1146 });
1147 definition_pushed = true;
1148
1149 let marker_info = parse_blockquote_marker_info(content_line);
1150 for level in 0..blockquote_depth {
1151 self.builder.start_node(SyntaxKind::BLOCK_QUOTE.into());
1152 if let Some(info) = marker_info.get(level) {
1153 blockquotes::emit_one_blockquote_marker(
1154 &mut self.builder,
1155 info.leading_spaces,
1156 info.has_trailing_space,
1157 );
1158 }
1159 self.containers.push(Container::BlockQuote {});
1160 }
1161
1162 if !inner_blockquote_content.trim().is_empty() {
1163 paragraphs::start_paragraph_if_needed(
1164 &mut self.containers,
1165 &mut self.builder,
1166 );
1167 paragraphs::append_paragraph_line(
1168 &mut self.containers,
1169 &mut self.builder,
1170 inner_blockquote_content,
1171 self.config,
1172 );
1173 }
1174 } else if let Some(marker_match) =
1175 try_parse_list_marker(content_slice, self.config)
1176 && should_start_list_from_first_line
1177 {
1178 self.containers.push(Container::Definition {
1179 content_col,
1180 plain_open: false,
1181 plain_buffer: TextBuffer::new(),
1182 });
1183 definition_pushed = true;
1184
1185 let (indent_cols, indent_bytes) = leading_indent(content_line);
1186 self.builder.start_node(SyntaxKind::LIST.into());
1187 self.containers.push(Container::List {
1188 marker: marker_match.marker.clone(),
1189 base_indent_cols: indent_cols,
1190 has_blank_between_items: false,
1191 });
1192
1193 let list_item = ListItemEmissionInput {
1194 content: content_line,
1195 marker_len: marker_match.marker_len,
1196 spaces_after_cols: marker_match.spaces_after_cols,
1197 spaces_after_bytes: marker_match.spaces_after_bytes,
1198 indent_cols,
1199 indent_bytes,
1200 virtual_marker_space: marker_match.virtual_marker_space,
1201 };
1202
1203 if let Some(nested_marker) = is_content_nested_bullet_marker(
1204 content_line,
1205 marker_match.marker_len,
1206 marker_match.spaces_after_bytes,
1207 ) {
1208 lists::add_list_item_with_nested_empty_list(
1209 &mut self.containers,
1210 &mut self.builder,
1211 &list_item,
1212 nested_marker,
1213 );
1214 } else {
1215 lists::add_list_item(
1216 &mut self.containers,
1217 &mut self.builder,
1218 &list_item,
1219 self.config,
1220 );
1221 }
1222 } else if let Some(fence) = code_blocks::try_parse_fence_open(content_slice) {
1223 self.containers.push(Container::Definition {
1224 content_col,
1225 plain_open: false,
1226 plain_buffer: TextBuffer::new(),
1227 });
1228 definition_pushed = true;
1229
1230 let bq_depth = self.current_blockquote_depth();
1231 if let Some(indent_str) = indent_to_emit {
1232 self.builder
1233 .token(SyntaxKind::WHITESPACE.into(), indent_str);
1234 }
1235 let fence_line = current_line[content_start..].to_string();
1236 let new_pos = if self.config.extensions.tex_math_gfm
1237 && code_blocks::is_gfm_math_fence(&fence)
1238 {
1239 code_blocks::parse_fenced_math_block(
1240 &mut self.builder,
1241 &self.lines,
1242 self.pos,
1243 fence,
1244 bq_depth,
1245 content_col,
1246 Some(&fence_line),
1247 )
1248 } else {
1249 code_blocks::parse_fenced_code_block(
1250 &mut self.builder,
1251 &self.lines,
1252 self.pos,
1253 fence,
1254 bq_depth,
1255 content_col,
1256 Some(&fence_line),
1257 )
1258 };
1259 self.pos = new_pos - 1;
1260 } else {
1261 let (_, newline_str) = strip_newline(current_line);
1262 let (content_without_newline, _) = strip_newline(after_marker_and_spaces);
1263 if content_without_newline.is_empty() {
1264 plain_buffer.push_line(newline_str);
1265 } else {
1266 let line_with_newline = if !newline_str.is_empty() {
1267 format!("{}{}", content_without_newline, newline_str)
1268 } else {
1269 content_without_newline.to_string()
1270 };
1271 plain_buffer.push_line(line_with_newline);
1272 }
1273 }
1274 }
1275
1276 if !definition_pushed {
1277 self.containers.push(Container::Definition {
1278 content_col,
1279 plain_open: *has_content,
1280 plain_buffer,
1281 });
1282 }
1283 }
1284 DefinitionPrepared::Term { blank_count } => {
1285 self.emit_buffered_plain_if_needed();
1286
1287 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1288 self.close_containers_to(self.containers.depth() - 1);
1289 }
1290
1291 if !definition_lists::in_definition_list(&self.containers) {
1292 self.builder.start_node(SyntaxKind::DEFINITION_LIST.into());
1293 self.containers.push(Container::DefinitionList {});
1294 }
1295
1296 while matches!(
1297 self.containers.last(),
1298 Some(Container::Definition { .. }) | Some(Container::DefinitionItem { .. })
1299 ) {
1300 self.close_containers_to(self.containers.depth() - 1);
1301 }
1302
1303 self.builder.start_node(SyntaxKind::DEFINITION_ITEM.into());
1304 self.containers.push(Container::DefinitionItem {});
1305
1306 emit_term(&mut self.builder, content, self.config);
1307
1308 for i in 0..*blank_count {
1309 let blank_pos = self.pos + 1 + i;
1310 if blank_pos < self.lines.len() {
1311 let blank_line = self.lines[blank_pos];
1312 self.builder.start_node(SyntaxKind::BLANK_LINE.into());
1313 self.builder
1314 .token(SyntaxKind::BLANK_LINE.into(), blank_line);
1315 self.builder.finish_node();
1316 }
1317 }
1318 self.pos += *blank_count;
1319 }
1320 }
1321 }
1322
1323 fn blockquote_marker_info(
1325 &self,
1326 payload: Option<&BlockQuotePrepared>,
1327 line: &str,
1328 ) -> Vec<marker_utils::BlockQuoteMarkerInfo> {
1329 payload
1330 .map(|payload| payload.marker_info.clone())
1331 .unwrap_or_else(|| parse_blockquote_marker_info(line))
1332 }
1333
1334 fn marker_info_for_line(
1340 &self,
1341 payload: Option<&BlockQuotePrepared>,
1342 raw_line: &str,
1343 marker_line: &str,
1344 shifted_prefix: &str,
1345 used_shifted: bool,
1346 ) -> Vec<marker_utils::BlockQuoteMarkerInfo> {
1347 let mut marker_info = if used_shifted {
1348 parse_blockquote_marker_info(marker_line)
1349 } else {
1350 self.blockquote_marker_info(payload, raw_line)
1351 };
1352 if used_shifted && !shifted_prefix.is_empty() {
1353 let (prefix_cols, _) = leading_indent(shifted_prefix);
1354 if let Some(first) = marker_info.first_mut() {
1355 first.leading_spaces += prefix_cols;
1356 }
1357 }
1358 marker_info
1359 }
1360
1361 fn shifted_blockquote_from_list<'b>(
1364 &self,
1365 line: &'b str,
1366 ) -> Option<(usize, &'b str, &'b str, &'b str)> {
1367 let list_content_col = paragraphs::current_content_col(&self.containers);
1368 let content_container_indent = self.content_container_indent_to_strip();
1369 let marker_col = list_content_col.saturating_add(content_container_indent);
1370 if marker_col == 0 {
1371 return None;
1372 }
1373
1374 let (indent_cols, _) = leading_indent(line);
1375 if indent_cols < marker_col {
1376 return None;
1377 }
1378
1379 let idx = byte_index_at_column(line, marker_col);
1380 if idx > line.len() {
1381 return None;
1382 }
1383
1384 let candidate = &line[idx..];
1385 let (candidate_depth, candidate_inner) = count_blockquote_markers(candidate);
1386 if candidate_depth == 0 {
1387 return None;
1388 }
1389
1390 Some((candidate_depth, candidate_inner, candidate, &line[..idx]))
1391 }
1392
1393 fn emit_blockquote_markers(
1394 &mut self,
1395 marker_info: &[marker_utils::BlockQuoteMarkerInfo],
1396 depth: usize,
1397 ) {
1398 for i in 0..depth {
1399 if let Some(info) = marker_info.get(i) {
1400 blockquotes::emit_one_blockquote_marker(
1401 &mut self.builder,
1402 info.leading_spaces,
1403 info.has_trailing_space,
1404 );
1405 }
1406 }
1407 }
1408
1409 fn current_blockquote_depth(&self) -> usize {
1410 blockquotes::current_blockquote_depth(&self.containers)
1411 }
1412
1413 fn emit_or_buffer_blockquote_marker(
1418 &mut self,
1419 leading_spaces: usize,
1420 has_trailing_space: bool,
1421 ) {
1422 if let Some(Container::ListItem {
1423 buffer,
1424 marker_only,
1425 ..
1426 }) = self.containers.stack.last_mut()
1427 {
1428 buffer.push_blockquote_marker(leading_spaces, has_trailing_space);
1429 *marker_only = false;
1430 return;
1431 }
1432
1433 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1435 paragraphs::append_paragraph_marker(
1437 &mut self.containers,
1438 leading_spaces,
1439 has_trailing_space,
1440 );
1441 } else {
1442 blockquotes::emit_one_blockquote_marker(
1444 &mut self.builder,
1445 leading_spaces,
1446 has_trailing_space,
1447 );
1448 }
1449 }
1450
1451 fn parse_document_stack(&mut self) {
1452 self.builder.start_node(SyntaxKind::DOCUMENT.into());
1453
1454 log::trace!("Starting document parse");
1455
1456 while self.pos < self.lines.len() {
1459 let line = self.lines[self.pos];
1460
1461 log::trace!("Parsing line {}: {}", self.pos + 1, line);
1462
1463 if self.parse_line(line) {
1464 continue;
1465 }
1466 self.pos += 1;
1467 }
1468
1469 self.close_containers_to(0);
1470 self.builder.finish_node(); }
1472
1473 fn parse_line(&mut self, line: &str) -> bool {
1475 let (mut bq_depth, mut inner_content) = count_blockquote_markers(line);
1478 let mut bq_marker_line = line;
1479 let mut shifted_bq_prefix = "";
1480 let mut used_shifted_bq = false;
1481 if bq_depth == 0
1482 && let Some((candidate_depth, candidate_inner, candidate_line, candidate_prefix)) =
1483 self.shifted_blockquote_from_list(line)
1484 {
1485 bq_depth = candidate_depth;
1486 inner_content = candidate_inner;
1487 bq_marker_line = candidate_line;
1488 shifted_bq_prefix = candidate_prefix;
1489 used_shifted_bq = true;
1490 }
1491 let current_bq_depth = self.current_blockquote_depth();
1492
1493 let has_blank_before = self.pos == 0 || is_blank_line(self.lines[self.pos - 1]);
1494 let mut blockquote_match: Option<PreparedBlockMatch> = None;
1495 let dispatcher_ctx = if current_bq_depth == 0 {
1496 Some(BlockContext {
1497 content: line,
1498 has_blank_before,
1499 has_blank_before_strict: has_blank_before,
1500 at_document_start: self.pos == 0,
1501 in_fenced_div: self.in_fenced_div(),
1502 blockquote_depth: current_bq_depth,
1503 config: self.config,
1504 content_indent: 0,
1505 indent_to_emit: None,
1506 list_indent_info: None,
1507 in_list: lists::in_list(&self.containers),
1508 in_marker_only_list_item: matches!(
1509 self.containers.last(),
1510 Some(Container::ListItem {
1511 marker_only: true,
1512 ..
1513 })
1514 ),
1515 paragraph_open: self.is_paragraph_open(),
1516 next_line: if self.pos + 1 < self.lines.len() {
1517 Some(self.lines[self.pos + 1])
1518 } else {
1519 None
1520 },
1521 })
1522 } else {
1523 None
1524 };
1525
1526 let blockquote_payload = if let Some(dispatcher_ctx) = dispatcher_ctx.as_ref() {
1527 self.block_registry
1528 .detect_prepared(dispatcher_ctx, &self.lines, self.pos)
1529 .and_then(|prepared| {
1530 if matches!(prepared.effect, BlockEffect::OpenBlockQuote) {
1531 blockquote_match = Some(prepared);
1532 blockquote_match.as_ref().and_then(|prepared| {
1533 prepared
1534 .payload
1535 .as_ref()
1536 .and_then(|payload| payload.downcast_ref::<BlockQuotePrepared>())
1537 .cloned()
1538 })
1539 } else {
1540 None
1541 }
1542 })
1543 } else {
1544 None
1545 };
1546
1547 log::trace!(
1548 "parse_line [{}]: bq_depth={}, current_bq={}, depth={}, line={:?}",
1549 self.pos,
1550 bq_depth,
1551 current_bq_depth,
1552 self.containers.depth(),
1553 line.trim_end()
1554 );
1555
1556 let inner_blank_in_blockquote = bq_depth > 0
1563 && is_blank_line(inner_content)
1564 && (current_bq_depth > 0
1565 || !self.config.extensions.blank_before_blockquote
1566 || blockquotes::can_start_blockquote(self.pos, &self.lines));
1567 let is_blank = is_blank_line(line) || inner_blank_in_blockquote;
1568
1569 if is_blank {
1570 if self.is_paragraph_open()
1571 && paragraphs::has_open_inline_math_environment(&self.containers)
1572 {
1573 paragraphs::append_paragraph_line(
1574 &mut self.containers,
1575 &mut self.builder,
1576 line,
1577 self.config,
1578 );
1579 self.pos += 1;
1580 return true;
1581 }
1582
1583 self.close_paragraph_if_open();
1585
1586 self.emit_buffered_plain_if_needed();
1590
1591 if bq_depth > current_bq_depth {
1599 for _ in current_bq_depth..bq_depth {
1601 self.builder.start_node(SyntaxKind::BLOCK_QUOTE.into());
1602 self.containers.push(Container::BlockQuote {});
1603 }
1604 } else if bq_depth < current_bq_depth {
1605 self.close_blockquotes_to_depth(bq_depth);
1607 }
1608
1609 let mut peek = self.pos + 1;
1616 while peek < self.lines.len() {
1617 let peek_line = self.lines[peek];
1618 if is_blank_line(peek_line) {
1619 peek += 1;
1620 continue;
1621 }
1622 if bq_depth > 0 {
1623 let (peek_bq, _) = count_blockquote_markers(peek_line);
1624 if peek_bq >= bq_depth {
1625 let peek_inner =
1626 blockquotes::strip_n_blockquote_markers(peek_line, bq_depth);
1627 if is_blank_line(peek_inner) {
1628 peek += 1;
1629 continue;
1630 }
1631 }
1632 }
1633 break;
1634 }
1635
1636 let levels_to_keep = if peek < self.lines.len() {
1638 ContinuationPolicy::new(self.config, &self.block_registry).compute_levels_to_keep(
1639 self.current_blockquote_depth(),
1640 &self.containers,
1641 &self.lines,
1642 peek,
1643 self.lines[peek],
1644 )
1645 } else {
1646 0
1647 };
1648 log::trace!(
1649 "Blank line: depth={}, levels_to_keep={}, next='{}'",
1650 self.containers.depth(),
1651 levels_to_keep,
1652 if peek < self.lines.len() {
1653 self.lines[peek]
1654 } else {
1655 "<EOF>"
1656 }
1657 );
1658
1659 while self.containers.depth() > levels_to_keep {
1663 match self.containers.last() {
1664 Some(Container::ListItem { .. }) => {
1665 log::trace!(
1667 "Closing ListItem at blank line (levels_to_keep={} < depth={})",
1668 levels_to_keep,
1669 self.containers.depth()
1670 );
1671 self.close_containers_to(self.containers.depth() - 1);
1672 }
1673 Some(Container::List { .. })
1674 | Some(Container::FootnoteDefinition { .. })
1675 | Some(Container::Alert { .. })
1676 | Some(Container::Paragraph { .. })
1677 | Some(Container::Definition { .. })
1678 | Some(Container::DefinitionItem { .. })
1679 | Some(Container::DefinitionList { .. }) => {
1680 log::trace!(
1681 "Closing {:?} at blank line (depth {} > levels_to_keep {})",
1682 self.containers.last(),
1683 self.containers.depth(),
1684 levels_to_keep
1685 );
1686
1687 self.close_containers_to(self.containers.depth() - 1);
1688 }
1689 _ => break,
1690 }
1691 }
1692
1693 if matches!(self.containers.last(), Some(Container::ListItem { .. })) {
1697 self.emit_list_item_buffer_if_needed();
1698 }
1699
1700 if bq_depth > 0 {
1702 let marker_info = self.marker_info_for_line(
1703 blockquote_payload.as_ref(),
1704 line,
1705 bq_marker_line,
1706 shifted_bq_prefix,
1707 used_shifted_bq,
1708 );
1709 self.emit_blockquote_markers(&marker_info, bq_depth);
1710 }
1711
1712 self.builder.start_node(SyntaxKind::BLANK_LINE.into());
1713 self.builder
1714 .token(SyntaxKind::BLANK_LINE.into(), inner_content);
1715 self.builder.finish_node();
1716
1717 self.pos += 1;
1718 return true;
1719 }
1720
1721 if bq_depth > current_bq_depth {
1723 if self.config.extensions.blank_before_blockquote
1726 && current_bq_depth == 0
1727 && !used_shifted_bq
1728 && !blockquote_payload
1729 .as_ref()
1730 .map(|payload| payload.can_start)
1731 .unwrap_or_else(|| blockquotes::can_start_blockquote(self.pos, &self.lines))
1732 {
1733 self.emit_list_item_buffer_if_needed();
1737 paragraphs::start_paragraph_if_needed(&mut self.containers, &mut self.builder);
1738 paragraphs::append_paragraph_line(
1739 &mut self.containers,
1740 &mut self.builder,
1741 line,
1742 self.config,
1743 );
1744 self.pos += 1;
1745 return true;
1746 }
1747
1748 let can_nest = if current_bq_depth > 0 {
1751 if self.config.extensions.blank_before_blockquote {
1752 matches!(self.containers.last(), Some(Container::BlockQuote { .. }))
1754 || (self.pos > 0 && {
1755 let prev_line = self.lines[self.pos - 1];
1756 let (prev_bq_depth, prev_inner) = count_blockquote_markers(prev_line);
1757 prev_bq_depth >= current_bq_depth && is_blank_line(prev_inner)
1758 })
1759 } else {
1760 true
1761 }
1762 } else {
1763 blockquote_payload
1764 .as_ref()
1765 .map(|payload| payload.can_nest)
1766 .unwrap_or(true)
1767 };
1768
1769 if !can_nest {
1770 let content_at_current_depth =
1773 blockquotes::strip_n_blockquote_markers(line, current_bq_depth);
1774
1775 let marker_info = self.marker_info_for_line(
1777 blockquote_payload.as_ref(),
1778 line,
1779 bq_marker_line,
1780 shifted_bq_prefix,
1781 used_shifted_bq,
1782 );
1783 for i in 0..current_bq_depth {
1784 if let Some(info) = marker_info.get(i) {
1785 self.emit_or_buffer_blockquote_marker(
1786 info.leading_spaces,
1787 info.has_trailing_space,
1788 );
1789 }
1790 }
1791
1792 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1793 paragraphs::append_paragraph_line(
1795 &mut self.containers,
1796 &mut self.builder,
1797 content_at_current_depth,
1798 self.config,
1799 );
1800 self.pos += 1;
1801 return true;
1802 } else {
1803 paragraphs::start_paragraph_if_needed(&mut self.containers, &mut self.builder);
1805 paragraphs::append_paragraph_line(
1806 &mut self.containers,
1807 &mut self.builder,
1808 content_at_current_depth,
1809 self.config,
1810 );
1811 self.pos += 1;
1812 return true;
1813 }
1814 }
1815
1816 self.emit_list_item_buffer_if_needed();
1819
1820 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1822 self.close_containers_to(self.containers.depth() - 1);
1823 }
1824
1825 let marker_info = self.marker_info_for_line(
1827 blockquote_payload.as_ref(),
1828 line,
1829 bq_marker_line,
1830 shifted_bq_prefix,
1831 used_shifted_bq,
1832 );
1833
1834 if let (Some(dispatcher_ctx), Some(prepared)) =
1835 (dispatcher_ctx.as_ref(), blockquote_match.as_ref())
1836 {
1837 let _ = self.block_registry.parse_prepared(
1838 prepared,
1839 dispatcher_ctx,
1840 &mut self.builder,
1841 &self.lines,
1842 self.pos,
1843 );
1844 for _ in 0..bq_depth {
1845 self.containers.push(Container::BlockQuote {});
1846 }
1847 } else {
1848 for level in 0..current_bq_depth {
1850 if let Some(info) = marker_info.get(level) {
1851 self.emit_or_buffer_blockquote_marker(
1852 info.leading_spaces,
1853 info.has_trailing_space,
1854 );
1855 }
1856 }
1857
1858 for level in current_bq_depth..bq_depth {
1860 self.builder.start_node(SyntaxKind::BLOCK_QUOTE.into());
1861
1862 if let Some(info) = marker_info.get(level) {
1864 blockquotes::emit_one_blockquote_marker(
1865 &mut self.builder,
1866 info.leading_spaces,
1867 info.has_trailing_space,
1868 );
1869 }
1870
1871 self.containers.push(Container::BlockQuote {});
1872 }
1873 }
1874
1875 return self.parse_inner_content(inner_content, Some(inner_content));
1878 } else if bq_depth < current_bq_depth {
1879 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1885 let is_commonmark = self.config.dialect == crate::options::Dialect::CommonMark;
1892 let interrupts_via_hr = is_commonmark && try_parse_horizontal_rule(line).is_some();
1893 let interrupts_via_fence =
1894 is_commonmark && code_blocks::try_parse_fence_open(line).is_some();
1895 if !interrupts_via_hr && !interrupts_via_fence {
1896 if bq_depth > 0 {
1897 let marker_info = self.marker_info_for_line(
1903 blockquote_payload.as_ref(),
1904 line,
1905 bq_marker_line,
1906 shifted_bq_prefix,
1907 used_shifted_bq,
1908 );
1909 for i in 0..bq_depth {
1910 if let Some(info) = marker_info.get(i) {
1911 paragraphs::append_paragraph_marker(
1912 &mut self.containers,
1913 info.leading_spaces,
1914 info.has_trailing_space,
1915 );
1916 }
1917 }
1918 paragraphs::append_paragraph_line(
1919 &mut self.containers,
1920 &mut self.builder,
1921 inner_content,
1922 self.config,
1923 );
1924 } else {
1925 paragraphs::append_paragraph_line(
1926 &mut self.containers,
1927 &mut self.builder,
1928 line,
1929 self.config,
1930 );
1931 }
1932 self.pos += 1;
1933 return true;
1934 }
1935 }
1936 if matches!(self.containers.last(), Some(Container::ListItem { .. }))
1944 && lists::in_blockquote_list(&self.containers)
1945 && try_parse_list_marker(line, self.config).is_none()
1946 {
1947 let is_commonmark = self.config.dialect == crate::options::Dialect::CommonMark;
1948 let interrupts_via_hr = is_commonmark && try_parse_horizontal_rule(line).is_some();
1949 let interrupts_via_fence =
1950 is_commonmark && code_blocks::try_parse_fence_open(line).is_some();
1951 if !interrupts_via_hr && !interrupts_via_fence {
1952 if bq_depth > 0 {
1953 let marker_info = self.marker_info_for_line(
1954 blockquote_payload.as_ref(),
1955 line,
1956 bq_marker_line,
1957 shifted_bq_prefix,
1958 used_shifted_bq,
1959 );
1960 if let Some(Container::ListItem {
1961 buffer,
1962 marker_only,
1963 ..
1964 }) = self.containers.stack.last_mut()
1965 {
1966 for i in 0..bq_depth {
1967 if let Some(info) = marker_info.get(i) {
1968 buffer.push_blockquote_marker(
1969 info.leading_spaces,
1970 info.has_trailing_space,
1971 );
1972 }
1973 }
1974 buffer.push_text(inner_content);
1975 if !inner_content.trim().is_empty() {
1976 *marker_only = false;
1977 }
1978 }
1979 } else if let Some(Container::ListItem {
1980 buffer,
1981 marker_only,
1982 ..
1983 }) = self.containers.stack.last_mut()
1984 {
1985 buffer.push_text(line);
1986 if !line.trim().is_empty() {
1987 *marker_only = false;
1988 }
1989 }
1990 self.pos += 1;
1991 return true;
1992 }
1993 }
1994 if bq_depth == 0 && self.config.dialect != crate::options::Dialect::CommonMark {
2000 if lists::in_blockquote_list(&self.containers)
2003 && let Some(marker_match) = try_parse_list_marker(line, self.config)
2004 {
2005 let (indent_cols, indent_bytes) = leading_indent(line);
2006 if let Some(level) = lists::find_matching_list_level(
2007 &self.containers,
2008 &marker_match.marker,
2009 indent_cols,
2010 self.config.dialect,
2011 ) {
2012 self.close_containers_to(level + 1);
2015
2016 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
2018 self.close_containers_to(self.containers.depth() - 1);
2019 }
2020 if matches!(self.containers.last(), Some(Container::ListItem { .. })) {
2021 self.close_containers_to(self.containers.depth() - 1);
2022 }
2023
2024 if let Some(nested_marker) = is_content_nested_bullet_marker(
2026 line,
2027 marker_match.marker_len,
2028 marker_match.spaces_after_bytes,
2029 ) {
2030 let list_item = ListItemEmissionInput {
2031 content: line,
2032 marker_len: marker_match.marker_len,
2033 spaces_after_cols: marker_match.spaces_after_cols,
2034 spaces_after_bytes: marker_match.spaces_after_bytes,
2035 indent_cols,
2036 indent_bytes,
2037 virtual_marker_space: marker_match.virtual_marker_space,
2038 };
2039 lists::add_list_item_with_nested_empty_list(
2040 &mut self.containers,
2041 &mut self.builder,
2042 &list_item,
2043 nested_marker,
2044 );
2045 } else {
2046 let list_item = ListItemEmissionInput {
2047 content: line,
2048 marker_len: marker_match.marker_len,
2049 spaces_after_cols: marker_match.spaces_after_cols,
2050 spaces_after_bytes: marker_match.spaces_after_bytes,
2051 indent_cols,
2052 indent_bytes,
2053 virtual_marker_space: marker_match.virtual_marker_space,
2054 };
2055 lists::add_list_item(
2056 &mut self.containers,
2057 &mut self.builder,
2058 &list_item,
2059 self.config,
2060 );
2061 }
2062 self.pos += 1;
2063 return true;
2064 }
2065 }
2066 }
2067
2068 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
2070 self.close_containers_to(self.containers.depth() - 1);
2071 }
2072
2073 self.close_blockquotes_to_depth(bq_depth);
2075
2076 if bq_depth > 0 {
2078 let marker_info = self.marker_info_for_line(
2080 blockquote_payload.as_ref(),
2081 line,
2082 bq_marker_line,
2083 shifted_bq_prefix,
2084 used_shifted_bq,
2085 );
2086 for i in 0..bq_depth {
2087 if let Some(info) = marker_info.get(i) {
2088 self.emit_or_buffer_blockquote_marker(
2089 info.leading_spaces,
2090 info.has_trailing_space,
2091 );
2092 }
2093 }
2094 return self.parse_inner_content(inner_content, Some(inner_content));
2096 } else {
2097 return self.parse_inner_content(line, None);
2099 }
2100 } else if bq_depth > 0 {
2101 let mut list_item_continuation = false;
2103 let same_depth_marker_info = self.marker_info_for_line(
2104 blockquote_payload.as_ref(),
2105 line,
2106 bq_marker_line,
2107 shifted_bq_prefix,
2108 used_shifted_bq,
2109 );
2110 let has_explicit_same_depth_marker = same_depth_marker_info.len() >= bq_depth;
2111
2112 let (inner_indent_cols_raw, inner_indent_bytes) = leading_indent(inner_content);
2124 if let Some(marker_match) = try_parse_list_marker(inner_content, self.config) {
2125 let inner_content_threshold =
2129 marker_match.marker_len + marker_match.spaces_after_cols;
2130 let is_sibling_candidate = inner_indent_cols_raw < inner_content_threshold;
2131 let sibling_list_level = if is_sibling_candidate {
2132 self.containers
2133 .stack
2134 .iter()
2135 .enumerate()
2136 .rev()
2137 .find_map(|(i, c)| match c {
2138 Container::List { marker, .. }
2139 if lists::markers_match(
2140 &marker_match.marker,
2141 marker,
2142 self.config.dialect,
2143 ) && self.containers.stack[..i]
2144 .iter()
2145 .filter(|x| matches!(x, Container::BlockQuote { .. }))
2146 .count()
2147 == bq_depth =>
2148 {
2149 Some(i)
2150 }
2151 _ => None,
2152 })
2153 } else {
2154 None
2155 };
2156 if let Some(list_level) = sibling_list_level {
2157 let sibling_base_indent_cols = match self.containers.stack.get(list_level) {
2163 Some(Container::List {
2164 base_indent_cols, ..
2165 }) => *base_indent_cols,
2166 _ => 0,
2167 };
2168
2169 self.emit_list_item_buffer_if_needed();
2171 self.close_containers_to(list_level + 1);
2174
2175 for i in 0..bq_depth {
2179 if let Some(info) = same_depth_marker_info.get(i) {
2180 self.emit_or_buffer_blockquote_marker(
2181 info.leading_spaces,
2182 info.has_trailing_space,
2183 );
2184 }
2185 }
2186
2187 let list_item = ListItemEmissionInput {
2189 content: inner_content,
2190 marker_len: marker_match.marker_len,
2191 spaces_after_cols: marker_match.spaces_after_cols,
2192 spaces_after_bytes: marker_match.spaces_after_bytes,
2193 indent_cols: sibling_base_indent_cols,
2194 indent_bytes: inner_indent_bytes,
2195 virtual_marker_space: marker_match.virtual_marker_space,
2196 };
2197 lists::add_list_item(
2198 &mut self.containers,
2199 &mut self.builder,
2200 &list_item,
2201 self.config,
2202 );
2203 self.maybe_open_fenced_code_in_new_list_item();
2204 self.maybe_open_indented_code_in_new_list_item();
2205 self.pos += 1;
2206 return true;
2207 }
2208 }
2209
2210 if matches!(
2213 self.containers.last(),
2214 Some(Container::ListItem { content_col: _, .. })
2215 ) {
2216 let (indent_cols, _) = leading_indent(inner_content);
2217 let content_indent = self.content_container_indent_to_strip();
2218 let effective_indent = indent_cols.saturating_sub(content_indent);
2219 let content_col = match self.containers.last() {
2220 Some(Container::ListItem { content_col, .. }) => *content_col,
2221 _ => 0,
2222 };
2223
2224 let is_new_item_at_outer_level =
2226 if try_parse_list_marker(inner_content, self.config).is_some() {
2227 effective_indent < content_col
2228 } else {
2229 false
2230 };
2231
2232 if is_new_item_at_outer_level
2236 || (effective_indent < content_col && !has_explicit_same_depth_marker)
2237 {
2238 log::trace!(
2239 "Closing ListItem: is_new_item={}, effective_indent={} < content_col={}",
2240 is_new_item_at_outer_level,
2241 effective_indent,
2242 content_col
2243 );
2244 self.close_containers_to(self.containers.depth() - 1);
2245 } else {
2246 log::trace!(
2247 "Keeping ListItem: effective_indent={} >= content_col={}",
2248 effective_indent,
2249 content_col
2250 );
2251 list_item_continuation = true;
2252 }
2253 }
2254
2255 if list_item_continuation && code_blocks::try_parse_fence_open(inner_content).is_some()
2259 {
2260 list_item_continuation = false;
2261 }
2262
2263 let continuation_has_explicit_marker = list_item_continuation && {
2264 if has_explicit_same_depth_marker {
2265 for i in 0..bq_depth {
2266 if let Some(info) = same_depth_marker_info.get(i) {
2267 self.emit_or_buffer_blockquote_marker(
2268 info.leading_spaces,
2269 info.has_trailing_space,
2270 );
2271 }
2272 }
2273 true
2274 } else {
2275 false
2276 }
2277 };
2278
2279 if !list_item_continuation {
2280 let marker_info = self.marker_info_for_line(
2281 blockquote_payload.as_ref(),
2282 line,
2283 bq_marker_line,
2284 shifted_bq_prefix,
2285 used_shifted_bq,
2286 );
2287 for i in 0..bq_depth {
2288 if let Some(info) = marker_info.get(i) {
2289 self.emit_or_buffer_blockquote_marker(
2290 info.leading_spaces,
2291 info.has_trailing_space,
2292 );
2293 }
2294 }
2295 }
2296 let line_to_append = if list_item_continuation {
2297 if continuation_has_explicit_marker {
2298 Some(inner_content)
2299 } else {
2300 Some(line)
2301 }
2302 } else {
2303 Some(inner_content)
2304 };
2305 return self.parse_inner_content(inner_content, line_to_append);
2306 }
2307
2308 if current_bq_depth > 0 {
2311 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
2313 paragraphs::append_paragraph_line(
2314 &mut self.containers,
2315 &mut self.builder,
2316 line,
2317 self.config,
2318 );
2319 self.pos += 1;
2320 return true;
2321 }
2322
2323 if lists::in_blockquote_list(&self.containers)
2325 && let Some(marker_match) = try_parse_list_marker(line, self.config)
2326 {
2327 let (indent_cols, indent_bytes) = leading_indent(line);
2328 if let Some(level) = lists::find_matching_list_level(
2329 &self.containers,
2330 &marker_match.marker,
2331 indent_cols,
2332 self.config.dialect,
2333 ) {
2334 self.close_containers_to(level + 1);
2336
2337 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
2339 self.close_containers_to(self.containers.depth() - 1);
2340 }
2341 if matches!(self.containers.last(), Some(Container::ListItem { .. })) {
2342 self.close_containers_to(self.containers.depth() - 1);
2343 }
2344
2345 if let Some(nested_marker) = is_content_nested_bullet_marker(
2347 line,
2348 marker_match.marker_len,
2349 marker_match.spaces_after_bytes,
2350 ) {
2351 let list_item = ListItemEmissionInput {
2352 content: line,
2353 marker_len: marker_match.marker_len,
2354 spaces_after_cols: marker_match.spaces_after_cols,
2355 spaces_after_bytes: marker_match.spaces_after_bytes,
2356 indent_cols,
2357 indent_bytes,
2358 virtual_marker_space: marker_match.virtual_marker_space,
2359 };
2360 lists::add_list_item_with_nested_empty_list(
2361 &mut self.containers,
2362 &mut self.builder,
2363 &list_item,
2364 nested_marker,
2365 );
2366 } else {
2367 let list_item = ListItemEmissionInput {
2368 content: line,
2369 marker_len: marker_match.marker_len,
2370 spaces_after_cols: marker_match.spaces_after_cols,
2371 spaces_after_bytes: marker_match.spaces_after_bytes,
2372 indent_cols,
2373 indent_bytes,
2374 virtual_marker_space: marker_match.virtual_marker_space,
2375 };
2376 lists::add_list_item(
2377 &mut self.containers,
2378 &mut self.builder,
2379 &list_item,
2380 self.config,
2381 );
2382 }
2383 self.pos += 1;
2384 return true;
2385 }
2386 }
2387 }
2388
2389 self.parse_inner_content(line, None)
2391 }
2392
2393 fn content_container_indent_to_strip(&self) -> usize {
2395 self.containers
2396 .stack
2397 .iter()
2398 .filter_map(|c| match c {
2399 Container::FootnoteDefinition { content_col, .. } => Some(*content_col),
2400 Container::Definition { content_col, .. } => Some(*content_col),
2401 _ => None,
2402 })
2403 .sum()
2404 }
2405
2406 fn parse_inner_content(&mut self, content: &str, line_to_append: Option<&str>) -> bool {
2412 log::trace!(
2413 "parse_inner_content [{}]: depth={}, last={:?}, content={:?}",
2414 self.pos,
2415 self.containers.depth(),
2416 self.containers.last(),
2417 content.trim_end()
2418 );
2419 let content_indent = self.content_container_indent_to_strip();
2422 let (stripped_content, indent_to_emit) = if content_indent > 0 {
2423 let (indent_cols, _) = leading_indent(content);
2424 if indent_cols >= content_indent {
2425 let idx = byte_index_at_column(content, content_indent);
2426 (&content[idx..], Some(&content[..idx]))
2427 } else {
2428 let trimmed_start = content.trim_start();
2430 let ws_len = content.len() - trimmed_start.len();
2431 if ws_len > 0 {
2432 (trimmed_start, Some(&content[..ws_len]))
2433 } else {
2434 (content, None)
2435 }
2436 }
2437 } else {
2438 (content, None)
2439 };
2440
2441 if self.config.extensions.alerts
2442 && self.current_blockquote_depth() > 0
2443 && !self.in_active_alert()
2444 && !self.is_paragraph_open()
2445 && let Some(marker) = Self::alert_marker_from_content(stripped_content)
2446 {
2447 let (_, newline_str) = strip_newline(stripped_content);
2448 self.builder.start_node(SyntaxKind::ALERT.into());
2449 self.builder.token(SyntaxKind::ALERT_MARKER.into(), marker);
2450 if !newline_str.is_empty() {
2451 self.builder.token(SyntaxKind::NEWLINE.into(), newline_str);
2452 }
2453 self.containers.push(Container::Alert {
2454 blockquote_depth: self.current_blockquote_depth(),
2455 });
2456 self.pos += 1;
2457 return true;
2458 }
2459
2460 if matches!(self.containers.last(), Some(Container::Definition { .. })) {
2464 let is_definition_marker =
2465 definition_lists::try_parse_definition_marker(stripped_content).is_some()
2466 && !stripped_content.starts_with(':');
2467 if content_indent == 0 && is_definition_marker {
2468 } else {
2470 let policy = ContinuationPolicy::new(self.config, &self.block_registry);
2471
2472 if policy.definition_plain_can_continue(
2473 stripped_content,
2474 content,
2475 content_indent,
2476 &BlockContext {
2477 content: stripped_content,
2478 has_blank_before: self.pos == 0 || is_blank_line(self.lines[self.pos - 1]),
2479 has_blank_before_strict: self.pos == 0
2480 || is_blank_line(self.lines[self.pos - 1]),
2481 at_document_start: self.pos == 0 && self.current_blockquote_depth() == 0,
2482 in_fenced_div: self.in_fenced_div(),
2483 blockquote_depth: self.current_blockquote_depth(),
2484 config: self.config,
2485 content_indent,
2486 indent_to_emit: None,
2487 list_indent_info: None,
2488 in_list: lists::in_list(&self.containers),
2489 in_marker_only_list_item: matches!(
2490 self.containers.last(),
2491 Some(Container::ListItem {
2492 marker_only: true,
2493 ..
2494 })
2495 ),
2496 paragraph_open: self.is_paragraph_open(),
2497 next_line: if self.pos + 1 < self.lines.len() {
2498 Some(self.lines[self.pos + 1])
2499 } else {
2500 None
2501 },
2502 },
2503 &self.lines,
2504 self.pos,
2505 ) {
2506 let content_line = stripped_content;
2507 let (text_without_newline, newline_str) = strip_newline(content_line);
2508 let indent_prefix = if !text_without_newline.trim().is_empty() {
2509 indent_to_emit.unwrap_or("")
2510 } else {
2511 ""
2512 };
2513 let content_line = format!("{}{}", indent_prefix, text_without_newline);
2514
2515 if let Some(Container::Definition {
2516 plain_open,
2517 plain_buffer,
2518 ..
2519 }) = self.containers.stack.last_mut()
2520 {
2521 let line_with_newline = if !newline_str.is_empty() {
2522 format!("{}{}", content_line, newline_str)
2523 } else {
2524 content_line
2525 };
2526 plain_buffer.push_line(line_with_newline);
2527 *plain_open = true;
2528 }
2529
2530 self.pos += 1;
2531 return true;
2532 }
2533 }
2534 }
2535
2536 if content_indent > 0 {
2539 let (bq_depth, inner_content) = count_blockquote_markers(stripped_content);
2540 let current_bq_depth = self.current_blockquote_depth();
2541 let in_footnote_definition = self
2542 .containers
2543 .stack
2544 .iter()
2545 .any(|container| matches!(container, Container::FootnoteDefinition { .. }));
2546
2547 if bq_depth > 0 {
2548 if in_footnote_definition
2549 && self.config.extensions.blank_before_blockquote
2550 && current_bq_depth == 0
2551 && !blockquotes::can_start_blockquote(self.pos, &self.lines)
2552 {
2553 } else {
2557 self.emit_buffered_plain_if_needed();
2560 self.emit_list_item_buffer_if_needed();
2561
2562 self.close_paragraph_if_open();
2565
2566 if bq_depth > current_bq_depth {
2567 let marker_info = parse_blockquote_marker_info(stripped_content);
2568
2569 for level in current_bq_depth..bq_depth {
2571 self.builder.start_node(SyntaxKind::BLOCK_QUOTE.into());
2572
2573 if level == current_bq_depth
2574 && let Some(indent_str) = indent_to_emit
2575 {
2576 self.builder
2577 .token(SyntaxKind::WHITESPACE.into(), indent_str);
2578 }
2579
2580 if let Some(info) = marker_info.get(level) {
2581 blockquotes::emit_one_blockquote_marker(
2582 &mut self.builder,
2583 info.leading_spaces,
2584 info.has_trailing_space,
2585 );
2586 }
2587
2588 self.containers.push(Container::BlockQuote {});
2589 }
2590 } else if bq_depth < current_bq_depth {
2591 self.close_blockquotes_to_depth(bq_depth);
2592 } else {
2593 let marker_info = parse_blockquote_marker_info(stripped_content);
2595 self.emit_blockquote_markers(&marker_info, bq_depth);
2596 }
2597
2598 return self.parse_inner_content(inner_content, Some(inner_content));
2599 }
2600 }
2601 }
2602
2603 let content = stripped_content;
2605
2606 if self.is_paragraph_open()
2607 && (paragraphs::has_open_inline_math_environment(&self.containers)
2608 || paragraphs::has_open_display_math_dollars(&self.containers))
2609 {
2610 paragraphs::append_paragraph_line(
2611 &mut self.containers,
2612 &mut self.builder,
2613 line_to_append.unwrap_or(self.lines[self.pos]),
2614 self.config,
2615 );
2616 self.pos += 1;
2617 return true;
2618 }
2619
2620 use super::blocks::lists;
2624 use super::blocks::paragraphs;
2625 let list_indent_info = if lists::in_list(&self.containers) {
2626 let content_col = paragraphs::current_content_col(&self.containers);
2627 if content_col > 0 {
2628 Some(super::block_dispatcher::ListIndentInfo { content_col })
2629 } else {
2630 None
2631 }
2632 } else {
2633 None
2634 };
2635
2636 let next_line = if self.pos + 1 < self.lines.len() {
2637 Some(count_blockquote_markers(self.lines[self.pos + 1]).1)
2640 } else {
2641 None
2642 };
2643
2644 let current_bq_depth = self.current_blockquote_depth();
2645 if let Some(alert_bq_depth) = self.active_alert_blockquote_depth()
2646 && current_bq_depth < alert_bq_depth
2647 {
2648 while matches!(self.containers.last(), Some(Container::Alert { .. })) {
2649 self.close_containers_to(self.containers.depth() - 1);
2650 }
2651 }
2652
2653 let dispatcher_ctx = BlockContext {
2654 content,
2655 has_blank_before: false, has_blank_before_strict: false, at_document_start: false, in_fenced_div: self.in_fenced_div(),
2659 blockquote_depth: current_bq_depth,
2660 config: self.config,
2661 content_indent,
2662 indent_to_emit,
2663 list_indent_info,
2664 in_list: lists::in_list(&self.containers),
2665 in_marker_only_list_item: matches!(
2666 self.containers.last(),
2667 Some(Container::ListItem {
2668 marker_only: true,
2669 ..
2670 })
2671 ),
2672 paragraph_open: self.is_paragraph_open(),
2673 next_line,
2674 };
2675
2676 let mut dispatcher_ctx = dispatcher_ctx;
2679
2680 if self.try_fold_list_item_buffer_into_setext(stripped_content) {
2684 return true;
2685 }
2686
2687 let dispatcher_match =
2690 self.block_registry
2691 .detect_prepared(&dispatcher_ctx, &self.lines, self.pos);
2692
2693 let after_metadata_block = std::mem::replace(&mut self.after_metadata_block, false);
2699 let has_blank_before = if self.pos == 0 || after_metadata_block {
2700 true
2701 } else {
2702 let prev_line = self.lines[self.pos - 1];
2703 let (prev_bq_depth, prev_inner) = count_blockquote_markers(prev_line);
2704 let (prev_inner_no_nl, _) = strip_newline(prev_inner);
2705 let prev_is_fenced_div_open = self.config.extensions.fenced_divs
2706 && fenced_divs::try_parse_div_fence_open(
2707 strip_n_blockquote_markers(prev_inner_no_nl, prev_bq_depth).trim_start(),
2708 )
2709 .is_some();
2710
2711 let prev_line_blank = is_blank_line(prev_line);
2712 prev_line_blank
2713 || prev_is_fenced_div_open
2714 || matches!(self.containers.last(), Some(Container::BlockQuote { .. }))
2715 || !self.previous_block_requires_blank_before_heading()
2716 };
2717
2718 let at_document_start = self.pos == 0 && current_bq_depth == 0;
2721
2722 let prev_line_blank = if self.pos > 0 {
2723 let prev_line = self.lines[self.pos - 1];
2724 let (prev_bq_depth, prev_inner) = count_blockquote_markers(prev_line);
2725 is_blank_line(prev_line) || (prev_bq_depth > 0 && is_blank_line(prev_inner))
2726 } else {
2727 false
2728 };
2729 let has_blank_before_strict = at_document_start || prev_line_blank;
2730
2731 dispatcher_ctx.has_blank_before = has_blank_before;
2732 dispatcher_ctx.has_blank_before_strict = has_blank_before_strict;
2733 dispatcher_ctx.at_document_start = at_document_start;
2734
2735 let dispatcher_match =
2736 if dispatcher_ctx.has_blank_before || dispatcher_ctx.at_document_start {
2737 self.block_registry
2739 .detect_prepared(&dispatcher_ctx, &self.lines, self.pos)
2740 } else {
2741 dispatcher_match
2742 };
2743
2744 if has_blank_before {
2745 if let Some(env_name) = extract_environment_name(content)
2746 && is_inline_math_environment(env_name)
2747 {
2748 if !self.is_paragraph_open() {
2749 paragraphs::start_paragraph_if_needed(&mut self.containers, &mut self.builder);
2750 }
2751 paragraphs::append_paragraph_line(
2752 &mut self.containers,
2753 &mut self.builder,
2754 line_to_append.unwrap_or(self.lines[self.pos]),
2755 self.config,
2756 );
2757 self.pos += 1;
2758 return true;
2759 }
2760
2761 if let Some(block_match) = dispatcher_match.as_ref() {
2762 let detection = block_match.detection;
2763
2764 match detection {
2765 BlockDetectionResult::YesCanInterrupt => {
2766 self.emit_list_item_buffer_if_needed();
2767 if self.is_paragraph_open() {
2768 self.close_containers_to(self.containers.depth() - 1);
2769 }
2770 }
2771 BlockDetectionResult::Yes => {
2772 self.prepare_for_block_element();
2773 }
2774 BlockDetectionResult::No => unreachable!(),
2775 }
2776
2777 if matches!(block_match.effect, BlockEffect::CloseFencedDiv) {
2778 self.close_containers_to_fenced_div();
2779 }
2780
2781 if matches!(block_match.effect, BlockEffect::OpenFootnoteDefinition) {
2782 self.close_open_footnote_definition();
2783 }
2784
2785 let lines_consumed = self.block_registry.parse_prepared(
2786 block_match,
2787 &dispatcher_ctx,
2788 &mut self.builder,
2789 &self.lines,
2790 self.pos,
2791 );
2792
2793 if matches!(
2794 self.block_registry.parser_name(block_match),
2795 "yaml_metadata" | "pandoc_title_block" | "mmd_title_block"
2796 ) {
2797 self.after_metadata_block = true;
2798 }
2799
2800 match block_match.effect {
2801 BlockEffect::None => {}
2802 BlockEffect::OpenFencedDiv => {
2803 self.containers.push(Container::FencedDiv {});
2804 }
2805 BlockEffect::CloseFencedDiv => {
2806 self.close_fenced_div();
2807 }
2808 BlockEffect::OpenFootnoteDefinition => {
2809 self.handle_footnote_open_effect(block_match, content);
2810 }
2811 BlockEffect::OpenList => {
2812 self.handle_list_open_effect(block_match, content, indent_to_emit);
2813 }
2814 BlockEffect::OpenDefinitionList => {
2815 self.handle_definition_list_effect(block_match, content, indent_to_emit);
2816 }
2817 BlockEffect::OpenBlockQuote => {
2818 }
2820 }
2821
2822 if lines_consumed == 0 {
2823 log::warn!(
2824 "block parser made no progress at line {} (parser={})",
2825 self.pos + 1,
2826 self.block_registry.parser_name(block_match)
2827 );
2828 return false;
2829 }
2830
2831 self.pos += lines_consumed;
2832 return true;
2833 }
2834 } else if let Some(block_match) = dispatcher_match.as_ref() {
2835 let parser_name = self.block_registry.parser_name(block_match);
2838 match block_match.detection {
2839 BlockDetectionResult::YesCanInterrupt => {
2840 if matches!(block_match.effect, BlockEffect::OpenFencedDiv)
2841 && self.is_paragraph_open()
2842 {
2843 if !self.is_paragraph_open() {
2845 paragraphs::start_paragraph_if_needed(
2846 &mut self.containers,
2847 &mut self.builder,
2848 );
2849 }
2850 paragraphs::append_paragraph_line(
2851 &mut self.containers,
2852 &mut self.builder,
2853 line_to_append.unwrap_or(self.lines[self.pos]),
2854 self.config,
2855 );
2856 self.pos += 1;
2857 return true;
2858 }
2859
2860 if matches!(block_match.effect, BlockEffect::OpenList)
2861 && self.is_paragraph_open()
2862 && !lists::in_list(&self.containers)
2863 && self.content_container_indent_to_strip() == 0
2864 {
2865 let allow_interrupt =
2871 self.config.dialect == crate::options::Dialect::CommonMark && {
2872 use super::block_dispatcher::ListPrepared;
2873 use super::blocks::lists::OrderedMarker;
2874 let prepared = block_match
2875 .payload
2876 .as_ref()
2877 .and_then(|p| p.downcast_ref::<ListPrepared>());
2878 match prepared.map(|p| &p.marker) {
2879 Some(ListMarker::Bullet(_)) => true,
2880 Some(ListMarker::Ordered(OrderedMarker::Decimal {
2881 number,
2882 ..
2883 })) => number == "1",
2884 _ => false,
2885 }
2886 };
2887 if !allow_interrupt {
2888 paragraphs::append_paragraph_line(
2889 &mut self.containers,
2890 &mut self.builder,
2891 line_to_append.unwrap_or(self.lines[self.pos]),
2892 self.config,
2893 );
2894 self.pos += 1;
2895 return true;
2896 }
2897 }
2898
2899 if matches!(block_match.effect, BlockEffect::OpenList)
2906 && self.try_lazy_list_continuation(block_match, content)
2907 {
2908 self.pos += 1;
2909 return true;
2910 }
2911
2912 self.emit_list_item_buffer_if_needed();
2913 if self.is_paragraph_open() {
2914 self.close_containers_to(self.containers.depth() - 1);
2915 }
2916
2917 if self.config.dialect == crate::options::Dialect::CommonMark
2924 && !matches!(block_match.effect, BlockEffect::OpenList)
2925 {
2926 let (indent_cols, _) = leading_indent(content);
2927 self.close_lists_above_indent(indent_cols);
2928 }
2929 }
2930 BlockDetectionResult::Yes => {
2931 if parser_name == "setext_heading"
2943 && self.is_paragraph_open()
2944 && self.config.dialect == crate::options::Dialect::CommonMark
2945 {
2946 let text_line = self.lines[self.pos];
2947 let underline_line = self.lines[self.pos + 1];
2948 let underline_char = underline_line.trim().chars().next().unwrap_or('=');
2949 let level = if underline_char == '=' { 1 } else { 2 };
2950 self.emit_setext_heading_folding_paragraph(
2951 text_line,
2952 underline_line,
2953 level,
2954 );
2955 self.pos += 2;
2956 return true;
2957 }
2958
2959 if parser_name == "fenced_div_open" && self.is_paragraph_open() {
2962 if !self.is_paragraph_open() {
2963 paragraphs::start_paragraph_if_needed(
2964 &mut self.containers,
2965 &mut self.builder,
2966 );
2967 }
2968 paragraphs::append_paragraph_line(
2969 &mut self.containers,
2970 &mut self.builder,
2971 line_to_append.unwrap_or(self.lines[self.pos]),
2972 self.config,
2973 );
2974 self.pos += 1;
2975 return true;
2976 }
2977
2978 if parser_name == "reference_definition" && self.is_paragraph_open() {
2981 paragraphs::append_paragraph_line(
2982 &mut self.containers,
2983 &mut self.builder,
2984 line_to_append.unwrap_or(self.lines[self.pos]),
2985 self.config,
2986 );
2987 self.pos += 1;
2988 return true;
2989 }
2990 }
2991 BlockDetectionResult::No => unreachable!(),
2992 }
2993
2994 if !matches!(block_match.detection, BlockDetectionResult::No) {
2995 if matches!(block_match.effect, BlockEffect::CloseFencedDiv) {
2996 self.close_containers_to_fenced_div();
2997 }
2998
2999 if matches!(block_match.effect, BlockEffect::OpenFootnoteDefinition) {
3000 self.close_open_footnote_definition();
3001 }
3002
3003 let lines_consumed = self.block_registry.parse_prepared(
3004 block_match,
3005 &dispatcher_ctx,
3006 &mut self.builder,
3007 &self.lines,
3008 self.pos,
3009 );
3010
3011 match block_match.effect {
3012 BlockEffect::None => {}
3013 BlockEffect::OpenFencedDiv => {
3014 self.containers.push(Container::FencedDiv {});
3015 }
3016 BlockEffect::CloseFencedDiv => {
3017 self.close_fenced_div();
3018 }
3019 BlockEffect::OpenFootnoteDefinition => {
3020 self.handle_footnote_open_effect(block_match, content);
3021 }
3022 BlockEffect::OpenList => {
3023 self.handle_list_open_effect(block_match, content, indent_to_emit);
3024 }
3025 BlockEffect::OpenDefinitionList => {
3026 self.handle_definition_list_effect(block_match, content, indent_to_emit);
3027 }
3028 BlockEffect::OpenBlockQuote => {
3029 }
3031 }
3032
3033 if lines_consumed == 0 {
3034 log::warn!(
3035 "block parser made no progress at line {} (parser={})",
3036 self.pos + 1,
3037 self.block_registry.parser_name(block_match)
3038 );
3039 return false;
3040 }
3041
3042 self.pos += lines_consumed;
3043 return true;
3044 }
3045 }
3046
3047 if self.config.extensions.line_blocks
3049 && (has_blank_before || self.pos == 0)
3050 && try_parse_line_block_start(content).is_some()
3051 && try_parse_line_block_start(self.lines[self.pos]).is_some()
3055 {
3056 log::trace!("Parsed line block at line {}", self.pos);
3057 self.close_paragraph_if_open();
3059
3060 let new_pos = parse_line_block(&self.lines, self.pos, &mut self.builder, self.config);
3061 if new_pos > self.pos {
3062 self.pos = new_pos;
3063 return true;
3064 }
3065 }
3066
3067 if matches!(self.containers.last(), Some(Container::ListItem { .. })) {
3070 log::trace!(
3071 "Inside ListItem - buffering content: {:?}",
3072 line_to_append.unwrap_or(self.lines[self.pos]).trim_end()
3073 );
3074 let line = line_to_append.unwrap_or(self.lines[self.pos]);
3076
3077 if let Some(Container::ListItem {
3079 buffer,
3080 marker_only,
3081 ..
3082 }) = self.containers.stack.last_mut()
3083 {
3084 buffer.push_text(line);
3085 if !is_blank_line(line) {
3086 *marker_only = false;
3087 }
3088 }
3089
3090 self.pos += 1;
3091 return true;
3092 }
3093
3094 log::trace!(
3095 "Not in ListItem - creating paragraph for: {:?}",
3096 line_to_append.unwrap_or(self.lines[self.pos]).trim_end()
3097 );
3098 paragraphs::start_paragraph_if_needed(&mut self.containers, &mut self.builder);
3100 let line = line_to_append.unwrap_or(self.lines[self.pos]);
3103 paragraphs::append_paragraph_line(
3104 &mut self.containers,
3105 &mut self.builder,
3106 line,
3107 self.config,
3108 );
3109 self.pos += 1;
3110 true
3111 }
3112
3113 fn fenced_div_container_index(&self) -> Option<usize> {
3114 self.containers
3115 .stack
3116 .iter()
3117 .rposition(|c| matches!(c, Container::FencedDiv { .. }))
3118 }
3119
3120 fn close_containers_to_fenced_div(&mut self) {
3121 if let Some(index) = self.fenced_div_container_index() {
3122 self.close_containers_to(index + 1);
3123 }
3124 }
3125
3126 fn close_fenced_div(&mut self) {
3127 if let Some(index) = self.fenced_div_container_index() {
3128 self.close_containers_to(index);
3129 }
3130 }
3131
3132 fn in_fenced_div(&self) -> bool {
3133 self.containers
3134 .stack
3135 .iter()
3136 .any(|c| matches!(c, Container::FencedDiv { .. }))
3137 }
3138}
3139
3140fn emit_definition_plain_or_heading(
3147 builder: &mut GreenNodeBuilder<'static>,
3148 text: &str,
3149 config: &ParserOptions,
3150) {
3151 let line_without_newline = text
3152 .strip_suffix("\r\n")
3153 .or_else(|| text.strip_suffix('\n'));
3154 if let Some(line) = line_without_newline
3155 && !line.contains('\n')
3156 && !line.contains('\r')
3157 && let Some(level) = try_parse_atx_heading(line)
3158 {
3159 emit_atx_heading(builder, text, level, config);
3160 return;
3161 }
3162
3163 if let Some(first_nl) = text.find('\n') {
3165 let first_line = &text[..first_nl];
3166 let after_first = &text[first_nl + 1..];
3167 if !after_first.is_empty()
3168 && let Some(level) = try_parse_atx_heading(first_line)
3169 {
3170 let heading_bytes = &text[..first_nl + 1];
3171 emit_atx_heading(builder, heading_bytes, level, config);
3172 builder.start_node(SyntaxKind::PLAIN.into());
3173 inline_emission::emit_inlines(builder, after_first, config);
3174 builder.finish_node();
3175 return;
3176 }
3177 }
3178
3179 builder.start_node(SyntaxKind::PLAIN.into());
3180 inline_emission::emit_inlines(builder, text, config);
3181 builder.finish_node();
3182}
3183
3184fn footnote_first_line_term_lookahead(
3193 lines: &[&str],
3194 pos: usize,
3195 content_col: usize,
3196 table_captions_enabled: bool,
3197) -> Option<usize> {
3198 let mut check_pos = pos + 1;
3199 let mut blank_count = 0;
3200 while check_pos < lines.len() {
3201 let line = lines[check_pos];
3202 let (trimmed, _) = strip_newline(line);
3203 if trimmed.trim().is_empty() {
3204 blank_count += 1;
3205 check_pos += 1;
3206 continue;
3207 }
3208 let (line_indent_cols, _) = leading_indent(trimmed);
3209 if line_indent_cols < content_col {
3210 return None;
3211 }
3212 let strip_bytes = byte_index_at_column(trimmed, content_col);
3213 if strip_bytes > trimmed.len() {
3214 return None;
3215 }
3216 let stripped = &trimmed[strip_bytes..];
3217 if let Some((marker, ..)) = definition_lists::try_parse_definition_marker(stripped) {
3218 if marker == ':'
3222 && table_captions_enabled
3223 && super::blocks::tables::is_caption_followed_by_table(lines, check_pos)
3224 {
3225 return None;
3226 }
3227 return Some(blank_count);
3228 }
3229 return None;
3230 }
3231 None
3232}