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::{emit_atx_heading, try_parse_atx_heading};
14use super::blocks::line_blocks;
15use super::blocks::lists;
16use super::blocks::paragraphs;
17use super::blocks::raw_blocks::{extract_environment_name, is_inline_math_environment};
18use super::utils::container_stack;
19use super::utils::helpers::{split_lines_inclusive, strip_newline};
20use super::utils::inline_emission;
21use super::utils::marker_utils;
22use super::utils::text_buffer;
23
24use super::blocks::blockquotes::strip_n_blockquote_markers;
25use super::utils::continuation::ContinuationPolicy;
26use container_stack::{Container, ContainerStack, byte_index_at_column, leading_indent};
27use definition_lists::{emit_definition_marker, emit_term};
28use line_blocks::{parse_line_block, try_parse_line_block_start};
29use lists::{
30 ListItemEmissionInput, ListMarker, is_content_nested_bullet_marker, start_nested_list,
31 try_parse_list_marker,
32};
33use marker_utils::{count_blockquote_markers, parse_blockquote_marker_info};
34use text_buffer::TextBuffer;
35
36const GITHUB_ALERT_MARKERS: [&str; 5] = [
37 "[!TIP]",
38 "[!WARNING]",
39 "[!IMPORTANT]",
40 "[!CAUTION]",
41 "[!NOTE]",
42];
43
44pub struct Parser<'a> {
45 lines: Vec<&'a str>,
46 pos: usize,
47 builder: GreenNodeBuilder<'static>,
48 containers: ContainerStack,
49 config: &'a ParserOptions,
50 block_registry: BlockParserRegistry,
51 after_metadata_block: bool,
55}
56
57impl<'a> Parser<'a> {
58 pub fn new(input: &'a str, config: &'a ParserOptions) -> Self {
59 let lines = split_lines_inclusive(input);
61 Self {
62 lines,
63 pos: 0,
64 builder: GreenNodeBuilder::new(),
65 containers: ContainerStack::new(),
66 config,
67 block_registry: BlockParserRegistry::new(),
68 after_metadata_block: false,
69 }
70 }
71
72 pub fn parse(mut self) -> SyntaxNode {
73 self.parse_document_stack();
74
75 SyntaxNode::new_root(self.builder.finish())
76 }
77
78 fn close_containers_to(&mut self, keep: usize) {
81 while self.containers.depth() > keep {
83 match self.containers.stack.last() {
84 Some(Container::ListItem { buffer, .. }) if !buffer.is_empty() => {
86 let buffer_clone = buffer.clone();
88
89 log::trace!(
90 "Closing ListItem with buffer (is_empty={}, segment_count={})",
91 buffer_clone.is_empty(),
92 buffer_clone.segment_count()
93 );
94
95 let parent_list_is_loose = self
99 .containers
100 .stack
101 .iter()
102 .rev()
103 .find_map(|c| match c {
104 Container::List {
105 has_blank_between_items,
106 ..
107 } => Some(*has_blank_between_items),
108 _ => None,
109 })
110 .unwrap_or(false);
111
112 let use_paragraph =
113 parent_list_is_loose || buffer_clone.has_blank_lines_between_content();
114
115 log::trace!(
116 "Emitting ListItem buffer: use_paragraph={} (parent_list_is_loose={}, item_has_blanks={})",
117 use_paragraph,
118 parent_list_is_loose,
119 buffer_clone.has_blank_lines_between_content()
120 );
121
122 self.containers.stack.pop();
124 buffer_clone.emit_as_block(&mut self.builder, use_paragraph, self.config);
126 self.builder.finish_node(); }
128 Some(Container::ListItem { .. }) => {
130 log::trace!("Closing empty ListItem (no buffer content)");
131 self.containers.stack.pop();
133 self.builder.finish_node();
134 }
135 Some(Container::Paragraph { buffer, .. }) if !buffer.is_empty() => {
137 let buffer_clone = buffer.clone();
139 self.containers.stack.pop();
141 buffer_clone.emit_with_inlines(&mut self.builder, self.config);
143 self.builder.finish_node();
144 }
145 Some(Container::Paragraph { .. }) => {
147 self.containers.stack.pop();
149 self.builder.finish_node();
150 }
151 Some(Container::Definition {
153 plain_open: true,
154 plain_buffer,
155 ..
156 }) if !plain_buffer.is_empty() => {
157 let text = plain_buffer.get_accumulated_text();
158 let line_without_newline = text
159 .strip_suffix("\r\n")
160 .or_else(|| text.strip_suffix('\n'));
161 if let Some(line) = line_without_newline
162 && !line.contains('\n')
163 && !line.contains('\r')
164 && let Some(level) = try_parse_atx_heading(line)
165 {
166 emit_atx_heading(&mut self.builder, &text, level, self.config);
167 } else {
168 self.builder.start_node(SyntaxKind::PLAIN.into());
170 inline_emission::emit_inlines(&mut self.builder, &text, self.config);
171 self.builder.finish_node();
172 }
173
174 if let Some(Container::Definition {
176 plain_open,
177 plain_buffer,
178 ..
179 }) = self.containers.stack.last_mut()
180 {
181 plain_buffer.clear();
182 *plain_open = false;
183 }
184
185 self.containers.stack.pop();
187 self.builder.finish_node();
188 }
189 Some(Container::Definition {
191 plain_open: true, ..
192 }) => {
193 if let Some(Container::Definition {
195 plain_open,
196 plain_buffer,
197 ..
198 }) = self.containers.stack.last_mut()
199 {
200 plain_buffer.clear();
201 *plain_open = false;
202 }
203
204 self.containers.stack.pop();
206 self.builder.finish_node();
207 }
208 _ => {
210 self.containers.stack.pop();
211 self.builder.finish_node();
212 }
213 }
214 }
215 }
216
217 fn emit_buffered_plain_if_needed(&mut self) {
220 if let Some(Container::Definition {
222 plain_open: true,
223 plain_buffer,
224 ..
225 }) = self.containers.stack.last()
226 && !plain_buffer.is_empty()
227 {
228 let text = plain_buffer.get_accumulated_text();
229 let line_without_newline = text
230 .strip_suffix("\r\n")
231 .or_else(|| text.strip_suffix('\n'));
232 if let Some(line) = line_without_newline
233 && !line.contains('\n')
234 && !line.contains('\r')
235 && let Some(level) = try_parse_atx_heading(line)
236 {
237 emit_atx_heading(&mut self.builder, &text, level, self.config);
238 } else {
239 self.builder.start_node(SyntaxKind::PLAIN.into());
241 inline_emission::emit_inlines(&mut self.builder, &text, self.config);
242 self.builder.finish_node();
243 }
244 }
245
246 if let Some(Container::Definition {
248 plain_open,
249 plain_buffer,
250 ..
251 }) = self.containers.stack.last_mut()
252 && *plain_open
253 {
254 plain_buffer.clear();
255 *plain_open = false;
256 }
257 }
258
259 fn close_blockquotes_to_depth(&mut self, target_depth: usize) {
264 let mut current = self.current_blockquote_depth();
265 while current > target_depth {
266 while !matches!(self.containers.last(), Some(Container::BlockQuote { .. })) {
267 if self.containers.depth() == 0 {
268 break;
269 }
270 self.close_containers_to(self.containers.depth() - 1);
271 }
272 if matches!(self.containers.last(), Some(Container::BlockQuote { .. })) {
273 self.close_containers_to(self.containers.depth() - 1);
274 current -= 1;
275 } else {
276 break;
277 }
278 }
279 }
280
281 fn active_alert_blockquote_depth(&self) -> Option<usize> {
282 self.containers.stack.iter().rev().find_map(|c| match c {
283 Container::Alert { blockquote_depth } => Some(*blockquote_depth),
284 _ => None,
285 })
286 }
287
288 fn in_active_alert(&self) -> bool {
289 self.active_alert_blockquote_depth().is_some()
290 }
291
292 fn previous_block_requires_blank_before_heading(&self) -> bool {
293 matches!(
294 self.containers.last(),
295 Some(Container::Paragraph { .. })
296 | Some(Container::ListItem { .. })
297 | Some(Container::Definition { .. })
298 | Some(Container::DefinitionItem { .. })
299 | Some(Container::FootnoteDefinition { .. })
300 )
301 }
302
303 fn alert_marker_from_content(content: &str) -> Option<&'static str> {
304 let (without_newline, _) = strip_newline(content);
305 let trimmed = without_newline.trim();
306 GITHUB_ALERT_MARKERS
307 .into_iter()
308 .find(|marker| *marker == trimmed)
309 }
310
311 fn emit_list_item_buffer_if_needed(&mut self) {
314 if let Some(Container::ListItem { buffer, .. }) = self.containers.stack.last_mut()
315 && !buffer.is_empty()
316 {
317 let buffer_clone = buffer.clone();
318 buffer.clear();
319 let use_paragraph = buffer_clone.has_blank_lines_between_content();
320 buffer_clone.emit_as_block(&mut self.builder, use_paragraph, self.config);
321 }
322 }
323
324 fn is_paragraph_open(&self) -> bool {
326 matches!(self.containers.last(), Some(Container::Paragraph { .. }))
327 }
328
329 fn close_paragraph_if_open(&mut self) {
331 if self.is_paragraph_open() {
332 self.close_containers_to(self.containers.depth() - 1);
333 }
334 }
335
336 fn prepare_for_block_element(&mut self) {
339 self.emit_list_item_buffer_if_needed();
340 self.close_paragraph_if_open();
341 }
342
343 fn handle_footnote_open_effect(
344 &mut self,
345 block_match: &super::block_dispatcher::PreparedBlockMatch,
346 content: &str,
347 ) {
348 let content_start = block_match
349 .payload
350 .as_ref()
351 .and_then(|p| p.downcast_ref::<super::block_dispatcher::FootnoteDefinitionPrepared>())
352 .map(|p| p.content_start)
353 .unwrap_or(0);
354
355 while matches!(
356 self.containers.last(),
357 Some(Container::FootnoteDefinition { .. })
358 ) {
359 self.close_containers_to(self.containers.depth() - 1);
360 }
361
362 let content_col = 4;
363 self.containers
364 .push(Container::FootnoteDefinition { content_col });
365
366 if content_start > 0 {
367 let first_line_content = &content[content_start..];
368 if !first_line_content.trim().is_empty() {
369 paragraphs::start_paragraph_if_needed(&mut self.containers, &mut self.builder);
370 paragraphs::append_paragraph_line(
371 &mut self.containers,
372 &mut self.builder,
373 first_line_content,
374 self.config,
375 );
376 } else {
377 let (_, newline_str) = strip_newline(content);
378 if !newline_str.is_empty() {
379 self.builder.token(SyntaxKind::NEWLINE.into(), newline_str);
380 }
381 }
382 }
383 }
384
385 fn handle_list_open_effect(
386 &mut self,
387 block_match: &super::block_dispatcher::PreparedBlockMatch,
388 content: &str,
389 indent_to_emit: Option<&str>,
390 ) {
391 use super::block_dispatcher::ListPrepared;
392
393 let prepared = block_match
394 .payload
395 .as_ref()
396 .and_then(|p| p.downcast_ref::<ListPrepared>());
397 let Some(prepared) = prepared else {
398 return;
399 };
400
401 if prepared.indent_cols >= 4 && !lists::in_list(&self.containers) {
402 paragraphs::start_paragraph_if_needed(&mut self.containers, &mut self.builder);
403 paragraphs::append_paragraph_line(
404 &mut self.containers,
405 &mut self.builder,
406 content,
407 self.config,
408 );
409 return;
410 }
411
412 if self.is_paragraph_open() {
413 if !block_match.detection.eq(&BlockDetectionResult::Yes) {
414 paragraphs::append_paragraph_line(
415 &mut self.containers,
416 &mut self.builder,
417 content,
418 self.config,
419 );
420 return;
421 }
422 self.close_containers_to(self.containers.depth() - 1);
423 }
424
425 if matches!(
426 self.containers.last(),
427 Some(Container::Definition {
428 plain_open: true,
429 ..
430 })
431 ) {
432 self.emit_buffered_plain_if_needed();
433 }
434
435 let matched_level = lists::find_matching_list_level(
436 &self.containers,
437 &prepared.marker,
438 prepared.indent_cols,
439 );
440 let list_item = ListItemEmissionInput {
441 content,
442 marker_len: prepared.marker_len,
443 spaces_after_cols: prepared.spaces_after_cols,
444 spaces_after_bytes: prepared.spaces_after,
445 indent_cols: prepared.indent_cols,
446 indent_bytes: prepared.indent_bytes,
447 };
448 let current_content_col = paragraphs::current_content_col(&self.containers);
449 let deep_ordered_matched_level = matched_level
450 .and_then(|level| self.containers.stack.get(level).map(|c| (level, c)))
451 .and_then(|(level, container)| match container {
452 Container::List {
453 marker: list_marker,
454 base_indent_cols,
455 ..
456 } if matches!(
457 (&prepared.marker, list_marker),
458 (ListMarker::Ordered(_), ListMarker::Ordered(_))
459 ) && prepared.indent_cols >= 4
460 && *base_indent_cols >= 4
461 && prepared.indent_cols.abs_diff(*base_indent_cols) <= 3 =>
462 {
463 Some(level)
464 }
465 _ => None,
466 });
467
468 if deep_ordered_matched_level.is_none()
469 && current_content_col > 0
470 && prepared.indent_cols >= current_content_col
471 {
472 if let Some(level) = matched_level
473 && let Some(Container::List {
474 base_indent_cols, ..
475 }) = self.containers.stack.get(level)
476 && prepared.indent_cols == *base_indent_cols
477 {
478 let num_parent_lists = self.containers.stack[..level]
479 .iter()
480 .filter(|c| matches!(c, Container::List { .. }))
481 .count();
482
483 if num_parent_lists > 0 {
484 self.close_containers_to(level + 1);
485
486 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
487 self.close_containers_to(self.containers.depth() - 1);
488 }
489 if matches!(self.containers.last(), Some(Container::ListItem { .. })) {
490 self.close_containers_to(self.containers.depth() - 1);
491 }
492
493 if let Some(indent_str) = indent_to_emit {
494 self.builder
495 .token(SyntaxKind::WHITESPACE.into(), indent_str);
496 }
497
498 if let Some(nested_marker) = prepared.nested_marker {
499 lists::add_list_item_with_nested_empty_list(
500 &mut self.containers,
501 &mut self.builder,
502 &list_item,
503 nested_marker,
504 );
505 } else {
506 lists::add_list_item(&mut self.containers, &mut self.builder, &list_item);
507 }
508 return;
509 }
510 }
511
512 self.emit_list_item_buffer_if_needed();
513
514 start_nested_list(
515 &mut self.containers,
516 &mut self.builder,
517 &prepared.marker,
518 &list_item,
519 indent_to_emit,
520 );
521 return;
522 }
523
524 if let Some(level) = matched_level {
525 self.close_containers_to(level + 1);
526
527 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
528 self.close_containers_to(self.containers.depth() - 1);
529 }
530 if matches!(self.containers.last(), Some(Container::ListItem { .. })) {
531 self.close_containers_to(self.containers.depth() - 1);
532 }
533
534 if let Some(indent_str) = indent_to_emit {
535 self.builder
536 .token(SyntaxKind::WHITESPACE.into(), indent_str);
537 }
538
539 if let Some(nested_marker) = prepared.nested_marker {
540 lists::add_list_item_with_nested_empty_list(
541 &mut self.containers,
542 &mut self.builder,
543 &list_item,
544 nested_marker,
545 );
546 } else {
547 lists::add_list_item(&mut self.containers, &mut self.builder, &list_item);
548 }
549 return;
550 }
551
552 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
553 self.close_containers_to(self.containers.depth() - 1);
554 }
555 while matches!(self.containers.last(), Some(Container::ListItem { .. })) {
556 self.close_containers_to(self.containers.depth() - 1);
557 }
558 while matches!(self.containers.last(), Some(Container::List { .. })) {
559 self.close_containers_to(self.containers.depth() - 1);
560 }
561
562 self.builder.start_node(SyntaxKind::LIST.into());
563 if let Some(indent_str) = indent_to_emit {
564 self.builder
565 .token(SyntaxKind::WHITESPACE.into(), indent_str);
566 }
567 self.containers.push(Container::List {
568 marker: prepared.marker.clone(),
569 base_indent_cols: prepared.indent_cols,
570 has_blank_between_items: false,
571 });
572
573 if let Some(nested_marker) = prepared.nested_marker {
574 lists::add_list_item_with_nested_empty_list(
575 &mut self.containers,
576 &mut self.builder,
577 &list_item,
578 nested_marker,
579 );
580 } else {
581 lists::add_list_item(&mut self.containers, &mut self.builder, &list_item);
582 }
583 }
584
585 fn handle_definition_list_effect(
586 &mut self,
587 block_match: &super::block_dispatcher::PreparedBlockMatch,
588 content: &str,
589 indent_to_emit: Option<&str>,
590 ) {
591 use super::block_dispatcher::DefinitionPrepared;
592
593 let prepared = block_match
594 .payload
595 .as_ref()
596 .and_then(|p| p.downcast_ref::<DefinitionPrepared>());
597 let Some(prepared) = prepared else {
598 return;
599 };
600
601 match prepared {
602 DefinitionPrepared::Definition {
603 marker_char,
604 indent,
605 spaces_after,
606 spaces_after_cols,
607 has_content,
608 } => {
609 self.emit_buffered_plain_if_needed();
610
611 while matches!(self.containers.last(), Some(Container::ListItem { .. })) {
612 self.close_containers_to(self.containers.depth() - 1);
613 }
614 while matches!(self.containers.last(), Some(Container::List { .. })) {
615 self.close_containers_to(self.containers.depth() - 1);
616 }
617
618 if matches!(self.containers.last(), Some(Container::Definition { .. })) {
619 self.close_containers_to(self.containers.depth() - 1);
620 }
621
622 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
623 self.close_containers_to(self.containers.depth() - 1);
624 }
625
626 if definition_lists::in_definition_list(&self.containers)
630 && !matches!(
631 self.containers.last(),
632 Some(Container::DefinitionItem { .. })
633 )
634 {
635 self.builder.start_node(SyntaxKind::DEFINITION_ITEM.into());
636 self.containers.push(Container::DefinitionItem {});
637 }
638
639 if !definition_lists::in_definition_list(&self.containers) {
640 self.builder.start_node(SyntaxKind::DEFINITION_LIST.into());
641 self.containers.push(Container::DefinitionList {});
642 }
643
644 if !matches!(
645 self.containers.last(),
646 Some(Container::DefinitionItem { .. })
647 ) {
648 self.builder.start_node(SyntaxKind::DEFINITION_ITEM.into());
649 self.containers.push(Container::DefinitionItem {});
650 }
651
652 self.builder.start_node(SyntaxKind::DEFINITION.into());
653
654 if let Some(indent_str) = indent_to_emit {
655 self.builder
656 .token(SyntaxKind::WHITESPACE.into(), indent_str);
657 }
658
659 emit_definition_marker(&mut self.builder, *marker_char, *indent);
660 let indent_bytes = byte_index_at_column(content, *indent);
661 if *spaces_after > 0 {
662 let space_start = indent_bytes + 1;
663 let space_end = space_start + *spaces_after;
664 if space_end <= content.len() {
665 self.builder.token(
666 SyntaxKind::WHITESPACE.into(),
667 &content[space_start..space_end],
668 );
669 }
670 }
671
672 if !*has_content {
673 let current_line = self.lines[self.pos];
674 let (_, newline_str) = strip_newline(current_line);
675 if !newline_str.is_empty() {
676 self.builder.token(SyntaxKind::NEWLINE.into(), newline_str);
677 }
678 }
679
680 let content_col = *indent + 1 + *spaces_after_cols;
681 let content_start_bytes = indent_bytes + 1 + *spaces_after;
682 let after_marker_and_spaces = content.get(content_start_bytes..).unwrap_or("");
683 let mut plain_buffer = TextBuffer::new();
684 let mut definition_pushed = false;
685
686 if *has_content {
687 let current_line = self.lines[self.pos];
688 let (trimmed_line, _) = strip_newline(current_line);
689
690 let content_start = content_start_bytes.min(trimmed_line.len());
691 let content_slice = &trimmed_line[content_start..];
692 let content_line = ¤t_line[content_start_bytes.min(current_line.len())..];
693
694 let (blockquote_depth, inner_blockquote_content) =
695 count_blockquote_markers(content_line);
696
697 let should_start_list_from_first_line = self
698 .lines
699 .get(self.pos + 1)
700 .map(|next_line| {
701 let (next_without_newline, _) = strip_newline(next_line);
702 if next_without_newline.trim().is_empty() {
703 return false;
704 }
705
706 let (next_indent_cols, _) = leading_indent(next_without_newline);
707 next_indent_cols >= content_col
708 })
709 .unwrap_or(false);
710
711 if blockquote_depth > 0 {
712 self.containers.push(Container::Definition {
713 content_col,
714 plain_open: false,
715 plain_buffer: TextBuffer::new(),
716 });
717 definition_pushed = true;
718
719 let marker_info = parse_blockquote_marker_info(content_line);
720 for level in 0..blockquote_depth {
721 self.builder.start_node(SyntaxKind::BLOCK_QUOTE.into());
722 if let Some(info) = marker_info.get(level) {
723 blockquotes::emit_one_blockquote_marker(
724 &mut self.builder,
725 info.leading_spaces,
726 info.has_trailing_space,
727 );
728 }
729 self.containers.push(Container::BlockQuote {});
730 }
731
732 if !inner_blockquote_content.trim().is_empty() {
733 paragraphs::start_paragraph_if_needed(
734 &mut self.containers,
735 &mut self.builder,
736 );
737 paragraphs::append_paragraph_line(
738 &mut self.containers,
739 &mut self.builder,
740 inner_blockquote_content,
741 self.config,
742 );
743 }
744 } else if let Some(marker_match) =
745 try_parse_list_marker(content_slice, self.config)
746 && should_start_list_from_first_line
747 {
748 self.containers.push(Container::Definition {
749 content_col,
750 plain_open: false,
751 plain_buffer: TextBuffer::new(),
752 });
753 definition_pushed = true;
754
755 let (indent_cols, indent_bytes) = leading_indent(content_line);
756 self.builder.start_node(SyntaxKind::LIST.into());
757 self.containers.push(Container::List {
758 marker: marker_match.marker.clone(),
759 base_indent_cols: indent_cols,
760 has_blank_between_items: false,
761 });
762
763 let list_item = ListItemEmissionInput {
764 content: content_line,
765 marker_len: marker_match.marker_len,
766 spaces_after_cols: marker_match.spaces_after_cols,
767 spaces_after_bytes: marker_match.spaces_after_bytes,
768 indent_cols,
769 indent_bytes,
770 };
771
772 if let Some(nested_marker) = is_content_nested_bullet_marker(
773 content_line,
774 marker_match.marker_len,
775 marker_match.spaces_after_bytes,
776 ) {
777 lists::add_list_item_with_nested_empty_list(
778 &mut self.containers,
779 &mut self.builder,
780 &list_item,
781 nested_marker,
782 );
783 } else {
784 lists::add_list_item(
785 &mut self.containers,
786 &mut self.builder,
787 &list_item,
788 );
789 }
790 } else if let Some(fence) = code_blocks::try_parse_fence_open(content_slice) {
791 self.containers.push(Container::Definition {
792 content_col,
793 plain_open: false,
794 plain_buffer: TextBuffer::new(),
795 });
796 definition_pushed = true;
797
798 let bq_depth = self.current_blockquote_depth();
799 if let Some(indent_str) = indent_to_emit {
800 self.builder
801 .token(SyntaxKind::WHITESPACE.into(), indent_str);
802 }
803 let fence_line = current_line[content_start..].to_string();
804 let new_pos = if self.config.extensions.tex_math_gfm
805 && code_blocks::is_gfm_math_fence(&fence)
806 {
807 code_blocks::parse_fenced_math_block(
808 &mut self.builder,
809 &self.lines,
810 self.pos,
811 fence,
812 bq_depth,
813 content_col,
814 Some(&fence_line),
815 )
816 } else {
817 code_blocks::parse_fenced_code_block(
818 &mut self.builder,
819 &self.lines,
820 self.pos,
821 fence,
822 bq_depth,
823 content_col,
824 Some(&fence_line),
825 )
826 };
827 self.pos = new_pos - 1;
828 } else {
829 let (_, newline_str) = strip_newline(current_line);
830 let (content_without_newline, _) = strip_newline(after_marker_and_spaces);
831 if content_without_newline.is_empty() {
832 plain_buffer.push_line(newline_str);
833 } else {
834 let line_with_newline = if !newline_str.is_empty() {
835 format!("{}{}", content_without_newline, newline_str)
836 } else {
837 content_without_newline.to_string()
838 };
839 plain_buffer.push_line(line_with_newline);
840 }
841 }
842 }
843
844 if !definition_pushed {
845 self.containers.push(Container::Definition {
846 content_col,
847 plain_open: *has_content,
848 plain_buffer,
849 });
850 }
851 }
852 DefinitionPrepared::Term { blank_count } => {
853 self.emit_buffered_plain_if_needed();
854
855 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
856 self.close_containers_to(self.containers.depth() - 1);
857 }
858
859 if !definition_lists::in_definition_list(&self.containers) {
860 self.builder.start_node(SyntaxKind::DEFINITION_LIST.into());
861 self.containers.push(Container::DefinitionList {});
862 }
863
864 while matches!(
865 self.containers.last(),
866 Some(Container::Definition { .. }) | Some(Container::DefinitionItem { .. })
867 ) {
868 self.close_containers_to(self.containers.depth() - 1);
869 }
870
871 self.builder.start_node(SyntaxKind::DEFINITION_ITEM.into());
872 self.containers.push(Container::DefinitionItem {});
873
874 emit_term(&mut self.builder, content, self.config);
875
876 for i in 0..*blank_count {
877 let blank_pos = self.pos + 1 + i;
878 if blank_pos < self.lines.len() {
879 let blank_line = self.lines[blank_pos];
880 self.builder.start_node(SyntaxKind::BLANK_LINE.into());
881 self.builder
882 .token(SyntaxKind::BLANK_LINE.into(), blank_line);
883 self.builder.finish_node();
884 }
885 }
886 self.pos += *blank_count;
887 }
888 }
889 }
890
891 fn blockquote_marker_info(
893 &self,
894 payload: Option<&BlockQuotePrepared>,
895 line: &str,
896 ) -> Vec<marker_utils::BlockQuoteMarkerInfo> {
897 payload
898 .map(|payload| payload.marker_info.clone())
899 .unwrap_or_else(|| parse_blockquote_marker_info(line))
900 }
901
902 fn marker_info_for_line(
908 &self,
909 payload: Option<&BlockQuotePrepared>,
910 raw_line: &str,
911 marker_line: &str,
912 shifted_prefix: &str,
913 used_shifted: bool,
914 ) -> Vec<marker_utils::BlockQuoteMarkerInfo> {
915 let mut marker_info = if used_shifted {
916 parse_blockquote_marker_info(marker_line)
917 } else {
918 self.blockquote_marker_info(payload, raw_line)
919 };
920 if used_shifted && !shifted_prefix.is_empty() {
921 let (prefix_cols, _) = leading_indent(shifted_prefix);
922 if let Some(first) = marker_info.first_mut() {
923 first.leading_spaces += prefix_cols;
924 }
925 }
926 marker_info
927 }
928
929 fn shifted_blockquote_from_list<'b>(
932 &self,
933 line: &'b str,
934 ) -> Option<(usize, &'b str, &'b str, &'b str)> {
935 if !lists::in_list(&self.containers) {
936 return None;
937 }
938 let list_content_col = paragraphs::current_content_col(&self.containers);
939 let content_container_indent = self.content_container_indent_to_strip();
940 let marker_col = list_content_col.saturating_add(content_container_indent);
941 if marker_col == 0 {
942 return None;
943 }
944
945 let (indent_cols, _) = leading_indent(line);
946 if indent_cols < marker_col {
947 return None;
948 }
949
950 let idx = byte_index_at_column(line, marker_col);
951 if idx > line.len() {
952 return None;
953 }
954
955 let candidate = &line[idx..];
956 let (candidate_depth, candidate_inner) = count_blockquote_markers(candidate);
957 if candidate_depth == 0 {
958 return None;
959 }
960
961 Some((candidate_depth, candidate_inner, candidate, &line[..idx]))
962 }
963
964 fn emit_blockquote_markers(
965 &mut self,
966 marker_info: &[marker_utils::BlockQuoteMarkerInfo],
967 depth: usize,
968 ) {
969 for i in 0..depth {
970 if let Some(info) = marker_info.get(i) {
971 blockquotes::emit_one_blockquote_marker(
972 &mut self.builder,
973 info.leading_spaces,
974 info.has_trailing_space,
975 );
976 }
977 }
978 }
979
980 fn current_blockquote_depth(&self) -> usize {
981 blockquotes::current_blockquote_depth(&self.containers)
982 }
983
984 fn emit_or_buffer_blockquote_marker(
989 &mut self,
990 leading_spaces: usize,
991 has_trailing_space: bool,
992 ) {
993 if let Some(Container::ListItem { buffer, .. }) = self.containers.stack.last_mut() {
994 buffer.push_blockquote_marker(leading_spaces, has_trailing_space);
995 return;
996 }
997
998 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1000 paragraphs::append_paragraph_marker(
1002 &mut self.containers,
1003 leading_spaces,
1004 has_trailing_space,
1005 );
1006 } else {
1007 blockquotes::emit_one_blockquote_marker(
1009 &mut self.builder,
1010 leading_spaces,
1011 has_trailing_space,
1012 );
1013 }
1014 }
1015
1016 fn parse_document_stack(&mut self) {
1017 self.builder.start_node(SyntaxKind::DOCUMENT.into());
1018
1019 log::trace!("Starting document parse");
1020
1021 while self.pos < self.lines.len() {
1024 let line = self.lines[self.pos];
1025
1026 log::trace!("Parsing line {}: {}", self.pos + 1, line);
1027
1028 if self.parse_line(line) {
1029 continue;
1030 }
1031 self.pos += 1;
1032 }
1033
1034 self.close_containers_to(0);
1035 self.builder.finish_node(); }
1037
1038 fn parse_line(&mut self, line: &str) -> bool {
1040 let (mut bq_depth, mut inner_content) = count_blockquote_markers(line);
1043 let mut bq_marker_line = line;
1044 let mut shifted_bq_prefix = "";
1045 let mut used_shifted_bq = false;
1046 if bq_depth == 0
1047 && let Some((candidate_depth, candidate_inner, candidate_line, candidate_prefix)) =
1048 self.shifted_blockquote_from_list(line)
1049 {
1050 bq_depth = candidate_depth;
1051 inner_content = candidate_inner;
1052 bq_marker_line = candidate_line;
1053 shifted_bq_prefix = candidate_prefix;
1054 used_shifted_bq = true;
1055 }
1056 let current_bq_depth = self.current_blockquote_depth();
1057
1058 let has_blank_before = self.pos == 0 || self.lines[self.pos - 1].trim().is_empty();
1059 let mut blockquote_match: Option<PreparedBlockMatch> = None;
1060 let dispatcher_ctx = if current_bq_depth == 0 {
1061 Some(BlockContext {
1062 content: line,
1063 has_blank_before,
1064 has_blank_before_strict: has_blank_before,
1065 at_document_start: self.pos == 0,
1066 in_fenced_div: self.in_fenced_div(),
1067 blockquote_depth: current_bq_depth,
1068 config: self.config,
1069 content_indent: 0,
1070 indent_to_emit: None,
1071 list_indent_info: None,
1072 in_list: lists::in_list(&self.containers),
1073 next_line: if self.pos + 1 < self.lines.len() {
1074 Some(self.lines[self.pos + 1])
1075 } else {
1076 None
1077 },
1078 })
1079 } else {
1080 None
1081 };
1082
1083 let blockquote_payload = if let Some(dispatcher_ctx) = dispatcher_ctx.as_ref() {
1084 self.block_registry
1085 .detect_prepared(dispatcher_ctx, &self.lines, self.pos)
1086 .and_then(|prepared| {
1087 if matches!(prepared.effect, BlockEffect::OpenBlockQuote) {
1088 blockquote_match = Some(prepared);
1089 blockquote_match.as_ref().and_then(|prepared| {
1090 prepared
1091 .payload
1092 .as_ref()
1093 .and_then(|payload| payload.downcast_ref::<BlockQuotePrepared>())
1094 .cloned()
1095 })
1096 } else {
1097 None
1098 }
1099 })
1100 } else {
1101 None
1102 };
1103
1104 log::trace!(
1105 "parse_line [{}]: bq_depth={}, current_bq={}, depth={}, line={:?}",
1106 self.pos,
1107 bq_depth,
1108 current_bq_depth,
1109 self.containers.depth(),
1110 line.trim_end()
1111 );
1112
1113 let is_blank = line.trim_end_matches('\n').trim().is_empty()
1116 || (bq_depth > 0 && inner_content.trim_end_matches('\n').trim().is_empty());
1117
1118 if is_blank {
1119 if self.is_paragraph_open()
1120 && paragraphs::has_open_inline_math_environment(&self.containers)
1121 {
1122 paragraphs::append_paragraph_line(
1123 &mut self.containers,
1124 &mut self.builder,
1125 line,
1126 self.config,
1127 );
1128 self.pos += 1;
1129 return true;
1130 }
1131
1132 self.close_paragraph_if_open();
1134
1135 self.emit_buffered_plain_if_needed();
1139
1140 if bq_depth > current_bq_depth {
1148 for _ in current_bq_depth..bq_depth {
1150 self.builder.start_node(SyntaxKind::BLOCK_QUOTE.into());
1151 self.containers.push(Container::BlockQuote {});
1152 }
1153 } else if bq_depth < current_bq_depth {
1154 self.close_blockquotes_to_depth(bq_depth);
1156 }
1157
1158 let mut peek = self.pos + 1;
1160 while peek < self.lines.len() && self.lines[peek].trim().is_empty() {
1161 peek += 1;
1162 }
1163
1164 let levels_to_keep = if peek < self.lines.len() {
1166 ContinuationPolicy::new(self.config, &self.block_registry).compute_levels_to_keep(
1167 self.current_blockquote_depth(),
1168 &self.containers,
1169 &self.lines,
1170 peek,
1171 self.lines[peek],
1172 )
1173 } else {
1174 0
1175 };
1176 log::trace!(
1177 "Blank line: depth={}, levels_to_keep={}, next='{}'",
1178 self.containers.depth(),
1179 levels_to_keep,
1180 if peek < self.lines.len() {
1181 self.lines[peek]
1182 } else {
1183 "<EOF>"
1184 }
1185 );
1186
1187 while self.containers.depth() > levels_to_keep {
1191 match self.containers.last() {
1192 Some(Container::ListItem { .. }) => {
1193 log::trace!(
1195 "Closing ListItem at blank line (levels_to_keep={} < depth={})",
1196 levels_to_keep,
1197 self.containers.depth()
1198 );
1199 self.close_containers_to(self.containers.depth() - 1);
1200 }
1201 Some(Container::List { .. })
1202 | Some(Container::FootnoteDefinition { .. })
1203 | Some(Container::Alert { .. })
1204 | Some(Container::Paragraph { .. })
1205 | Some(Container::Definition { .. })
1206 | Some(Container::DefinitionItem { .. })
1207 | Some(Container::DefinitionList { .. }) => {
1208 log::trace!(
1209 "Closing {:?} at blank line (depth {} > levels_to_keep {})",
1210 self.containers.last(),
1211 self.containers.depth(),
1212 levels_to_keep
1213 );
1214
1215 self.close_containers_to(self.containers.depth() - 1);
1216 }
1217 _ => break,
1218 }
1219 }
1220
1221 if matches!(self.containers.last(), Some(Container::ListItem { .. })) {
1225 self.emit_list_item_buffer_if_needed();
1226 }
1227
1228 if bq_depth > 0 {
1230 let marker_info = self.marker_info_for_line(
1231 blockquote_payload.as_ref(),
1232 line,
1233 bq_marker_line,
1234 shifted_bq_prefix,
1235 used_shifted_bq,
1236 );
1237 self.emit_blockquote_markers(&marker_info, bq_depth);
1238 }
1239
1240 self.builder.start_node(SyntaxKind::BLANK_LINE.into());
1241 self.builder
1242 .token(SyntaxKind::BLANK_LINE.into(), inner_content);
1243 self.builder.finish_node();
1244
1245 self.pos += 1;
1246 return true;
1247 }
1248
1249 if bq_depth > current_bq_depth {
1251 if self.config.extensions.blank_before_blockquote
1254 && current_bq_depth == 0
1255 && !used_shifted_bq
1256 && !blockquote_payload
1257 .as_ref()
1258 .map(|payload| payload.can_start)
1259 .unwrap_or_else(|| blockquotes::can_start_blockquote(self.pos, &self.lines))
1260 {
1261 self.emit_list_item_buffer_if_needed();
1265 paragraphs::start_paragraph_if_needed(&mut self.containers, &mut self.builder);
1266 paragraphs::append_paragraph_line(
1267 &mut self.containers,
1268 &mut self.builder,
1269 line,
1270 self.config,
1271 );
1272 self.pos += 1;
1273 return true;
1274 }
1275
1276 let can_nest = if current_bq_depth > 0 {
1279 if self.config.extensions.blank_before_blockquote {
1280 matches!(self.containers.last(), Some(Container::BlockQuote { .. }))
1282 || (self.pos > 0 && {
1283 let prev_line = self.lines[self.pos - 1];
1284 let (prev_bq_depth, prev_inner) = count_blockquote_markers(prev_line);
1285 prev_bq_depth >= current_bq_depth && prev_inner.trim().is_empty()
1286 })
1287 } else {
1288 true
1289 }
1290 } else {
1291 blockquote_payload
1292 .as_ref()
1293 .map(|payload| payload.can_nest)
1294 .unwrap_or(true)
1295 };
1296
1297 if !can_nest {
1298 let content_at_current_depth =
1301 blockquotes::strip_n_blockquote_markers(line, current_bq_depth);
1302
1303 let marker_info = self.marker_info_for_line(
1305 blockquote_payload.as_ref(),
1306 line,
1307 bq_marker_line,
1308 shifted_bq_prefix,
1309 used_shifted_bq,
1310 );
1311 for i in 0..current_bq_depth {
1312 if let Some(info) = marker_info.get(i) {
1313 self.emit_or_buffer_blockquote_marker(
1314 info.leading_spaces,
1315 info.has_trailing_space,
1316 );
1317 }
1318 }
1319
1320 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1321 paragraphs::append_paragraph_line(
1323 &mut self.containers,
1324 &mut self.builder,
1325 content_at_current_depth,
1326 self.config,
1327 );
1328 self.pos += 1;
1329 return true;
1330 } else {
1331 paragraphs::start_paragraph_if_needed(&mut self.containers, &mut self.builder);
1333 paragraphs::append_paragraph_line(
1334 &mut self.containers,
1335 &mut self.builder,
1336 content_at_current_depth,
1337 self.config,
1338 );
1339 self.pos += 1;
1340 return true;
1341 }
1342 }
1343
1344 self.emit_list_item_buffer_if_needed();
1347
1348 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1350 self.close_containers_to(self.containers.depth() - 1);
1351 }
1352
1353 let marker_info = self.marker_info_for_line(
1355 blockquote_payload.as_ref(),
1356 line,
1357 bq_marker_line,
1358 shifted_bq_prefix,
1359 used_shifted_bq,
1360 );
1361
1362 if let (Some(dispatcher_ctx), Some(prepared)) =
1363 (dispatcher_ctx.as_ref(), blockquote_match.as_ref())
1364 {
1365 let _ = self.block_registry.parse_prepared(
1366 prepared,
1367 dispatcher_ctx,
1368 &mut self.builder,
1369 &self.lines,
1370 self.pos,
1371 );
1372 for _ in 0..bq_depth {
1373 self.containers.push(Container::BlockQuote {});
1374 }
1375 } else {
1376 for level in 0..current_bq_depth {
1378 if let Some(info) = marker_info.get(level) {
1379 self.emit_or_buffer_blockquote_marker(
1380 info.leading_spaces,
1381 info.has_trailing_space,
1382 );
1383 }
1384 }
1385
1386 for level in current_bq_depth..bq_depth {
1388 self.builder.start_node(SyntaxKind::BLOCK_QUOTE.into());
1389
1390 if let Some(info) = marker_info.get(level) {
1392 blockquotes::emit_one_blockquote_marker(
1393 &mut self.builder,
1394 info.leading_spaces,
1395 info.has_trailing_space,
1396 );
1397 }
1398
1399 self.containers.push(Container::BlockQuote {});
1400 }
1401 }
1402
1403 return self.parse_inner_content(inner_content, Some(inner_content));
1406 } else if bq_depth < current_bq_depth {
1407 if bq_depth == 0 {
1410 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1412 paragraphs::append_paragraph_line(
1413 &mut self.containers,
1414 &mut self.builder,
1415 line,
1416 self.config,
1417 );
1418 self.pos += 1;
1419 return true;
1420 }
1421
1422 if lists::in_blockquote_list(&self.containers)
1425 && let Some(marker_match) = try_parse_list_marker(line, self.config)
1426 {
1427 let (indent_cols, indent_bytes) = leading_indent(line);
1428 if let Some(level) = lists::find_matching_list_level(
1429 &self.containers,
1430 &marker_match.marker,
1431 indent_cols,
1432 ) {
1433 self.close_containers_to(level + 1);
1436
1437 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1439 self.close_containers_to(self.containers.depth() - 1);
1440 }
1441 if matches!(self.containers.last(), Some(Container::ListItem { .. })) {
1442 self.close_containers_to(self.containers.depth() - 1);
1443 }
1444
1445 if let Some(nested_marker) = is_content_nested_bullet_marker(
1447 line,
1448 marker_match.marker_len,
1449 marker_match.spaces_after_bytes,
1450 ) {
1451 let list_item = ListItemEmissionInput {
1452 content: line,
1453 marker_len: marker_match.marker_len,
1454 spaces_after_cols: marker_match.spaces_after_cols,
1455 spaces_after_bytes: marker_match.spaces_after_bytes,
1456 indent_cols,
1457 indent_bytes,
1458 };
1459 lists::add_list_item_with_nested_empty_list(
1460 &mut self.containers,
1461 &mut self.builder,
1462 &list_item,
1463 nested_marker,
1464 );
1465 } else {
1466 let list_item = ListItemEmissionInput {
1467 content: line,
1468 marker_len: marker_match.marker_len,
1469 spaces_after_cols: marker_match.spaces_after_cols,
1470 spaces_after_bytes: marker_match.spaces_after_bytes,
1471 indent_cols,
1472 indent_bytes,
1473 };
1474 lists::add_list_item(
1475 &mut self.containers,
1476 &mut self.builder,
1477 &list_item,
1478 );
1479 }
1480 self.pos += 1;
1481 return true;
1482 }
1483 }
1484 }
1485
1486 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1488 self.close_containers_to(self.containers.depth() - 1);
1489 }
1490
1491 self.close_blockquotes_to_depth(bq_depth);
1493
1494 if bq_depth > 0 {
1496 let marker_info = self.marker_info_for_line(
1498 blockquote_payload.as_ref(),
1499 line,
1500 bq_marker_line,
1501 shifted_bq_prefix,
1502 used_shifted_bq,
1503 );
1504 for i in 0..bq_depth {
1505 if let Some(info) = marker_info.get(i) {
1506 self.emit_or_buffer_blockquote_marker(
1507 info.leading_spaces,
1508 info.has_trailing_space,
1509 );
1510 }
1511 }
1512 return self.parse_inner_content(inner_content, Some(inner_content));
1514 } else {
1515 return self.parse_inner_content(line, None);
1517 }
1518 } else if bq_depth > 0 {
1519 let mut list_item_continuation = false;
1521 let same_depth_marker_info = self.marker_info_for_line(
1522 blockquote_payload.as_ref(),
1523 line,
1524 bq_marker_line,
1525 shifted_bq_prefix,
1526 used_shifted_bq,
1527 );
1528 let has_explicit_same_depth_marker = same_depth_marker_info.len() >= bq_depth;
1529
1530 if matches!(
1533 self.containers.last(),
1534 Some(Container::ListItem { content_col: _, .. })
1535 ) {
1536 let (indent_cols, _) = leading_indent(inner_content);
1537 let content_indent = self.content_container_indent_to_strip();
1538 let effective_indent = indent_cols.saturating_sub(content_indent);
1539 let content_col = match self.containers.last() {
1540 Some(Container::ListItem { content_col, .. }) => *content_col,
1541 _ => 0,
1542 };
1543
1544 let is_new_item_at_outer_level =
1546 if try_parse_list_marker(inner_content, self.config).is_some() {
1547 effective_indent < content_col
1548 } else {
1549 false
1550 };
1551
1552 if is_new_item_at_outer_level
1556 || (effective_indent < content_col && !has_explicit_same_depth_marker)
1557 {
1558 log::trace!(
1559 "Closing ListItem: is_new_item={}, effective_indent={} < content_col={}",
1560 is_new_item_at_outer_level,
1561 effective_indent,
1562 content_col
1563 );
1564 self.close_containers_to(self.containers.depth() - 1);
1565 } else {
1566 log::trace!(
1567 "Keeping ListItem: effective_indent={} >= content_col={}",
1568 effective_indent,
1569 content_col
1570 );
1571 list_item_continuation = true;
1572 }
1573 }
1574
1575 if list_item_continuation && code_blocks::try_parse_fence_open(inner_content).is_some()
1579 {
1580 list_item_continuation = false;
1581 }
1582
1583 let continuation_has_explicit_marker = list_item_continuation && {
1584 if has_explicit_same_depth_marker {
1585 for i in 0..bq_depth {
1586 if let Some(info) = same_depth_marker_info.get(i) {
1587 self.emit_or_buffer_blockquote_marker(
1588 info.leading_spaces,
1589 info.has_trailing_space,
1590 );
1591 }
1592 }
1593 true
1594 } else {
1595 false
1596 }
1597 };
1598
1599 if !list_item_continuation {
1600 let marker_info = self.marker_info_for_line(
1601 blockquote_payload.as_ref(),
1602 line,
1603 bq_marker_line,
1604 shifted_bq_prefix,
1605 used_shifted_bq,
1606 );
1607 for i in 0..bq_depth {
1608 if let Some(info) = marker_info.get(i) {
1609 self.emit_or_buffer_blockquote_marker(
1610 info.leading_spaces,
1611 info.has_trailing_space,
1612 );
1613 }
1614 }
1615 }
1616 let line_to_append = if list_item_continuation {
1617 if continuation_has_explicit_marker {
1618 Some(inner_content)
1619 } else {
1620 Some(line)
1621 }
1622 } else {
1623 Some(inner_content)
1624 };
1625 return self.parse_inner_content(inner_content, line_to_append);
1626 }
1627
1628 if current_bq_depth > 0 {
1631 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1633 paragraphs::append_paragraph_line(
1634 &mut self.containers,
1635 &mut self.builder,
1636 line,
1637 self.config,
1638 );
1639 self.pos += 1;
1640 return true;
1641 }
1642
1643 if lists::in_blockquote_list(&self.containers)
1645 && let Some(marker_match) = try_parse_list_marker(line, self.config)
1646 {
1647 let (indent_cols, indent_bytes) = leading_indent(line);
1648 if let Some(level) = lists::find_matching_list_level(
1649 &self.containers,
1650 &marker_match.marker,
1651 indent_cols,
1652 ) {
1653 self.close_containers_to(level + 1);
1655
1656 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1658 self.close_containers_to(self.containers.depth() - 1);
1659 }
1660 if matches!(self.containers.last(), Some(Container::ListItem { .. })) {
1661 self.close_containers_to(self.containers.depth() - 1);
1662 }
1663
1664 if let Some(nested_marker) = is_content_nested_bullet_marker(
1666 line,
1667 marker_match.marker_len,
1668 marker_match.spaces_after_bytes,
1669 ) {
1670 let list_item = ListItemEmissionInput {
1671 content: line,
1672 marker_len: marker_match.marker_len,
1673 spaces_after_cols: marker_match.spaces_after_cols,
1674 spaces_after_bytes: marker_match.spaces_after_bytes,
1675 indent_cols,
1676 indent_bytes,
1677 };
1678 lists::add_list_item_with_nested_empty_list(
1679 &mut self.containers,
1680 &mut self.builder,
1681 &list_item,
1682 nested_marker,
1683 );
1684 } else {
1685 let list_item = ListItemEmissionInput {
1686 content: line,
1687 marker_len: marker_match.marker_len,
1688 spaces_after_cols: marker_match.spaces_after_cols,
1689 spaces_after_bytes: marker_match.spaces_after_bytes,
1690 indent_cols,
1691 indent_bytes,
1692 };
1693 lists::add_list_item(&mut self.containers, &mut self.builder, &list_item);
1694 }
1695 self.pos += 1;
1696 return true;
1697 }
1698 }
1699 }
1700
1701 self.parse_inner_content(line, None)
1703 }
1704
1705 fn content_container_indent_to_strip(&self) -> usize {
1707 self.containers
1708 .stack
1709 .iter()
1710 .filter_map(|c| match c {
1711 Container::FootnoteDefinition { content_col, .. } => Some(*content_col),
1712 Container::Definition { content_col, .. } => Some(*content_col),
1713 _ => None,
1714 })
1715 .sum()
1716 }
1717
1718 fn parse_inner_content(&mut self, content: &str, line_to_append: Option<&str>) -> bool {
1724 log::trace!(
1725 "parse_inner_content [{}]: depth={}, last={:?}, content={:?}",
1726 self.pos,
1727 self.containers.depth(),
1728 self.containers.last(),
1729 content.trim_end()
1730 );
1731 let content_indent = self.content_container_indent_to_strip();
1734 let (stripped_content, indent_to_emit) = if content_indent > 0 {
1735 let (indent_cols, _) = leading_indent(content);
1736 if indent_cols >= content_indent {
1737 let idx = byte_index_at_column(content, content_indent);
1738 (&content[idx..], Some(&content[..idx]))
1739 } else {
1740 let trimmed_start = content.trim_start();
1742 let ws_len = content.len() - trimmed_start.len();
1743 if ws_len > 0 {
1744 (trimmed_start, Some(&content[..ws_len]))
1745 } else {
1746 (content, None)
1747 }
1748 }
1749 } else {
1750 (content, None)
1751 };
1752
1753 if self.config.extensions.alerts
1754 && self.current_blockquote_depth() > 0
1755 && !self.in_active_alert()
1756 && !self.is_paragraph_open()
1757 && let Some(marker) = Self::alert_marker_from_content(stripped_content)
1758 {
1759 let (_, newline_str) = strip_newline(stripped_content);
1760 self.builder.start_node(SyntaxKind::ALERT.into());
1761 self.builder.token(SyntaxKind::ALERT_MARKER.into(), marker);
1762 if !newline_str.is_empty() {
1763 self.builder.token(SyntaxKind::NEWLINE.into(), newline_str);
1764 }
1765 self.containers.push(Container::Alert {
1766 blockquote_depth: self.current_blockquote_depth(),
1767 });
1768 self.pos += 1;
1769 return true;
1770 }
1771
1772 if matches!(self.containers.last(), Some(Container::Definition { .. })) {
1776 let is_definition_marker =
1777 definition_lists::try_parse_definition_marker(stripped_content).is_some()
1778 && !stripped_content.starts_with(':');
1779 if content_indent == 0 && is_definition_marker {
1780 } else {
1782 let policy = ContinuationPolicy::new(self.config, &self.block_registry);
1783
1784 if policy.definition_plain_can_continue(
1785 stripped_content,
1786 content,
1787 content_indent,
1788 &BlockContext {
1789 content: stripped_content,
1790 has_blank_before: self.pos == 0
1791 || self.lines[self.pos - 1].trim().is_empty(),
1792 has_blank_before_strict: self.pos == 0
1793 || self.lines[self.pos - 1].trim().is_empty(),
1794 at_document_start: self.pos == 0 && self.current_blockquote_depth() == 0,
1795 in_fenced_div: self.in_fenced_div(),
1796 blockquote_depth: self.current_blockquote_depth(),
1797 config: self.config,
1798 content_indent,
1799 indent_to_emit: None,
1800 list_indent_info: None,
1801 in_list: lists::in_list(&self.containers),
1802 next_line: if self.pos + 1 < self.lines.len() {
1803 Some(self.lines[self.pos + 1])
1804 } else {
1805 None
1806 },
1807 },
1808 &self.lines,
1809 self.pos,
1810 ) {
1811 let content_line = stripped_content;
1812 let (text_without_newline, newline_str) = strip_newline(content_line);
1813 let indent_prefix = if !text_without_newline.trim().is_empty() {
1814 indent_to_emit.unwrap_or("")
1815 } else {
1816 ""
1817 };
1818 let content_line = format!("{}{}", indent_prefix, text_without_newline);
1819
1820 if let Some(Container::Definition {
1821 plain_open,
1822 plain_buffer,
1823 ..
1824 }) = self.containers.stack.last_mut()
1825 {
1826 let line_with_newline = if !newline_str.is_empty() {
1827 format!("{}{}", content_line, newline_str)
1828 } else {
1829 content_line
1830 };
1831 plain_buffer.push_line(line_with_newline);
1832 *plain_open = true;
1833 }
1834
1835 self.pos += 1;
1836 return true;
1837 }
1838 }
1839 }
1840
1841 if content_indent > 0 {
1844 let (bq_depth, inner_content) = count_blockquote_markers(stripped_content);
1845 let current_bq_depth = self.current_blockquote_depth();
1846 let in_footnote_definition = self
1847 .containers
1848 .stack
1849 .iter()
1850 .any(|container| matches!(container, Container::FootnoteDefinition { .. }));
1851
1852 if bq_depth > 0 {
1853 if in_footnote_definition
1854 && self.config.extensions.blank_before_blockquote
1855 && current_bq_depth == 0
1856 && !blockquotes::can_start_blockquote(self.pos, &self.lines)
1857 {
1858 } else {
1862 self.emit_buffered_plain_if_needed();
1865 self.emit_list_item_buffer_if_needed();
1866
1867 self.close_paragraph_if_open();
1870
1871 if bq_depth > current_bq_depth {
1872 let marker_info = parse_blockquote_marker_info(stripped_content);
1873
1874 for level in current_bq_depth..bq_depth {
1876 self.builder.start_node(SyntaxKind::BLOCK_QUOTE.into());
1877
1878 if level == current_bq_depth
1879 && let Some(indent_str) = indent_to_emit
1880 {
1881 self.builder
1882 .token(SyntaxKind::WHITESPACE.into(), indent_str);
1883 }
1884
1885 if let Some(info) = marker_info.get(level) {
1886 blockquotes::emit_one_blockquote_marker(
1887 &mut self.builder,
1888 info.leading_spaces,
1889 info.has_trailing_space,
1890 );
1891 }
1892
1893 self.containers.push(Container::BlockQuote {});
1894 }
1895 } else if bq_depth < current_bq_depth {
1896 self.close_blockquotes_to_depth(bq_depth);
1897 } else {
1898 let marker_info = parse_blockquote_marker_info(stripped_content);
1900 self.emit_blockquote_markers(&marker_info, bq_depth);
1901 }
1902
1903 return self.parse_inner_content(inner_content, Some(inner_content));
1904 }
1905 }
1906 }
1907
1908 let content = stripped_content;
1910
1911 if self.is_paragraph_open()
1912 && (paragraphs::has_open_inline_math_environment(&self.containers)
1913 || paragraphs::has_open_display_math_dollars(&self.containers))
1914 {
1915 paragraphs::append_paragraph_line(
1916 &mut self.containers,
1917 &mut self.builder,
1918 line_to_append.unwrap_or(self.lines[self.pos]),
1919 self.config,
1920 );
1921 self.pos += 1;
1922 return true;
1923 }
1924
1925 use super::blocks::lists;
1929 use super::blocks::paragraphs;
1930 let list_indent_info = if lists::in_list(&self.containers) {
1931 let content_col = paragraphs::current_content_col(&self.containers);
1932 if content_col > 0 {
1933 Some(super::block_dispatcher::ListIndentInfo { content_col })
1934 } else {
1935 None
1936 }
1937 } else {
1938 None
1939 };
1940
1941 let next_line = if self.pos + 1 < self.lines.len() {
1942 Some(count_blockquote_markers(self.lines[self.pos + 1]).1)
1945 } else {
1946 None
1947 };
1948
1949 let current_bq_depth = self.current_blockquote_depth();
1950 if let Some(alert_bq_depth) = self.active_alert_blockquote_depth()
1951 && current_bq_depth < alert_bq_depth
1952 {
1953 while matches!(self.containers.last(), Some(Container::Alert { .. })) {
1954 self.close_containers_to(self.containers.depth() - 1);
1955 }
1956 }
1957
1958 let dispatcher_ctx = BlockContext {
1959 content,
1960 has_blank_before: false, has_blank_before_strict: false, at_document_start: false, in_fenced_div: self.in_fenced_div(),
1964 blockquote_depth: current_bq_depth,
1965 config: self.config,
1966 content_indent,
1967 indent_to_emit,
1968 list_indent_info,
1969 in_list: lists::in_list(&self.containers),
1970 next_line,
1971 };
1972
1973 let mut dispatcher_ctx = dispatcher_ctx;
1976
1977 let dispatcher_match =
1980 self.block_registry
1981 .detect_prepared(&dispatcher_ctx, &self.lines, self.pos);
1982
1983 let after_metadata_block = std::mem::replace(&mut self.after_metadata_block, false);
1989 let has_blank_before = if self.pos == 0 || after_metadata_block {
1990 true
1991 } else {
1992 let prev_line = self.lines[self.pos - 1];
1993 let (prev_bq_depth, prev_inner) = count_blockquote_markers(prev_line);
1994 let (prev_inner_no_nl, _) = strip_newline(prev_inner);
1995 let prev_is_fenced_div_open = self.config.extensions.fenced_divs
1996 && fenced_divs::try_parse_div_fence_open(
1997 strip_n_blockquote_markers(prev_inner_no_nl, prev_bq_depth).trim_start(),
1998 )
1999 .is_some();
2000
2001 let prev_line_blank = prev_line.trim().is_empty();
2002 prev_line_blank
2003 || prev_is_fenced_div_open
2004 || matches!(self.containers.last(), Some(Container::BlockQuote { .. }))
2005 || !self.previous_block_requires_blank_before_heading()
2006 };
2007
2008 let at_document_start = self.pos == 0 && current_bq_depth == 0;
2011
2012 let prev_line_blank = if self.pos > 0 {
2013 let prev_line = self.lines[self.pos - 1];
2014 let (prev_bq_depth, prev_inner) = count_blockquote_markers(prev_line);
2015 prev_line.trim().is_empty() || (prev_bq_depth > 0 && prev_inner.trim().is_empty())
2016 } else {
2017 false
2018 };
2019 let has_blank_before_strict = at_document_start || prev_line_blank;
2020
2021 dispatcher_ctx.has_blank_before = has_blank_before;
2022 dispatcher_ctx.has_blank_before_strict = has_blank_before_strict;
2023 dispatcher_ctx.at_document_start = at_document_start;
2024
2025 let dispatcher_match =
2026 if dispatcher_ctx.has_blank_before || dispatcher_ctx.at_document_start {
2027 self.block_registry
2029 .detect_prepared(&dispatcher_ctx, &self.lines, self.pos)
2030 } else {
2031 dispatcher_match
2032 };
2033
2034 if has_blank_before {
2035 if let Some(env_name) = extract_environment_name(content)
2036 && is_inline_math_environment(&env_name)
2037 {
2038 if !self.is_paragraph_open() {
2039 paragraphs::start_paragraph_if_needed(&mut self.containers, &mut self.builder);
2040 }
2041 paragraphs::append_paragraph_line(
2042 &mut self.containers,
2043 &mut self.builder,
2044 line_to_append.unwrap_or(self.lines[self.pos]),
2045 self.config,
2046 );
2047 self.pos += 1;
2048 return true;
2049 }
2050
2051 if let Some(block_match) = dispatcher_match.as_ref() {
2052 let detection = block_match.detection;
2053
2054 match detection {
2055 BlockDetectionResult::YesCanInterrupt => {
2056 self.emit_list_item_buffer_if_needed();
2057 if self.is_paragraph_open() {
2058 self.close_containers_to(self.containers.depth() - 1);
2059 }
2060 }
2061 BlockDetectionResult::Yes => {
2062 self.prepare_for_block_element();
2063 }
2064 BlockDetectionResult::No => unreachable!(),
2065 }
2066
2067 if matches!(block_match.effect, BlockEffect::CloseFencedDiv) {
2068 self.close_containers_to_fenced_div();
2069 }
2070
2071 let lines_consumed = self.block_registry.parse_prepared(
2072 block_match,
2073 &dispatcher_ctx,
2074 &mut self.builder,
2075 &self.lines,
2076 self.pos,
2077 );
2078
2079 if matches!(
2080 self.block_registry.parser_name(block_match),
2081 "yaml_metadata" | "pandoc_title_block" | "mmd_title_block"
2082 ) {
2083 self.after_metadata_block = true;
2084 }
2085
2086 match block_match.effect {
2087 BlockEffect::None => {}
2088 BlockEffect::OpenFencedDiv => {
2089 self.containers.push(Container::FencedDiv {});
2090 }
2091 BlockEffect::CloseFencedDiv => {
2092 self.close_fenced_div();
2093 }
2094 BlockEffect::OpenFootnoteDefinition => {
2095 self.handle_footnote_open_effect(block_match, content);
2096 }
2097 BlockEffect::OpenList => {
2098 self.handle_list_open_effect(block_match, content, indent_to_emit);
2099 }
2100 BlockEffect::OpenDefinitionList => {
2101 self.handle_definition_list_effect(block_match, content, indent_to_emit);
2102 }
2103 BlockEffect::OpenBlockQuote => {
2104 }
2106 }
2107
2108 if lines_consumed == 0 {
2109 log::warn!(
2110 "block parser made no progress at line {} (parser={})",
2111 self.pos + 1,
2112 self.block_registry.parser_name(block_match)
2113 );
2114 return false;
2115 }
2116
2117 self.pos += lines_consumed;
2118 return true;
2119 }
2120 } else if let Some(block_match) = dispatcher_match.as_ref() {
2121 let parser_name = self.block_registry.parser_name(block_match);
2124 match block_match.detection {
2125 BlockDetectionResult::YesCanInterrupt => {
2126 if matches!(block_match.effect, BlockEffect::OpenFencedDiv)
2127 && self.is_paragraph_open()
2128 {
2129 if !self.is_paragraph_open() {
2131 paragraphs::start_paragraph_if_needed(
2132 &mut self.containers,
2133 &mut self.builder,
2134 );
2135 }
2136 paragraphs::append_paragraph_line(
2137 &mut self.containers,
2138 &mut self.builder,
2139 line_to_append.unwrap_or(self.lines[self.pos]),
2140 self.config,
2141 );
2142 self.pos += 1;
2143 return true;
2144 }
2145
2146 if matches!(block_match.effect, BlockEffect::OpenList)
2147 && self.is_paragraph_open()
2148 && !lists::in_list(&self.containers)
2149 && self.content_container_indent_to_strip() == 0
2150 {
2151 paragraphs::append_paragraph_line(
2153 &mut self.containers,
2154 &mut self.builder,
2155 line_to_append.unwrap_or(self.lines[self.pos]),
2156 self.config,
2157 );
2158 self.pos += 1;
2159 return true;
2160 }
2161
2162 self.emit_list_item_buffer_if_needed();
2163 if self.is_paragraph_open() {
2164 self.close_containers_to(self.containers.depth() - 1);
2165 }
2166 }
2167 BlockDetectionResult::Yes => {
2168 if parser_name == "fenced_div_open" && self.is_paragraph_open() {
2171 if !self.is_paragraph_open() {
2172 paragraphs::start_paragraph_if_needed(
2173 &mut self.containers,
2174 &mut self.builder,
2175 );
2176 }
2177 paragraphs::append_paragraph_line(
2178 &mut self.containers,
2179 &mut self.builder,
2180 line_to_append.unwrap_or(self.lines[self.pos]),
2181 self.config,
2182 );
2183 self.pos += 1;
2184 return true;
2185 }
2186 }
2187 BlockDetectionResult::No => unreachable!(),
2188 }
2189
2190 if !matches!(block_match.detection, BlockDetectionResult::No) {
2191 if matches!(block_match.effect, BlockEffect::CloseFencedDiv) {
2192 self.close_containers_to_fenced_div();
2193 }
2194
2195 let lines_consumed = self.block_registry.parse_prepared(
2196 block_match,
2197 &dispatcher_ctx,
2198 &mut self.builder,
2199 &self.lines,
2200 self.pos,
2201 );
2202
2203 match block_match.effect {
2204 BlockEffect::None => {}
2205 BlockEffect::OpenFencedDiv => {
2206 self.containers.push(Container::FencedDiv {});
2207 }
2208 BlockEffect::CloseFencedDiv => {
2209 self.close_fenced_div();
2210 }
2211 BlockEffect::OpenFootnoteDefinition => {
2212 self.handle_footnote_open_effect(block_match, content);
2213 }
2214 BlockEffect::OpenList => {
2215 self.handle_list_open_effect(block_match, content, indent_to_emit);
2216 }
2217 BlockEffect::OpenDefinitionList => {
2218 self.handle_definition_list_effect(block_match, content, indent_to_emit);
2219 }
2220 BlockEffect::OpenBlockQuote => {
2221 }
2223 }
2224
2225 if lines_consumed == 0 {
2226 log::warn!(
2227 "block parser made no progress at line {} (parser={})",
2228 self.pos + 1,
2229 self.block_registry.parser_name(block_match)
2230 );
2231 return false;
2232 }
2233
2234 self.pos += lines_consumed;
2235 return true;
2236 }
2237 }
2238
2239 if self.config.extensions.line_blocks
2241 && (has_blank_before || self.pos == 0)
2242 && try_parse_line_block_start(content).is_some()
2243 && try_parse_line_block_start(self.lines[self.pos]).is_some()
2247 {
2248 log::trace!("Parsed line block at line {}", self.pos);
2249 self.close_paragraph_if_open();
2251
2252 let new_pos = parse_line_block(&self.lines, self.pos, &mut self.builder, self.config);
2253 if new_pos > self.pos {
2254 self.pos = new_pos;
2255 return true;
2256 }
2257 }
2258
2259 if matches!(self.containers.last(), Some(Container::ListItem { .. })) {
2262 log::trace!(
2263 "Inside ListItem - buffering content: {:?}",
2264 line_to_append.unwrap_or(self.lines[self.pos]).trim_end()
2265 );
2266 let line = line_to_append.unwrap_or(self.lines[self.pos]);
2268
2269 if let Some(Container::ListItem { buffer, .. }) = self.containers.stack.last_mut() {
2271 buffer.push_text(line);
2272 }
2273
2274 self.pos += 1;
2275 return true;
2276 }
2277
2278 log::trace!(
2279 "Not in ListItem - creating paragraph for: {:?}",
2280 line_to_append.unwrap_or(self.lines[self.pos]).trim_end()
2281 );
2282 paragraphs::start_paragraph_if_needed(&mut self.containers, &mut self.builder);
2284 let line = line_to_append.unwrap_or(self.lines[self.pos]);
2287 paragraphs::append_paragraph_line(
2288 &mut self.containers,
2289 &mut self.builder,
2290 line,
2291 self.config,
2292 );
2293 self.pos += 1;
2294 true
2295 }
2296
2297 fn fenced_div_container_index(&self) -> Option<usize> {
2298 self.containers
2299 .stack
2300 .iter()
2301 .rposition(|c| matches!(c, Container::FencedDiv { .. }))
2302 }
2303
2304 fn close_containers_to_fenced_div(&mut self) {
2305 if let Some(index) = self.fenced_div_container_index() {
2306 self.close_containers_to(index + 1);
2307 }
2308 }
2309
2310 fn close_fenced_div(&mut self) {
2311 if let Some(index) = self.fenced_div_container_index() {
2312 self.close_containers_to(index);
2313 }
2314 }
2315
2316 fn in_fenced_div(&self) -> bool {
2317 self.containers
2318 .stack
2319 .iter()
2320 .any(|c| matches!(c, Container::FencedDiv { .. }))
2321 }
2322}