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::debug!(
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::debug!(
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::debug!("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 alert_marker_from_content(content: &str) -> Option<&'static str> {
293 let (without_newline, _) = strip_newline(content);
294 let trimmed = without_newline.trim();
295 GITHUB_ALERT_MARKERS
296 .into_iter()
297 .find(|marker| *marker == trimmed)
298 }
299
300 fn emit_list_item_buffer_if_needed(&mut self) {
303 if let Some(Container::ListItem { buffer, .. }) = self.containers.stack.last_mut()
304 && !buffer.is_empty()
305 {
306 let buffer_clone = buffer.clone();
307 buffer.clear();
308 let use_paragraph = buffer_clone.has_blank_lines_between_content();
309 buffer_clone.emit_as_block(&mut self.builder, use_paragraph, self.config);
310 }
311 }
312
313 fn is_paragraph_open(&self) -> bool {
315 matches!(self.containers.last(), Some(Container::Paragraph { .. }))
316 }
317
318 fn close_paragraph_if_open(&mut self) {
320 if self.is_paragraph_open() {
321 self.close_containers_to(self.containers.depth() - 1);
322 }
323 }
324
325 fn prepare_for_block_element(&mut self) {
328 self.emit_list_item_buffer_if_needed();
329 self.close_paragraph_if_open();
330 }
331
332 fn handle_footnote_open_effect(
333 &mut self,
334 block_match: &super::block_dispatcher::PreparedBlockMatch,
335 content: &str,
336 ) {
337 let content_start = block_match
338 .payload
339 .as_ref()
340 .and_then(|p| p.downcast_ref::<super::block_dispatcher::FootnoteDefinitionPrepared>())
341 .map(|p| p.content_start)
342 .unwrap_or(0);
343
344 while matches!(
345 self.containers.last(),
346 Some(Container::FootnoteDefinition { .. })
347 ) {
348 self.close_containers_to(self.containers.depth() - 1);
349 }
350
351 let content_col = 4;
352 self.containers
353 .push(Container::FootnoteDefinition { content_col });
354
355 if content_start > 0 {
356 let first_line_content = &content[content_start..];
357 if !first_line_content.trim().is_empty() {
358 paragraphs::start_paragraph_if_needed(&mut self.containers, &mut self.builder);
359 paragraphs::append_paragraph_line(
360 &mut self.containers,
361 &mut self.builder,
362 first_line_content,
363 self.config,
364 );
365 } else {
366 let (_, newline_str) = strip_newline(content);
367 if !newline_str.is_empty() {
368 self.builder.token(SyntaxKind::NEWLINE.into(), newline_str);
369 }
370 }
371 }
372 }
373
374 fn handle_list_open_effect(
375 &mut self,
376 block_match: &super::block_dispatcher::PreparedBlockMatch,
377 content: &str,
378 indent_to_emit: Option<&str>,
379 ) {
380 use super::block_dispatcher::ListPrepared;
381
382 let prepared = block_match
383 .payload
384 .as_ref()
385 .and_then(|p| p.downcast_ref::<ListPrepared>());
386 let Some(prepared) = prepared else {
387 return;
388 };
389
390 if prepared.indent_cols >= 4 && !lists::in_list(&self.containers) {
391 paragraphs::start_paragraph_if_needed(&mut self.containers, &mut self.builder);
392 paragraphs::append_paragraph_line(
393 &mut self.containers,
394 &mut self.builder,
395 content,
396 self.config,
397 );
398 return;
399 }
400
401 if self.is_paragraph_open() {
402 if !block_match.detection.eq(&BlockDetectionResult::Yes) {
403 paragraphs::append_paragraph_line(
404 &mut self.containers,
405 &mut self.builder,
406 content,
407 self.config,
408 );
409 return;
410 }
411 self.close_containers_to(self.containers.depth() - 1);
412 }
413
414 if matches!(
415 self.containers.last(),
416 Some(Container::Definition {
417 plain_open: true,
418 ..
419 })
420 ) {
421 self.emit_buffered_plain_if_needed();
422 }
423
424 let matched_level = lists::find_matching_list_level(
425 &self.containers,
426 &prepared.marker,
427 prepared.indent_cols,
428 );
429 let list_item = ListItemEmissionInput {
430 content,
431 marker_len: prepared.marker_len,
432 spaces_after_cols: prepared.spaces_after_cols,
433 spaces_after_bytes: prepared.spaces_after,
434 indent_cols: prepared.indent_cols,
435 indent_bytes: prepared.indent_bytes,
436 };
437 let current_content_col = paragraphs::current_content_col(&self.containers);
438 let deep_ordered_matched_level = matched_level
439 .and_then(|level| self.containers.stack.get(level).map(|c| (level, c)))
440 .and_then(|(level, container)| match container {
441 Container::List {
442 marker: list_marker,
443 base_indent_cols,
444 ..
445 } if matches!(
446 (&prepared.marker, list_marker),
447 (ListMarker::Ordered(_), ListMarker::Ordered(_))
448 ) && prepared.indent_cols >= 4
449 && *base_indent_cols >= 4
450 && prepared.indent_cols.abs_diff(*base_indent_cols) <= 3 =>
451 {
452 Some(level)
453 }
454 _ => None,
455 });
456
457 if deep_ordered_matched_level.is_none()
458 && current_content_col > 0
459 && prepared.indent_cols >= current_content_col
460 {
461 if let Some(level) = matched_level
462 && let Some(Container::List {
463 base_indent_cols, ..
464 }) = self.containers.stack.get(level)
465 && prepared.indent_cols == *base_indent_cols
466 {
467 let num_parent_lists = self.containers.stack[..level]
468 .iter()
469 .filter(|c| matches!(c, Container::List { .. }))
470 .count();
471
472 if num_parent_lists > 0 {
473 self.close_containers_to(level + 1);
474
475 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
476 self.close_containers_to(self.containers.depth() - 1);
477 }
478 if matches!(self.containers.last(), Some(Container::ListItem { .. })) {
479 self.close_containers_to(self.containers.depth() - 1);
480 }
481
482 if let Some(indent_str) = indent_to_emit {
483 self.builder
484 .token(SyntaxKind::WHITESPACE.into(), indent_str);
485 }
486
487 if let Some(nested_marker) = prepared.nested_marker {
488 lists::add_list_item_with_nested_empty_list(
489 &mut self.containers,
490 &mut self.builder,
491 &list_item,
492 nested_marker,
493 );
494 } else {
495 lists::add_list_item(&mut self.containers, &mut self.builder, &list_item);
496 }
497 return;
498 }
499 }
500
501 self.emit_list_item_buffer_if_needed();
502
503 start_nested_list(
504 &mut self.containers,
505 &mut self.builder,
506 &prepared.marker,
507 &list_item,
508 indent_to_emit,
509 );
510 return;
511 }
512
513 if let Some(level) = matched_level {
514 self.close_containers_to(level + 1);
515
516 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
517 self.close_containers_to(self.containers.depth() - 1);
518 }
519 if matches!(self.containers.last(), Some(Container::ListItem { .. })) {
520 self.close_containers_to(self.containers.depth() - 1);
521 }
522
523 if let Some(indent_str) = indent_to_emit {
524 self.builder
525 .token(SyntaxKind::WHITESPACE.into(), indent_str);
526 }
527
528 if let Some(nested_marker) = prepared.nested_marker {
529 lists::add_list_item_with_nested_empty_list(
530 &mut self.containers,
531 &mut self.builder,
532 &list_item,
533 nested_marker,
534 );
535 } else {
536 lists::add_list_item(&mut self.containers, &mut self.builder, &list_item);
537 }
538 return;
539 }
540
541 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
542 self.close_containers_to(self.containers.depth() - 1);
543 }
544 while matches!(self.containers.last(), Some(Container::ListItem { .. })) {
545 self.close_containers_to(self.containers.depth() - 1);
546 }
547 while matches!(self.containers.last(), Some(Container::List { .. })) {
548 self.close_containers_to(self.containers.depth() - 1);
549 }
550
551 self.builder.start_node(SyntaxKind::LIST.into());
552 if let Some(indent_str) = indent_to_emit {
553 self.builder
554 .token(SyntaxKind::WHITESPACE.into(), indent_str);
555 }
556 self.containers.push(Container::List {
557 marker: prepared.marker.clone(),
558 base_indent_cols: prepared.indent_cols,
559 has_blank_between_items: false,
560 });
561
562 if let Some(nested_marker) = prepared.nested_marker {
563 lists::add_list_item_with_nested_empty_list(
564 &mut self.containers,
565 &mut self.builder,
566 &list_item,
567 nested_marker,
568 );
569 } else {
570 lists::add_list_item(&mut self.containers, &mut self.builder, &list_item);
571 }
572 }
573
574 fn handle_definition_list_effect(
575 &mut self,
576 block_match: &super::block_dispatcher::PreparedBlockMatch,
577 content: &str,
578 indent_to_emit: Option<&str>,
579 ) {
580 use super::block_dispatcher::DefinitionPrepared;
581
582 let prepared = block_match
583 .payload
584 .as_ref()
585 .and_then(|p| p.downcast_ref::<DefinitionPrepared>());
586 let Some(prepared) = prepared else {
587 return;
588 };
589
590 match prepared {
591 DefinitionPrepared::Definition {
592 marker_char,
593 indent,
594 spaces_after,
595 spaces_after_cols,
596 has_content,
597 } => {
598 self.emit_buffered_plain_if_needed();
599
600 while matches!(self.containers.last(), Some(Container::ListItem { .. })) {
601 self.close_containers_to(self.containers.depth() - 1);
602 }
603 while matches!(self.containers.last(), Some(Container::List { .. })) {
604 self.close_containers_to(self.containers.depth() - 1);
605 }
606
607 if matches!(self.containers.last(), Some(Container::Definition { .. })) {
608 self.close_containers_to(self.containers.depth() - 1);
609 }
610
611 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
612 self.close_containers_to(self.containers.depth() - 1);
613 }
614
615 if definition_lists::in_definition_list(&self.containers)
619 && !matches!(
620 self.containers.last(),
621 Some(Container::DefinitionItem { .. })
622 )
623 {
624 self.builder.start_node(SyntaxKind::DEFINITION_ITEM.into());
625 self.containers.push(Container::DefinitionItem {});
626 }
627
628 if !definition_lists::in_definition_list(&self.containers) {
629 self.builder.start_node(SyntaxKind::DEFINITION_LIST.into());
630 self.containers.push(Container::DefinitionList {});
631 }
632
633 if !matches!(
634 self.containers.last(),
635 Some(Container::DefinitionItem { .. })
636 ) {
637 self.builder.start_node(SyntaxKind::DEFINITION_ITEM.into());
638 self.containers.push(Container::DefinitionItem {});
639 }
640
641 self.builder.start_node(SyntaxKind::DEFINITION.into());
642
643 if let Some(indent_str) = indent_to_emit {
644 self.builder
645 .token(SyntaxKind::WHITESPACE.into(), indent_str);
646 }
647
648 emit_definition_marker(&mut self.builder, *marker_char, *indent);
649 let indent_bytes = byte_index_at_column(content, *indent);
650 if *spaces_after > 0 {
651 let space_start = indent_bytes + 1;
652 let space_end = space_start + *spaces_after;
653 if space_end <= content.len() {
654 self.builder.token(
655 SyntaxKind::WHITESPACE.into(),
656 &content[space_start..space_end],
657 );
658 }
659 }
660
661 if !*has_content {
662 let current_line = self.lines[self.pos];
663 let (_, newline_str) = strip_newline(current_line);
664 if !newline_str.is_empty() {
665 self.builder.token(SyntaxKind::NEWLINE.into(), newline_str);
666 }
667 }
668
669 let content_col = *indent + 1 + *spaces_after_cols;
670 let content_start_bytes = indent_bytes + 1 + *spaces_after;
671 let after_marker_and_spaces = content.get(content_start_bytes..).unwrap_or("");
672 let mut plain_buffer = TextBuffer::new();
673 let mut definition_pushed = false;
674
675 if *has_content {
676 let current_line = self.lines[self.pos];
677 let (trimmed_line, _) = strip_newline(current_line);
678
679 let content_start = content_start_bytes.min(trimmed_line.len());
680 let content_slice = &trimmed_line[content_start..];
681 let content_line = ¤t_line[content_start_bytes.min(current_line.len())..];
682
683 let (blockquote_depth, inner_blockquote_content) =
684 count_blockquote_markers(content_line);
685
686 let should_start_list_from_first_line = self
687 .lines
688 .get(self.pos + 1)
689 .map(|next_line| {
690 let (next_without_newline, _) = strip_newline(next_line);
691 if next_without_newline.trim().is_empty() {
692 return false;
693 }
694
695 let (next_indent_cols, _) = leading_indent(next_without_newline);
696 next_indent_cols >= content_col
697 })
698 .unwrap_or(false);
699
700 if blockquote_depth > 0 {
701 self.containers.push(Container::Definition {
702 content_col,
703 plain_open: false,
704 plain_buffer: TextBuffer::new(),
705 });
706 definition_pushed = true;
707
708 let marker_info = parse_blockquote_marker_info(content_line);
709 for level in 0..blockquote_depth {
710 self.builder.start_node(SyntaxKind::BLOCK_QUOTE.into());
711 if let Some(info) = marker_info.get(level) {
712 blockquotes::emit_one_blockquote_marker(
713 &mut self.builder,
714 info.leading_spaces,
715 info.has_trailing_space,
716 );
717 }
718 self.containers.push(Container::BlockQuote {});
719 }
720
721 if !inner_blockquote_content.trim().is_empty() {
722 paragraphs::start_paragraph_if_needed(
723 &mut self.containers,
724 &mut self.builder,
725 );
726 paragraphs::append_paragraph_line(
727 &mut self.containers,
728 &mut self.builder,
729 inner_blockquote_content,
730 self.config,
731 );
732 }
733 } else if let Some(marker_match) =
734 try_parse_list_marker(content_slice, self.config)
735 && should_start_list_from_first_line
736 {
737 self.containers.push(Container::Definition {
738 content_col,
739 plain_open: false,
740 plain_buffer: TextBuffer::new(),
741 });
742 definition_pushed = true;
743
744 let (indent_cols, indent_bytes) = leading_indent(content_line);
745 self.builder.start_node(SyntaxKind::LIST.into());
746 self.containers.push(Container::List {
747 marker: marker_match.marker.clone(),
748 base_indent_cols: indent_cols,
749 has_blank_between_items: false,
750 });
751
752 let list_item = ListItemEmissionInput {
753 content: content_line,
754 marker_len: marker_match.marker_len,
755 spaces_after_cols: marker_match.spaces_after_cols,
756 spaces_after_bytes: marker_match.spaces_after_bytes,
757 indent_cols,
758 indent_bytes,
759 };
760
761 if let Some(nested_marker) = is_content_nested_bullet_marker(
762 content_line,
763 marker_match.marker_len,
764 marker_match.spaces_after_bytes,
765 ) {
766 lists::add_list_item_with_nested_empty_list(
767 &mut self.containers,
768 &mut self.builder,
769 &list_item,
770 nested_marker,
771 );
772 } else {
773 lists::add_list_item(
774 &mut self.containers,
775 &mut self.builder,
776 &list_item,
777 );
778 }
779 } else if let Some(fence) = code_blocks::try_parse_fence_open(content_slice) {
780 self.containers.push(Container::Definition {
781 content_col,
782 plain_open: false,
783 plain_buffer: TextBuffer::new(),
784 });
785 definition_pushed = true;
786
787 let bq_depth = self.current_blockquote_depth();
788 if let Some(indent_str) = indent_to_emit {
789 self.builder
790 .token(SyntaxKind::WHITESPACE.into(), indent_str);
791 }
792 let fence_line = current_line[content_start..].to_string();
793 let new_pos = if self.config.extensions.tex_math_gfm
794 && code_blocks::is_gfm_math_fence(&fence)
795 {
796 code_blocks::parse_fenced_math_block(
797 &mut self.builder,
798 &self.lines,
799 self.pos,
800 fence,
801 bq_depth,
802 content_col,
803 Some(&fence_line),
804 )
805 } else {
806 code_blocks::parse_fenced_code_block(
807 &mut self.builder,
808 &self.lines,
809 self.pos,
810 fence,
811 bq_depth,
812 content_col,
813 Some(&fence_line),
814 )
815 };
816 self.pos = new_pos - 1;
817 } else {
818 let (_, newline_str) = strip_newline(current_line);
819 let (content_without_newline, _) = strip_newline(after_marker_and_spaces);
820 if content_without_newline.is_empty() {
821 plain_buffer.push_line(newline_str);
822 } else {
823 let line_with_newline = if !newline_str.is_empty() {
824 format!("{}{}", content_without_newline, newline_str)
825 } else {
826 content_without_newline.to_string()
827 };
828 plain_buffer.push_line(line_with_newline);
829 }
830 }
831 }
832
833 if !definition_pushed {
834 self.containers.push(Container::Definition {
835 content_col,
836 plain_open: *has_content,
837 plain_buffer,
838 });
839 }
840 }
841 DefinitionPrepared::Term { blank_count } => {
842 self.emit_buffered_plain_if_needed();
843
844 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
845 self.close_containers_to(self.containers.depth() - 1);
846 }
847
848 if !definition_lists::in_definition_list(&self.containers) {
849 self.builder.start_node(SyntaxKind::DEFINITION_LIST.into());
850 self.containers.push(Container::DefinitionList {});
851 }
852
853 while matches!(
854 self.containers.last(),
855 Some(Container::Definition { .. }) | Some(Container::DefinitionItem { .. })
856 ) {
857 self.close_containers_to(self.containers.depth() - 1);
858 }
859
860 self.builder.start_node(SyntaxKind::DEFINITION_ITEM.into());
861 self.containers.push(Container::DefinitionItem {});
862
863 emit_term(&mut self.builder, content, self.config);
864
865 for i in 0..*blank_count {
866 let blank_pos = self.pos + 1 + i;
867 if blank_pos < self.lines.len() {
868 let blank_line = self.lines[blank_pos];
869 self.builder.start_node(SyntaxKind::BLANK_LINE.into());
870 self.builder
871 .token(SyntaxKind::BLANK_LINE.into(), blank_line);
872 self.builder.finish_node();
873 }
874 }
875 self.pos += *blank_count;
876 }
877 }
878 }
879
880 fn blockquote_marker_info(
882 &self,
883 payload: Option<&BlockQuotePrepared>,
884 line: &str,
885 ) -> Vec<marker_utils::BlockQuoteMarkerInfo> {
886 payload
887 .map(|payload| payload.marker_info.clone())
888 .unwrap_or_else(|| parse_blockquote_marker_info(line))
889 }
890
891 fn emit_blockquote_markers(
892 &mut self,
893 marker_info: &[marker_utils::BlockQuoteMarkerInfo],
894 depth: usize,
895 ) {
896 for i in 0..depth {
897 if let Some(info) = marker_info.get(i) {
898 blockquotes::emit_one_blockquote_marker(
899 &mut self.builder,
900 info.leading_spaces,
901 info.has_trailing_space,
902 );
903 }
904 }
905 }
906
907 fn current_blockquote_depth(&self) -> usize {
908 blockquotes::current_blockquote_depth(&self.containers)
909 }
910
911 fn emit_or_buffer_blockquote_marker(
916 &mut self,
917 leading_spaces: usize,
918 has_trailing_space: bool,
919 ) {
920 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
922 paragraphs::append_paragraph_marker(
924 &mut self.containers,
925 leading_spaces,
926 has_trailing_space,
927 );
928 } else {
929 blockquotes::emit_one_blockquote_marker(
931 &mut self.builder,
932 leading_spaces,
933 has_trailing_space,
934 );
935 }
936 }
937
938 fn parse_document_stack(&mut self) {
939 self.builder.start_node(SyntaxKind::DOCUMENT.into());
940
941 log::debug!("Starting document parse");
942
943 while self.pos < self.lines.len() {
946 let line = self.lines[self.pos];
947
948 log::debug!("Parsing line {}: {}", self.pos + 1, line);
949
950 if self.parse_line(line) {
951 continue;
952 }
953 self.pos += 1;
954 }
955
956 self.close_containers_to(0);
957 self.builder.finish_node(); }
959
960 fn parse_line(&mut self, line: &str) -> bool {
962 let (bq_depth, inner_content) = count_blockquote_markers(line);
964 let current_bq_depth = self.current_blockquote_depth();
965
966 let has_blank_before = self.pos == 0 || self.lines[self.pos - 1].trim().is_empty();
967 let mut blockquote_match: Option<PreparedBlockMatch> = None;
968 let dispatcher_ctx = if current_bq_depth == 0 {
969 Some(BlockContext {
970 content: line,
971 has_blank_before,
972 has_blank_before_strict: has_blank_before,
973 at_document_start: self.pos == 0,
974 in_fenced_div: self.in_fenced_div(),
975 blockquote_depth: current_bq_depth,
976 config: self.config,
977 content_indent: 0,
978 indent_to_emit: None,
979 list_indent_info: None,
980 in_list: lists::in_list(&self.containers),
981 next_line: if self.pos + 1 < self.lines.len() {
982 Some(self.lines[self.pos + 1])
983 } else {
984 None
985 },
986 })
987 } else {
988 None
989 };
990
991 let blockquote_payload = if let Some(dispatcher_ctx) = dispatcher_ctx.as_ref() {
992 self.block_registry
993 .detect_prepared(dispatcher_ctx, &self.lines, self.pos)
994 .and_then(|prepared| {
995 if matches!(prepared.effect, BlockEffect::OpenBlockQuote) {
996 blockquote_match = Some(prepared);
997 blockquote_match.as_ref().and_then(|prepared| {
998 prepared
999 .payload
1000 .as_ref()
1001 .and_then(|payload| payload.downcast_ref::<BlockQuotePrepared>())
1002 .cloned()
1003 })
1004 } else {
1005 None
1006 }
1007 })
1008 } else {
1009 None
1010 };
1011
1012 log::debug!(
1013 "parse_line [{}]: bq_depth={}, current_bq={}, depth={}, line={:?}",
1014 self.pos,
1015 bq_depth,
1016 current_bq_depth,
1017 self.containers.depth(),
1018 line.trim_end()
1019 );
1020
1021 let is_blank = line.trim_end_matches('\n').trim().is_empty()
1024 || (bq_depth > 0 && inner_content.trim_end_matches('\n').trim().is_empty());
1025
1026 if is_blank {
1027 if self.is_paragraph_open()
1028 && paragraphs::has_open_inline_math_environment(&self.containers)
1029 {
1030 paragraphs::append_paragraph_line(
1031 &mut self.containers,
1032 &mut self.builder,
1033 line,
1034 self.config,
1035 );
1036 self.pos += 1;
1037 return true;
1038 }
1039
1040 self.close_paragraph_if_open();
1042
1043 self.emit_buffered_plain_if_needed();
1047
1048 if bq_depth > current_bq_depth {
1054 for _ in current_bq_depth..bq_depth {
1056 self.builder.start_node(SyntaxKind::BLOCK_QUOTE.into());
1057 self.containers.push(Container::BlockQuote {});
1058 }
1059 } else if bq_depth < current_bq_depth {
1060 self.close_blockquotes_to_depth(bq_depth);
1062 }
1063
1064 let mut peek = self.pos + 1;
1066 while peek < self.lines.len() && self.lines[peek].trim().is_empty() {
1067 peek += 1;
1068 }
1069
1070 let levels_to_keep = if peek < self.lines.len() {
1072 ContinuationPolicy::new(self.config, &self.block_registry).compute_levels_to_keep(
1073 self.current_blockquote_depth(),
1074 &self.containers,
1075 &self.lines,
1076 peek,
1077 self.lines[peek],
1078 )
1079 } else {
1080 0
1081 };
1082 log::trace!(
1083 "Blank line: depth={}, levels_to_keep={}, next='{}'",
1084 self.containers.depth(),
1085 levels_to_keep,
1086 if peek < self.lines.len() {
1087 self.lines[peek]
1088 } else {
1089 "<EOF>"
1090 }
1091 );
1092
1093 while self.containers.depth() > levels_to_keep {
1097 match self.containers.last() {
1098 Some(Container::ListItem { .. }) => {
1099 log::debug!(
1101 "Closing ListItem at blank line (levels_to_keep={} < depth={})",
1102 levels_to_keep,
1103 self.containers.depth()
1104 );
1105 self.close_containers_to(self.containers.depth() - 1);
1106 }
1107 Some(Container::List { .. })
1108 | Some(Container::FootnoteDefinition { .. })
1109 | Some(Container::Alert { .. })
1110 | Some(Container::Paragraph { .. })
1111 | Some(Container::Definition { .. })
1112 | Some(Container::DefinitionItem { .. })
1113 | Some(Container::DefinitionList { .. }) => {
1114 log::debug!(
1115 "Closing {:?} at blank line (depth {} > levels_to_keep {})",
1116 self.containers.last(),
1117 self.containers.depth(),
1118 levels_to_keep
1119 );
1120
1121 self.close_containers_to(self.containers.depth() - 1);
1122 }
1123 _ => break,
1124 }
1125 }
1126
1127 if matches!(self.containers.last(), Some(Container::ListItem { .. })) {
1131 self.emit_list_item_buffer_if_needed();
1132 }
1133
1134 if bq_depth > 0 {
1136 let marker_info = self.blockquote_marker_info(blockquote_payload.as_ref(), line);
1137 self.emit_blockquote_markers(&marker_info, bq_depth);
1138 }
1139
1140 self.builder.start_node(SyntaxKind::BLANK_LINE.into());
1141 self.builder
1142 .token(SyntaxKind::BLANK_LINE.into(), inner_content);
1143 self.builder.finish_node();
1144
1145 self.pos += 1;
1146 return true;
1147 }
1148
1149 if bq_depth > current_bq_depth {
1151 if self.config.extensions.blank_before_blockquote
1154 && current_bq_depth == 0
1155 && !blockquote_payload
1156 .as_ref()
1157 .map(|payload| payload.can_start)
1158 .unwrap_or_else(|| blockquotes::can_start_blockquote(self.pos, &self.lines))
1159 {
1160 self.emit_list_item_buffer_if_needed();
1164 paragraphs::start_paragraph_if_needed(&mut self.containers, &mut self.builder);
1165 paragraphs::append_paragraph_line(
1166 &mut self.containers,
1167 &mut self.builder,
1168 line,
1169 self.config,
1170 );
1171 self.pos += 1;
1172 return true;
1173 }
1174
1175 let can_nest = if current_bq_depth > 0 {
1178 if self.config.extensions.blank_before_blockquote {
1179 matches!(self.containers.last(), Some(Container::BlockQuote { .. }))
1181 || (self.pos > 0 && {
1182 let prev_line = self.lines[self.pos - 1];
1183 let (prev_bq_depth, prev_inner) = count_blockquote_markers(prev_line);
1184 prev_bq_depth >= current_bq_depth && prev_inner.trim().is_empty()
1185 })
1186 } else {
1187 true
1188 }
1189 } else {
1190 blockquote_payload
1191 .as_ref()
1192 .map(|payload| payload.can_nest)
1193 .unwrap_or(true)
1194 };
1195
1196 if !can_nest {
1197 let content_at_current_depth =
1200 blockquotes::strip_n_blockquote_markers(line, current_bq_depth);
1201
1202 let marker_info = self.blockquote_marker_info(blockquote_payload.as_ref(), line);
1204 for i in 0..current_bq_depth {
1205 if let Some(info) = marker_info.get(i) {
1206 self.emit_or_buffer_blockquote_marker(
1207 info.leading_spaces,
1208 info.has_trailing_space,
1209 );
1210 }
1211 }
1212
1213 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1214 paragraphs::append_paragraph_line(
1216 &mut self.containers,
1217 &mut self.builder,
1218 content_at_current_depth,
1219 self.config,
1220 );
1221 self.pos += 1;
1222 return true;
1223 } else {
1224 paragraphs::start_paragraph_if_needed(&mut self.containers, &mut self.builder);
1226 paragraphs::append_paragraph_line(
1227 &mut self.containers,
1228 &mut self.builder,
1229 content_at_current_depth,
1230 self.config,
1231 );
1232 self.pos += 1;
1233 return true;
1234 }
1235 }
1236
1237 self.emit_list_item_buffer_if_needed();
1240
1241 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1243 self.close_containers_to(self.containers.depth() - 1);
1244 }
1245
1246 let marker_info = self.blockquote_marker_info(blockquote_payload.as_ref(), line);
1248
1249 if let (Some(dispatcher_ctx), Some(prepared)) =
1250 (dispatcher_ctx.as_ref(), blockquote_match.as_ref())
1251 {
1252 let _ = self.block_registry.parse_prepared(
1253 prepared,
1254 dispatcher_ctx,
1255 &mut self.builder,
1256 &self.lines,
1257 self.pos,
1258 );
1259 for _ in 0..bq_depth {
1260 self.containers.push(Container::BlockQuote {});
1261 }
1262 } else {
1263 for level in 0..current_bq_depth {
1265 if let Some(info) = marker_info.get(level) {
1266 self.emit_or_buffer_blockquote_marker(
1267 info.leading_spaces,
1268 info.has_trailing_space,
1269 );
1270 }
1271 }
1272
1273 for level in current_bq_depth..bq_depth {
1275 self.builder.start_node(SyntaxKind::BLOCK_QUOTE.into());
1276
1277 if let Some(info) = marker_info.get(level) {
1279 blockquotes::emit_one_blockquote_marker(
1280 &mut self.builder,
1281 info.leading_spaces,
1282 info.has_trailing_space,
1283 );
1284 }
1285
1286 self.containers.push(Container::BlockQuote {});
1287 }
1288 }
1289
1290 return self.parse_inner_content(inner_content, Some(inner_content));
1293 } else if bq_depth < current_bq_depth {
1294 if bq_depth == 0 {
1297 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1299 paragraphs::append_paragraph_line(
1300 &mut self.containers,
1301 &mut self.builder,
1302 line,
1303 self.config,
1304 );
1305 self.pos += 1;
1306 return true;
1307 }
1308
1309 if lists::in_blockquote_list(&self.containers)
1312 && let Some(marker_match) = try_parse_list_marker(line, self.config)
1313 {
1314 let (indent_cols, indent_bytes) = leading_indent(line);
1315 if let Some(level) = lists::find_matching_list_level(
1316 &self.containers,
1317 &marker_match.marker,
1318 indent_cols,
1319 ) {
1320 self.close_containers_to(level + 1);
1323
1324 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1326 self.close_containers_to(self.containers.depth() - 1);
1327 }
1328 if matches!(self.containers.last(), Some(Container::ListItem { .. })) {
1329 self.close_containers_to(self.containers.depth() - 1);
1330 }
1331
1332 if let Some(nested_marker) = is_content_nested_bullet_marker(
1334 line,
1335 marker_match.marker_len,
1336 marker_match.spaces_after_bytes,
1337 ) {
1338 let list_item = ListItemEmissionInput {
1339 content: line,
1340 marker_len: marker_match.marker_len,
1341 spaces_after_cols: marker_match.spaces_after_cols,
1342 spaces_after_bytes: marker_match.spaces_after_bytes,
1343 indent_cols,
1344 indent_bytes,
1345 };
1346 lists::add_list_item_with_nested_empty_list(
1347 &mut self.containers,
1348 &mut self.builder,
1349 &list_item,
1350 nested_marker,
1351 );
1352 } else {
1353 let list_item = ListItemEmissionInput {
1354 content: line,
1355 marker_len: marker_match.marker_len,
1356 spaces_after_cols: marker_match.spaces_after_cols,
1357 spaces_after_bytes: marker_match.spaces_after_bytes,
1358 indent_cols,
1359 indent_bytes,
1360 };
1361 lists::add_list_item(
1362 &mut self.containers,
1363 &mut self.builder,
1364 &list_item,
1365 );
1366 }
1367 self.pos += 1;
1368 return true;
1369 }
1370 }
1371 }
1372
1373 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1375 self.close_containers_to(self.containers.depth() - 1);
1376 }
1377
1378 self.close_blockquotes_to_depth(bq_depth);
1380
1381 if bq_depth > 0 {
1383 let marker_info = parse_blockquote_marker_info(line);
1385 for i in 0..bq_depth {
1386 if let Some(info) = marker_info.get(i) {
1387 self.emit_or_buffer_blockquote_marker(
1388 info.leading_spaces,
1389 info.has_trailing_space,
1390 );
1391 }
1392 }
1393 return self.parse_inner_content(inner_content, Some(inner_content));
1395 } else {
1396 return self.parse_inner_content(line, None);
1398 }
1399 } else if bq_depth > 0 {
1400 let mut list_item_continuation = false;
1402
1403 if matches!(
1406 self.containers.last(),
1407 Some(Container::ListItem { content_col: _, .. })
1408 ) {
1409 let (indent_cols, _) = leading_indent(inner_content);
1410 let content_indent = self.content_container_indent_to_strip();
1411 let effective_indent = indent_cols.saturating_sub(content_indent);
1412 let content_col = match self.containers.last() {
1413 Some(Container::ListItem { content_col, .. }) => *content_col,
1414 _ => 0,
1415 };
1416
1417 let is_new_item_at_outer_level =
1419 if try_parse_list_marker(inner_content, self.config).is_some() {
1420 effective_indent < content_col
1421 } else {
1422 false
1423 };
1424
1425 if is_new_item_at_outer_level || effective_indent < content_col {
1429 log::debug!(
1430 "Closing ListItem: is_new_item={}, effective_indent={} < content_col={}",
1431 is_new_item_at_outer_level,
1432 effective_indent,
1433 content_col
1434 );
1435 self.close_containers_to(self.containers.depth() - 1);
1436 } else {
1437 log::debug!(
1438 "Keeping ListItem: effective_indent={} >= content_col={}",
1439 effective_indent,
1440 content_col
1441 );
1442 list_item_continuation = true;
1443 }
1444 }
1445
1446 if list_item_continuation && code_blocks::try_parse_fence_open(inner_content).is_some()
1450 {
1451 list_item_continuation = false;
1452 }
1453
1454 if !list_item_continuation {
1455 let marker_info = parse_blockquote_marker_info(line);
1456 for i in 0..bq_depth {
1457 if let Some(info) = marker_info.get(i) {
1458 self.emit_or_buffer_blockquote_marker(
1459 info.leading_spaces,
1460 info.has_trailing_space,
1461 );
1462 }
1463 }
1464 }
1465 let line_to_append = if list_item_continuation {
1468 Some(line)
1469 } else {
1470 Some(inner_content)
1471 };
1472 return self.parse_inner_content(inner_content, line_to_append);
1473 }
1474
1475 if current_bq_depth > 0 {
1478 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1480 paragraphs::append_paragraph_line(
1481 &mut self.containers,
1482 &mut self.builder,
1483 line,
1484 self.config,
1485 );
1486 self.pos += 1;
1487 return true;
1488 }
1489
1490 if lists::in_blockquote_list(&self.containers)
1492 && let Some(marker_match) = try_parse_list_marker(line, self.config)
1493 {
1494 let (indent_cols, indent_bytes) = leading_indent(line);
1495 if let Some(level) = lists::find_matching_list_level(
1496 &self.containers,
1497 &marker_match.marker,
1498 indent_cols,
1499 ) {
1500 self.close_containers_to(level + 1);
1502
1503 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1505 self.close_containers_to(self.containers.depth() - 1);
1506 }
1507 if matches!(self.containers.last(), Some(Container::ListItem { .. })) {
1508 self.close_containers_to(self.containers.depth() - 1);
1509 }
1510
1511 if let Some(nested_marker) = is_content_nested_bullet_marker(
1513 line,
1514 marker_match.marker_len,
1515 marker_match.spaces_after_bytes,
1516 ) {
1517 let list_item = ListItemEmissionInput {
1518 content: line,
1519 marker_len: marker_match.marker_len,
1520 spaces_after_cols: marker_match.spaces_after_cols,
1521 spaces_after_bytes: marker_match.spaces_after_bytes,
1522 indent_cols,
1523 indent_bytes,
1524 };
1525 lists::add_list_item_with_nested_empty_list(
1526 &mut self.containers,
1527 &mut self.builder,
1528 &list_item,
1529 nested_marker,
1530 );
1531 } else {
1532 let list_item = ListItemEmissionInput {
1533 content: line,
1534 marker_len: marker_match.marker_len,
1535 spaces_after_cols: marker_match.spaces_after_cols,
1536 spaces_after_bytes: marker_match.spaces_after_bytes,
1537 indent_cols,
1538 indent_bytes,
1539 };
1540 lists::add_list_item(&mut self.containers, &mut self.builder, &list_item);
1541 }
1542 self.pos += 1;
1543 return true;
1544 }
1545 }
1546 }
1547
1548 self.parse_inner_content(line, None)
1550 }
1551
1552 fn content_container_indent_to_strip(&self) -> usize {
1554 self.containers
1555 .stack
1556 .iter()
1557 .filter_map(|c| match c {
1558 Container::FootnoteDefinition { content_col, .. } => Some(*content_col),
1559 Container::Definition { content_col, .. } => Some(*content_col),
1560 _ => None,
1561 })
1562 .sum()
1563 }
1564
1565 fn parse_inner_content(&mut self, content: &str, line_to_append: Option<&str>) -> bool {
1571 log::debug!(
1572 "parse_inner_content [{}]: depth={}, last={:?}, content={:?}",
1573 self.pos,
1574 self.containers.depth(),
1575 self.containers.last(),
1576 content.trim_end()
1577 );
1578 let content_indent = self.content_container_indent_to_strip();
1581 let (stripped_content, indent_to_emit) = if content_indent > 0 {
1582 let (indent_cols, _) = leading_indent(content);
1583 if indent_cols >= content_indent {
1584 let idx = byte_index_at_column(content, content_indent);
1585 (&content[idx..], Some(&content[..idx]))
1586 } else {
1587 let trimmed_start = content.trim_start();
1589 let ws_len = content.len() - trimmed_start.len();
1590 if ws_len > 0 {
1591 (trimmed_start, Some(&content[..ws_len]))
1592 } else {
1593 (content, None)
1594 }
1595 }
1596 } else {
1597 (content, None)
1598 };
1599
1600 if self.config.extensions.alerts
1601 && self.current_blockquote_depth() > 0
1602 && !self.in_active_alert()
1603 && !self.is_paragraph_open()
1604 && let Some(marker) = Self::alert_marker_from_content(stripped_content)
1605 {
1606 let (_, newline_str) = strip_newline(stripped_content);
1607 self.builder.start_node(SyntaxKind::ALERT.into());
1608 self.builder.token(SyntaxKind::ALERT_MARKER.into(), marker);
1609 if !newline_str.is_empty() {
1610 self.builder.token(SyntaxKind::NEWLINE.into(), newline_str);
1611 }
1612 self.containers.push(Container::Alert {
1613 blockquote_depth: self.current_blockquote_depth(),
1614 });
1615 self.pos += 1;
1616 return true;
1617 }
1618
1619 if matches!(self.containers.last(), Some(Container::Definition { .. })) {
1623 let is_definition_marker =
1624 definition_lists::try_parse_definition_marker(stripped_content).is_some()
1625 && !stripped_content.starts_with(':');
1626 if content_indent == 0 && is_definition_marker {
1627 } else {
1629 let policy = ContinuationPolicy::new(self.config, &self.block_registry);
1630
1631 if policy.definition_plain_can_continue(
1632 stripped_content,
1633 content,
1634 content_indent,
1635 &BlockContext {
1636 content: stripped_content,
1637 has_blank_before: self.pos == 0
1638 || self.lines[self.pos - 1].trim().is_empty(),
1639 has_blank_before_strict: self.pos == 0
1640 || self.lines[self.pos - 1].trim().is_empty(),
1641 at_document_start: self.pos == 0 && self.current_blockquote_depth() == 0,
1642 in_fenced_div: self.in_fenced_div(),
1643 blockquote_depth: self.current_blockquote_depth(),
1644 config: self.config,
1645 content_indent,
1646 indent_to_emit: None,
1647 list_indent_info: None,
1648 in_list: lists::in_list(&self.containers),
1649 next_line: if self.pos + 1 < self.lines.len() {
1650 Some(self.lines[self.pos + 1])
1651 } else {
1652 None
1653 },
1654 },
1655 &self.lines,
1656 self.pos,
1657 ) {
1658 let content_line = stripped_content;
1659 let (text_without_newline, newline_str) = strip_newline(content_line);
1660 let indent_prefix = if !text_without_newline.trim().is_empty() {
1661 indent_to_emit.unwrap_or("")
1662 } else {
1663 ""
1664 };
1665 let content_line = format!("{}{}", indent_prefix, text_without_newline);
1666
1667 if let Some(Container::Definition {
1668 plain_open,
1669 plain_buffer,
1670 ..
1671 }) = self.containers.stack.last_mut()
1672 {
1673 let line_with_newline = if !newline_str.is_empty() {
1674 format!("{}{}", content_line, newline_str)
1675 } else {
1676 content_line
1677 };
1678 plain_buffer.push_line(line_with_newline);
1679 *plain_open = true;
1680 }
1681
1682 self.pos += 1;
1683 return true;
1684 }
1685 }
1686 }
1687
1688 if content_indent > 0 {
1691 let (bq_depth, inner_content) = count_blockquote_markers(stripped_content);
1692 let current_bq_depth = self.current_blockquote_depth();
1693
1694 if bq_depth > 0 {
1695 self.emit_buffered_plain_if_needed();
1698 self.emit_list_item_buffer_if_needed();
1699
1700 self.close_paragraph_if_open();
1703
1704 if bq_depth > current_bq_depth {
1705 let marker_info = parse_blockquote_marker_info(stripped_content);
1706
1707 for level in current_bq_depth..bq_depth {
1709 self.builder.start_node(SyntaxKind::BLOCK_QUOTE.into());
1710
1711 if level == current_bq_depth
1712 && let Some(indent_str) = indent_to_emit
1713 {
1714 self.builder
1715 .token(SyntaxKind::WHITESPACE.into(), indent_str);
1716 }
1717
1718 if let Some(info) = marker_info.get(level) {
1719 blockquotes::emit_one_blockquote_marker(
1720 &mut self.builder,
1721 info.leading_spaces,
1722 info.has_trailing_space,
1723 );
1724 }
1725
1726 self.containers.push(Container::BlockQuote {});
1727 }
1728 } else if bq_depth < current_bq_depth {
1729 self.close_blockquotes_to_depth(bq_depth);
1730 } else {
1731 let marker_info = parse_blockquote_marker_info(stripped_content);
1733 self.emit_blockquote_markers(&marker_info, bq_depth);
1734 }
1735
1736 return self.parse_inner_content(inner_content, Some(inner_content));
1737 }
1738 }
1739
1740 let content = stripped_content;
1742
1743 if self.is_paragraph_open()
1744 && (paragraphs::has_open_inline_math_environment(&self.containers)
1745 || paragraphs::has_open_display_math_dollars(&self.containers))
1746 {
1747 paragraphs::append_paragraph_line(
1748 &mut self.containers,
1749 &mut self.builder,
1750 line_to_append.unwrap_or(self.lines[self.pos]),
1751 self.config,
1752 );
1753 self.pos += 1;
1754 return true;
1755 }
1756
1757 use super::blocks::lists;
1761 use super::blocks::paragraphs;
1762 let list_indent_info = if lists::in_list(&self.containers) {
1763 let content_col = paragraphs::current_content_col(&self.containers);
1764 if content_col > 0 {
1765 Some(super::block_dispatcher::ListIndentInfo { content_col })
1766 } else {
1767 None
1768 }
1769 } else {
1770 None
1771 };
1772
1773 let next_line = if self.pos + 1 < self.lines.len() {
1774 Some(count_blockquote_markers(self.lines[self.pos + 1]).1)
1777 } else {
1778 None
1779 };
1780
1781 let current_bq_depth = self.current_blockquote_depth();
1782 if let Some(alert_bq_depth) = self.active_alert_blockquote_depth()
1783 && current_bq_depth < alert_bq_depth
1784 {
1785 while matches!(self.containers.last(), Some(Container::Alert { .. })) {
1786 self.close_containers_to(self.containers.depth() - 1);
1787 }
1788 }
1789
1790 let dispatcher_ctx = BlockContext {
1791 content,
1792 has_blank_before: false, has_blank_before_strict: false, at_document_start: false, in_fenced_div: self.in_fenced_div(),
1796 blockquote_depth: current_bq_depth,
1797 config: self.config,
1798 content_indent,
1799 indent_to_emit,
1800 list_indent_info,
1801 in_list: lists::in_list(&self.containers),
1802 next_line,
1803 };
1804
1805 let mut dispatcher_ctx = dispatcher_ctx;
1808
1809 let dispatcher_match =
1812 self.block_registry
1813 .detect_prepared(&dispatcher_ctx, &self.lines, self.pos);
1814
1815 let after_metadata_block = std::mem::replace(&mut self.after_metadata_block, false);
1821 let has_blank_before = if self.pos == 0 || after_metadata_block {
1822 true
1823 } else {
1824 let prev_line = self.lines[self.pos - 1];
1825 let (prev_bq_depth, prev_inner) = count_blockquote_markers(prev_line);
1826 let (prev_inner_no_nl, _) = strip_newline(prev_inner);
1827 let prev_is_fenced_div_open = self.config.extensions.fenced_divs
1828 && fenced_divs::try_parse_div_fence_open(
1829 strip_n_blockquote_markers(prev_inner_no_nl, prev_bq_depth).trim_start(),
1830 )
1831 .is_some();
1832
1833 prev_line.trim().is_empty()
1834 || prev_is_fenced_div_open
1835 || matches!(self.containers.last(), Some(Container::BlockQuote { .. }))
1836 };
1837
1838 let at_document_start = self.pos == 0 && current_bq_depth == 0;
1841
1842 let prev_line_blank = if self.pos > 0 {
1843 let prev_line = self.lines[self.pos - 1];
1844 let (prev_bq_depth, prev_inner) = count_blockquote_markers(prev_line);
1845 prev_line.trim().is_empty() || (prev_bq_depth > 0 && prev_inner.trim().is_empty())
1846 } else {
1847 false
1848 };
1849 let has_blank_before_strict = at_document_start || prev_line_blank;
1850
1851 dispatcher_ctx.has_blank_before = has_blank_before;
1852 dispatcher_ctx.has_blank_before_strict = has_blank_before_strict;
1853 dispatcher_ctx.at_document_start = at_document_start;
1854
1855 let dispatcher_match =
1856 if dispatcher_ctx.has_blank_before || dispatcher_ctx.at_document_start {
1857 self.block_registry
1859 .detect_prepared(&dispatcher_ctx, &self.lines, self.pos)
1860 } else {
1861 dispatcher_match
1862 };
1863
1864 if has_blank_before {
1865 if let Some(env_name) = extract_environment_name(content)
1866 && is_inline_math_environment(&env_name)
1867 {
1868 if !self.is_paragraph_open() {
1869 paragraphs::start_paragraph_if_needed(&mut self.containers, &mut self.builder);
1870 }
1871 paragraphs::append_paragraph_line(
1872 &mut self.containers,
1873 &mut self.builder,
1874 line_to_append.unwrap_or(self.lines[self.pos]),
1875 self.config,
1876 );
1877 self.pos += 1;
1878 return true;
1879 }
1880
1881 if let Some(block_match) = dispatcher_match.as_ref() {
1882 let detection = block_match.detection;
1883
1884 match detection {
1885 BlockDetectionResult::YesCanInterrupt => {
1886 self.emit_list_item_buffer_if_needed();
1887 if self.is_paragraph_open() {
1888 self.close_containers_to(self.containers.depth() - 1);
1889 }
1890 }
1891 BlockDetectionResult::Yes => {
1892 self.prepare_for_block_element();
1893 }
1894 BlockDetectionResult::No => unreachable!(),
1895 }
1896
1897 if matches!(block_match.effect, BlockEffect::CloseFencedDiv) {
1898 self.close_containers_to_fenced_div();
1899 }
1900
1901 let lines_consumed = self.block_registry.parse_prepared(
1902 block_match,
1903 &dispatcher_ctx,
1904 &mut self.builder,
1905 &self.lines,
1906 self.pos,
1907 );
1908
1909 if matches!(
1910 self.block_registry.parser_name(block_match),
1911 "yaml_metadata" | "pandoc_title_block" | "mmd_title_block"
1912 ) {
1913 self.after_metadata_block = true;
1914 }
1915
1916 match block_match.effect {
1917 BlockEffect::None => {}
1918 BlockEffect::OpenFencedDiv => {
1919 self.containers.push(Container::FencedDiv {});
1920 }
1921 BlockEffect::CloseFencedDiv => {
1922 self.close_fenced_div();
1923 }
1924 BlockEffect::OpenFootnoteDefinition => {
1925 self.handle_footnote_open_effect(block_match, content);
1926 }
1927 BlockEffect::OpenList => {
1928 self.handle_list_open_effect(block_match, content, indent_to_emit);
1929 }
1930 BlockEffect::OpenDefinitionList => {
1931 self.handle_definition_list_effect(block_match, content, indent_to_emit);
1932 }
1933 BlockEffect::OpenBlockQuote => {
1934 }
1936 }
1937
1938 if lines_consumed == 0 {
1939 log::warn!(
1940 "block parser made no progress at line {} (parser={})",
1941 self.pos + 1,
1942 self.block_registry.parser_name(block_match)
1943 );
1944 return false;
1945 }
1946
1947 self.pos += lines_consumed;
1948 return true;
1949 }
1950 } else if let Some(block_match) = dispatcher_match.as_ref() {
1951 let parser_name = self.block_registry.parser_name(block_match);
1954 match block_match.detection {
1955 BlockDetectionResult::YesCanInterrupt => {
1956 if matches!(block_match.effect, BlockEffect::OpenFencedDiv)
1957 && self.is_paragraph_open()
1958 {
1959 if !self.is_paragraph_open() {
1961 paragraphs::start_paragraph_if_needed(
1962 &mut self.containers,
1963 &mut self.builder,
1964 );
1965 }
1966 paragraphs::append_paragraph_line(
1967 &mut self.containers,
1968 &mut self.builder,
1969 line_to_append.unwrap_or(self.lines[self.pos]),
1970 self.config,
1971 );
1972 self.pos += 1;
1973 return true;
1974 }
1975
1976 if matches!(block_match.effect, BlockEffect::OpenList)
1977 && self.is_paragraph_open()
1978 && !lists::in_list(&self.containers)
1979 && self.content_container_indent_to_strip() == 0
1980 {
1981 paragraphs::append_paragraph_line(
1983 &mut self.containers,
1984 &mut self.builder,
1985 line_to_append.unwrap_or(self.lines[self.pos]),
1986 self.config,
1987 );
1988 self.pos += 1;
1989 return true;
1990 }
1991
1992 self.emit_list_item_buffer_if_needed();
1993 if self.is_paragraph_open() {
1994 self.close_containers_to(self.containers.depth() - 1);
1995 }
1996 }
1997 BlockDetectionResult::Yes => {
1998 if parser_name == "fenced_div_open" && self.is_paragraph_open() {
2001 if !self.is_paragraph_open() {
2002 paragraphs::start_paragraph_if_needed(
2003 &mut self.containers,
2004 &mut self.builder,
2005 );
2006 }
2007 paragraphs::append_paragraph_line(
2008 &mut self.containers,
2009 &mut self.builder,
2010 line_to_append.unwrap_or(self.lines[self.pos]),
2011 self.config,
2012 );
2013 self.pos += 1;
2014 return true;
2015 }
2016 }
2017 BlockDetectionResult::No => unreachable!(),
2018 }
2019
2020 if !matches!(block_match.detection, BlockDetectionResult::No) {
2021 if matches!(block_match.effect, BlockEffect::CloseFencedDiv) {
2022 self.close_containers_to_fenced_div();
2023 }
2024
2025 let lines_consumed = self.block_registry.parse_prepared(
2026 block_match,
2027 &dispatcher_ctx,
2028 &mut self.builder,
2029 &self.lines,
2030 self.pos,
2031 );
2032
2033 match block_match.effect {
2034 BlockEffect::None => {}
2035 BlockEffect::OpenFencedDiv => {
2036 self.containers.push(Container::FencedDiv {});
2037 }
2038 BlockEffect::CloseFencedDiv => {
2039 self.close_fenced_div();
2040 }
2041 BlockEffect::OpenFootnoteDefinition => {
2042 self.handle_footnote_open_effect(block_match, content);
2043 }
2044 BlockEffect::OpenList => {
2045 self.handle_list_open_effect(block_match, content, indent_to_emit);
2046 }
2047 BlockEffect::OpenDefinitionList => {
2048 self.handle_definition_list_effect(block_match, content, indent_to_emit);
2049 }
2050 BlockEffect::OpenBlockQuote => {
2051 }
2053 }
2054
2055 if lines_consumed == 0 {
2056 log::warn!(
2057 "block parser made no progress at line {} (parser={})",
2058 self.pos + 1,
2059 self.block_registry.parser_name(block_match)
2060 );
2061 return false;
2062 }
2063
2064 self.pos += lines_consumed;
2065 return true;
2066 }
2067 }
2068
2069 if self.config.extensions.line_blocks
2071 && (has_blank_before || self.pos == 0)
2072 && try_parse_line_block_start(content).is_some()
2073 && try_parse_line_block_start(self.lines[self.pos]).is_some()
2077 {
2078 log::debug!("Parsed line block at line {}", self.pos);
2079 self.close_paragraph_if_open();
2081
2082 let new_pos = parse_line_block(&self.lines, self.pos, &mut self.builder, self.config);
2083 if new_pos > self.pos {
2084 self.pos = new_pos;
2085 return true;
2086 }
2087 }
2088
2089 if matches!(self.containers.last(), Some(Container::ListItem { .. })) {
2092 log::debug!(
2093 "Inside ListItem - buffering content: {:?}",
2094 line_to_append.unwrap_or(self.lines[self.pos]).trim_end()
2095 );
2096 let line = line_to_append.unwrap_or(self.lines[self.pos]);
2098
2099 if let Some(Container::ListItem { buffer, .. }) = self.containers.stack.last_mut() {
2101 buffer.push_text(line);
2102 }
2103
2104 self.pos += 1;
2105 return true;
2106 }
2107
2108 log::debug!(
2109 "Not in ListItem - creating paragraph for: {:?}",
2110 line_to_append.unwrap_or(self.lines[self.pos]).trim_end()
2111 );
2112 paragraphs::start_paragraph_if_needed(&mut self.containers, &mut self.builder);
2114 let line = line_to_append.unwrap_or(self.lines[self.pos]);
2117 paragraphs::append_paragraph_line(
2118 &mut self.containers,
2119 &mut self.builder,
2120 line,
2121 self.config,
2122 );
2123 self.pos += 1;
2124 true
2125 }
2126
2127 fn fenced_div_container_index(&self) -> Option<usize> {
2128 self.containers
2129 .stack
2130 .iter()
2131 .rposition(|c| matches!(c, Container::FencedDiv { .. }))
2132 }
2133
2134 fn close_containers_to_fenced_div(&mut self) {
2135 if let Some(index) = self.fenced_div_container_index() {
2136 self.close_containers_to(index + 1);
2137 }
2138 }
2139
2140 fn close_fenced_div(&mut self) {
2141 if let Some(index) = self.fenced_div_container_index() {
2142 self.close_containers_to(index);
2143 }
2144 }
2145
2146 fn in_fenced_div(&self) -> bool {
2147 self.containers
2148 .stack
2149 .iter()
2150 .any(|c| matches!(c, Container::FencedDiv { .. }))
2151 }
2152}