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 close_open_footnote_definition(&mut self) {
347 while matches!(
348 self.containers.last(),
349 Some(Container::FootnoteDefinition { .. })
350 ) {
351 self.close_containers_to(self.containers.depth() - 1);
352 }
353 }
354
355 fn handle_footnote_open_effect(
356 &mut self,
357 block_match: &super::block_dispatcher::PreparedBlockMatch,
358 content: &str,
359 ) {
360 let content_start = block_match
361 .payload
362 .as_ref()
363 .and_then(|p| p.downcast_ref::<super::block_dispatcher::FootnoteDefinitionPrepared>())
364 .map(|p| p.content_start)
365 .unwrap_or(0);
366
367 let content_col = 4;
368 self.containers
369 .push(Container::FootnoteDefinition { content_col });
370
371 if content_start > 0 {
372 let first_line_content = &content[content_start..];
373 if !first_line_content.trim().is_empty() {
374 paragraphs::start_paragraph_if_needed(&mut self.containers, &mut self.builder);
375 paragraphs::append_paragraph_line(
376 &mut self.containers,
377 &mut self.builder,
378 first_line_content,
379 self.config,
380 );
381 } else {
382 let (_, newline_str) = strip_newline(content);
383 if !newline_str.is_empty() {
384 self.builder.token(SyntaxKind::NEWLINE.into(), newline_str);
385 }
386 }
387 }
388 }
389
390 fn handle_list_open_effect(
391 &mut self,
392 block_match: &super::block_dispatcher::PreparedBlockMatch,
393 content: &str,
394 indent_to_emit: Option<&str>,
395 ) {
396 use super::block_dispatcher::ListPrepared;
397
398 let prepared = block_match
399 .payload
400 .as_ref()
401 .and_then(|p| p.downcast_ref::<ListPrepared>());
402 let Some(prepared) = prepared else {
403 return;
404 };
405
406 if prepared.indent_cols >= 4 && !lists::in_list(&self.containers) {
407 paragraphs::start_paragraph_if_needed(&mut self.containers, &mut self.builder);
408 paragraphs::append_paragraph_line(
409 &mut self.containers,
410 &mut self.builder,
411 content,
412 self.config,
413 );
414 return;
415 }
416
417 if self.is_paragraph_open() {
418 if !block_match.detection.eq(&BlockDetectionResult::Yes) {
419 paragraphs::append_paragraph_line(
420 &mut self.containers,
421 &mut self.builder,
422 content,
423 self.config,
424 );
425 return;
426 }
427 self.close_containers_to(self.containers.depth() - 1);
428 }
429
430 if matches!(
431 self.containers.last(),
432 Some(Container::Definition {
433 plain_open: true,
434 ..
435 })
436 ) {
437 self.emit_buffered_plain_if_needed();
438 }
439
440 let matched_level = lists::find_matching_list_level(
441 &self.containers,
442 &prepared.marker,
443 prepared.indent_cols,
444 );
445 let list_item = ListItemEmissionInput {
446 content,
447 marker_len: prepared.marker_len,
448 spaces_after_cols: prepared.spaces_after_cols,
449 spaces_after_bytes: prepared.spaces_after,
450 indent_cols: prepared.indent_cols,
451 indent_bytes: prepared.indent_bytes,
452 };
453 let current_content_col = paragraphs::current_content_col(&self.containers);
454 let deep_ordered_matched_level = matched_level
455 .and_then(|level| self.containers.stack.get(level).map(|c| (level, c)))
456 .and_then(|(level, container)| match container {
457 Container::List {
458 marker: list_marker,
459 base_indent_cols,
460 ..
461 } if matches!(
462 (&prepared.marker, list_marker),
463 (ListMarker::Ordered(_), ListMarker::Ordered(_))
464 ) && prepared.indent_cols >= 4
465 && *base_indent_cols >= 4
466 && prepared.indent_cols.abs_diff(*base_indent_cols) <= 3 =>
467 {
468 Some(level)
469 }
470 _ => None,
471 });
472
473 if deep_ordered_matched_level.is_none()
474 && current_content_col > 0
475 && prepared.indent_cols >= current_content_col
476 {
477 if let Some(level) = matched_level
478 && let Some(Container::List {
479 base_indent_cols, ..
480 }) = self.containers.stack.get(level)
481 && prepared.indent_cols == *base_indent_cols
482 {
483 let num_parent_lists = self.containers.stack[..level]
484 .iter()
485 .filter(|c| matches!(c, Container::List { .. }))
486 .count();
487
488 if num_parent_lists > 0 {
489 self.close_containers_to(level + 1);
490
491 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
492 self.close_containers_to(self.containers.depth() - 1);
493 }
494 if matches!(self.containers.last(), Some(Container::ListItem { .. })) {
495 self.close_containers_to(self.containers.depth() - 1);
496 }
497
498 if let Some(indent_str) = indent_to_emit {
499 self.builder
500 .token(SyntaxKind::WHITESPACE.into(), indent_str);
501 }
502
503 if let Some(nested_marker) = prepared.nested_marker {
504 lists::add_list_item_with_nested_empty_list(
505 &mut self.containers,
506 &mut self.builder,
507 &list_item,
508 nested_marker,
509 );
510 } else {
511 lists::add_list_item(&mut self.containers, &mut self.builder, &list_item);
512 }
513 return;
514 }
515 }
516
517 self.emit_list_item_buffer_if_needed();
518
519 start_nested_list(
520 &mut self.containers,
521 &mut self.builder,
522 &prepared.marker,
523 &list_item,
524 indent_to_emit,
525 );
526 return;
527 }
528
529 if let Some(level) = matched_level {
530 self.close_containers_to(level + 1);
531
532 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
533 self.close_containers_to(self.containers.depth() - 1);
534 }
535 if matches!(self.containers.last(), Some(Container::ListItem { .. })) {
536 self.close_containers_to(self.containers.depth() - 1);
537 }
538
539 if let Some(indent_str) = indent_to_emit {
540 self.builder
541 .token(SyntaxKind::WHITESPACE.into(), indent_str);
542 }
543
544 if let Some(nested_marker) = prepared.nested_marker {
545 lists::add_list_item_with_nested_empty_list(
546 &mut self.containers,
547 &mut self.builder,
548 &list_item,
549 nested_marker,
550 );
551 } else {
552 lists::add_list_item(&mut self.containers, &mut self.builder, &list_item);
553 }
554 return;
555 }
556
557 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
558 self.close_containers_to(self.containers.depth() - 1);
559 }
560 while matches!(self.containers.last(), Some(Container::ListItem { .. })) {
561 self.close_containers_to(self.containers.depth() - 1);
562 }
563 while matches!(self.containers.last(), Some(Container::List { .. })) {
564 self.close_containers_to(self.containers.depth() - 1);
565 }
566
567 self.builder.start_node(SyntaxKind::LIST.into());
568 if let Some(indent_str) = indent_to_emit {
569 self.builder
570 .token(SyntaxKind::WHITESPACE.into(), indent_str);
571 }
572 self.containers.push(Container::List {
573 marker: prepared.marker.clone(),
574 base_indent_cols: prepared.indent_cols,
575 has_blank_between_items: false,
576 });
577
578 if let Some(nested_marker) = prepared.nested_marker {
579 lists::add_list_item_with_nested_empty_list(
580 &mut self.containers,
581 &mut self.builder,
582 &list_item,
583 nested_marker,
584 );
585 } else {
586 lists::add_list_item(&mut self.containers, &mut self.builder, &list_item);
587 }
588 }
589
590 fn handle_definition_list_effect(
591 &mut self,
592 block_match: &super::block_dispatcher::PreparedBlockMatch,
593 content: &str,
594 indent_to_emit: Option<&str>,
595 ) {
596 use super::block_dispatcher::DefinitionPrepared;
597
598 let prepared = block_match
599 .payload
600 .as_ref()
601 .and_then(|p| p.downcast_ref::<DefinitionPrepared>());
602 let Some(prepared) = prepared else {
603 return;
604 };
605
606 match prepared {
607 DefinitionPrepared::Definition {
608 marker_char,
609 indent,
610 spaces_after,
611 spaces_after_cols,
612 has_content,
613 } => {
614 self.emit_buffered_plain_if_needed();
615
616 while matches!(self.containers.last(), Some(Container::ListItem { .. })) {
617 self.close_containers_to(self.containers.depth() - 1);
618 }
619 while matches!(self.containers.last(), Some(Container::List { .. })) {
620 self.close_containers_to(self.containers.depth() - 1);
621 }
622
623 if matches!(self.containers.last(), Some(Container::Definition { .. })) {
624 self.close_containers_to(self.containers.depth() - 1);
625 }
626
627 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
628 self.close_containers_to(self.containers.depth() - 1);
629 }
630
631 if definition_lists::in_definition_list(&self.containers)
635 && !matches!(
636 self.containers.last(),
637 Some(Container::DefinitionItem { .. })
638 )
639 {
640 self.builder.start_node(SyntaxKind::DEFINITION_ITEM.into());
641 self.containers.push(Container::DefinitionItem {});
642 }
643
644 if !definition_lists::in_definition_list(&self.containers) {
645 self.builder.start_node(SyntaxKind::DEFINITION_LIST.into());
646 self.containers.push(Container::DefinitionList {});
647 }
648
649 if !matches!(
650 self.containers.last(),
651 Some(Container::DefinitionItem { .. })
652 ) {
653 self.builder.start_node(SyntaxKind::DEFINITION_ITEM.into());
654 self.containers.push(Container::DefinitionItem {});
655 }
656
657 self.builder.start_node(SyntaxKind::DEFINITION.into());
658
659 if let Some(indent_str) = indent_to_emit {
660 self.builder
661 .token(SyntaxKind::WHITESPACE.into(), indent_str);
662 }
663
664 emit_definition_marker(&mut self.builder, *marker_char, *indent);
665 let indent_bytes = byte_index_at_column(content, *indent);
666 if *spaces_after > 0 {
667 let space_start = indent_bytes + 1;
668 let space_end = space_start + *spaces_after;
669 if space_end <= content.len() {
670 self.builder.token(
671 SyntaxKind::WHITESPACE.into(),
672 &content[space_start..space_end],
673 );
674 }
675 }
676
677 if !*has_content {
678 let current_line = self.lines[self.pos];
679 let (_, newline_str) = strip_newline(current_line);
680 if !newline_str.is_empty() {
681 self.builder.token(SyntaxKind::NEWLINE.into(), newline_str);
682 }
683 }
684
685 let content_col = *indent + 1 + *spaces_after_cols;
686 let content_start_bytes = indent_bytes + 1 + *spaces_after;
687 let after_marker_and_spaces = content.get(content_start_bytes..).unwrap_or("");
688 let mut plain_buffer = TextBuffer::new();
689 let mut definition_pushed = false;
690
691 if *has_content {
692 let current_line = self.lines[self.pos];
693 let (trimmed_line, _) = strip_newline(current_line);
694
695 let content_start = content_start_bytes.min(trimmed_line.len());
696 let content_slice = &trimmed_line[content_start..];
697 let content_line = ¤t_line[content_start_bytes.min(current_line.len())..];
698
699 let (blockquote_depth, inner_blockquote_content) =
700 count_blockquote_markers(content_line);
701
702 let should_start_list_from_first_line = self
703 .lines
704 .get(self.pos + 1)
705 .map(|next_line| {
706 let (next_without_newline, _) = strip_newline(next_line);
707 if next_without_newline.trim().is_empty() {
708 return false;
709 }
710
711 let (next_indent_cols, _) = leading_indent(next_without_newline);
712 next_indent_cols >= content_col
713 })
714 .unwrap_or(false);
715
716 if blockquote_depth > 0 {
717 self.containers.push(Container::Definition {
718 content_col,
719 plain_open: false,
720 plain_buffer: TextBuffer::new(),
721 });
722 definition_pushed = true;
723
724 let marker_info = parse_blockquote_marker_info(content_line);
725 for level in 0..blockquote_depth {
726 self.builder.start_node(SyntaxKind::BLOCK_QUOTE.into());
727 if let Some(info) = marker_info.get(level) {
728 blockquotes::emit_one_blockquote_marker(
729 &mut self.builder,
730 info.leading_spaces,
731 info.has_trailing_space,
732 );
733 }
734 self.containers.push(Container::BlockQuote {});
735 }
736
737 if !inner_blockquote_content.trim().is_empty() {
738 paragraphs::start_paragraph_if_needed(
739 &mut self.containers,
740 &mut self.builder,
741 );
742 paragraphs::append_paragraph_line(
743 &mut self.containers,
744 &mut self.builder,
745 inner_blockquote_content,
746 self.config,
747 );
748 }
749 } else if let Some(marker_match) =
750 try_parse_list_marker(content_slice, self.config)
751 && should_start_list_from_first_line
752 {
753 self.containers.push(Container::Definition {
754 content_col,
755 plain_open: false,
756 plain_buffer: TextBuffer::new(),
757 });
758 definition_pushed = true;
759
760 let (indent_cols, indent_bytes) = leading_indent(content_line);
761 self.builder.start_node(SyntaxKind::LIST.into());
762 self.containers.push(Container::List {
763 marker: marker_match.marker.clone(),
764 base_indent_cols: indent_cols,
765 has_blank_between_items: false,
766 });
767
768 let list_item = ListItemEmissionInput {
769 content: content_line,
770 marker_len: marker_match.marker_len,
771 spaces_after_cols: marker_match.spaces_after_cols,
772 spaces_after_bytes: marker_match.spaces_after_bytes,
773 indent_cols,
774 indent_bytes,
775 };
776
777 if let Some(nested_marker) = is_content_nested_bullet_marker(
778 content_line,
779 marker_match.marker_len,
780 marker_match.spaces_after_bytes,
781 ) {
782 lists::add_list_item_with_nested_empty_list(
783 &mut self.containers,
784 &mut self.builder,
785 &list_item,
786 nested_marker,
787 );
788 } else {
789 lists::add_list_item(
790 &mut self.containers,
791 &mut self.builder,
792 &list_item,
793 );
794 }
795 } else if let Some(fence) = code_blocks::try_parse_fence_open(content_slice) {
796 self.containers.push(Container::Definition {
797 content_col,
798 plain_open: false,
799 plain_buffer: TextBuffer::new(),
800 });
801 definition_pushed = true;
802
803 let bq_depth = self.current_blockquote_depth();
804 if let Some(indent_str) = indent_to_emit {
805 self.builder
806 .token(SyntaxKind::WHITESPACE.into(), indent_str);
807 }
808 let fence_line = current_line[content_start..].to_string();
809 let new_pos = if self.config.extensions.tex_math_gfm
810 && code_blocks::is_gfm_math_fence(&fence)
811 {
812 code_blocks::parse_fenced_math_block(
813 &mut self.builder,
814 &self.lines,
815 self.pos,
816 fence,
817 bq_depth,
818 content_col,
819 Some(&fence_line),
820 )
821 } else {
822 code_blocks::parse_fenced_code_block(
823 &mut self.builder,
824 &self.lines,
825 self.pos,
826 fence,
827 bq_depth,
828 content_col,
829 Some(&fence_line),
830 )
831 };
832 self.pos = new_pos - 1;
833 } else {
834 let (_, newline_str) = strip_newline(current_line);
835 let (content_without_newline, _) = strip_newline(after_marker_and_spaces);
836 if content_without_newline.is_empty() {
837 plain_buffer.push_line(newline_str);
838 } else {
839 let line_with_newline = if !newline_str.is_empty() {
840 format!("{}{}", content_without_newline, newline_str)
841 } else {
842 content_without_newline.to_string()
843 };
844 plain_buffer.push_line(line_with_newline);
845 }
846 }
847 }
848
849 if !definition_pushed {
850 self.containers.push(Container::Definition {
851 content_col,
852 plain_open: *has_content,
853 plain_buffer,
854 });
855 }
856 }
857 DefinitionPrepared::Term { blank_count } => {
858 self.emit_buffered_plain_if_needed();
859
860 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
861 self.close_containers_to(self.containers.depth() - 1);
862 }
863
864 if !definition_lists::in_definition_list(&self.containers) {
865 self.builder.start_node(SyntaxKind::DEFINITION_LIST.into());
866 self.containers.push(Container::DefinitionList {});
867 }
868
869 while matches!(
870 self.containers.last(),
871 Some(Container::Definition { .. }) | Some(Container::DefinitionItem { .. })
872 ) {
873 self.close_containers_to(self.containers.depth() - 1);
874 }
875
876 self.builder.start_node(SyntaxKind::DEFINITION_ITEM.into());
877 self.containers.push(Container::DefinitionItem {});
878
879 emit_term(&mut self.builder, content, self.config);
880
881 for i in 0..*blank_count {
882 let blank_pos = self.pos + 1 + i;
883 if blank_pos < self.lines.len() {
884 let blank_line = self.lines[blank_pos];
885 self.builder.start_node(SyntaxKind::BLANK_LINE.into());
886 self.builder
887 .token(SyntaxKind::BLANK_LINE.into(), blank_line);
888 self.builder.finish_node();
889 }
890 }
891 self.pos += *blank_count;
892 }
893 }
894 }
895
896 fn blockquote_marker_info(
898 &self,
899 payload: Option<&BlockQuotePrepared>,
900 line: &str,
901 ) -> Vec<marker_utils::BlockQuoteMarkerInfo> {
902 payload
903 .map(|payload| payload.marker_info.clone())
904 .unwrap_or_else(|| parse_blockquote_marker_info(line))
905 }
906
907 fn marker_info_for_line(
913 &self,
914 payload: Option<&BlockQuotePrepared>,
915 raw_line: &str,
916 marker_line: &str,
917 shifted_prefix: &str,
918 used_shifted: bool,
919 ) -> Vec<marker_utils::BlockQuoteMarkerInfo> {
920 let mut marker_info = if used_shifted {
921 parse_blockquote_marker_info(marker_line)
922 } else {
923 self.blockquote_marker_info(payload, raw_line)
924 };
925 if used_shifted && !shifted_prefix.is_empty() {
926 let (prefix_cols, _) = leading_indent(shifted_prefix);
927 if let Some(first) = marker_info.first_mut() {
928 first.leading_spaces += prefix_cols;
929 }
930 }
931 marker_info
932 }
933
934 fn shifted_blockquote_from_list<'b>(
937 &self,
938 line: &'b str,
939 ) -> Option<(usize, &'b str, &'b str, &'b str)> {
940 if !lists::in_list(&self.containers) {
941 return None;
942 }
943 let list_content_col = paragraphs::current_content_col(&self.containers);
944 let content_container_indent = self.content_container_indent_to_strip();
945 let marker_col = list_content_col.saturating_add(content_container_indent);
946 if marker_col == 0 {
947 return None;
948 }
949
950 let (indent_cols, _) = leading_indent(line);
951 if indent_cols < marker_col {
952 return None;
953 }
954
955 let idx = byte_index_at_column(line, marker_col);
956 if idx > line.len() {
957 return None;
958 }
959
960 let candidate = &line[idx..];
961 let (candidate_depth, candidate_inner) = count_blockquote_markers(candidate);
962 if candidate_depth == 0 {
963 return None;
964 }
965
966 Some((candidate_depth, candidate_inner, candidate, &line[..idx]))
967 }
968
969 fn emit_blockquote_markers(
970 &mut self,
971 marker_info: &[marker_utils::BlockQuoteMarkerInfo],
972 depth: usize,
973 ) {
974 for i in 0..depth {
975 if let Some(info) = marker_info.get(i) {
976 blockquotes::emit_one_blockquote_marker(
977 &mut self.builder,
978 info.leading_spaces,
979 info.has_trailing_space,
980 );
981 }
982 }
983 }
984
985 fn current_blockquote_depth(&self) -> usize {
986 blockquotes::current_blockquote_depth(&self.containers)
987 }
988
989 fn emit_or_buffer_blockquote_marker(
994 &mut self,
995 leading_spaces: usize,
996 has_trailing_space: bool,
997 ) {
998 if let Some(Container::ListItem { buffer, .. }) = self.containers.stack.last_mut() {
999 buffer.push_blockquote_marker(leading_spaces, has_trailing_space);
1000 return;
1001 }
1002
1003 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1005 paragraphs::append_paragraph_marker(
1007 &mut self.containers,
1008 leading_spaces,
1009 has_trailing_space,
1010 );
1011 } else {
1012 blockquotes::emit_one_blockquote_marker(
1014 &mut self.builder,
1015 leading_spaces,
1016 has_trailing_space,
1017 );
1018 }
1019 }
1020
1021 fn parse_document_stack(&mut self) {
1022 self.builder.start_node(SyntaxKind::DOCUMENT.into());
1023
1024 log::trace!("Starting document parse");
1025
1026 while self.pos < self.lines.len() {
1029 let line = self.lines[self.pos];
1030
1031 log::trace!("Parsing line {}: {}", self.pos + 1, line);
1032
1033 if self.parse_line(line) {
1034 continue;
1035 }
1036 self.pos += 1;
1037 }
1038
1039 self.close_containers_to(0);
1040 self.builder.finish_node(); }
1042
1043 fn parse_line(&mut self, line: &str) -> bool {
1045 let (mut bq_depth, mut inner_content) = count_blockquote_markers(line);
1048 let mut bq_marker_line = line;
1049 let mut shifted_bq_prefix = "";
1050 let mut used_shifted_bq = false;
1051 if bq_depth == 0
1052 && let Some((candidate_depth, candidate_inner, candidate_line, candidate_prefix)) =
1053 self.shifted_blockquote_from_list(line)
1054 {
1055 bq_depth = candidate_depth;
1056 inner_content = candidate_inner;
1057 bq_marker_line = candidate_line;
1058 shifted_bq_prefix = candidate_prefix;
1059 used_shifted_bq = true;
1060 }
1061 let current_bq_depth = self.current_blockquote_depth();
1062
1063 let has_blank_before = self.pos == 0 || self.lines[self.pos - 1].trim().is_empty();
1064 let mut blockquote_match: Option<PreparedBlockMatch> = None;
1065 let dispatcher_ctx = if current_bq_depth == 0 {
1066 Some(BlockContext {
1067 content: line,
1068 has_blank_before,
1069 has_blank_before_strict: has_blank_before,
1070 at_document_start: self.pos == 0,
1071 in_fenced_div: self.in_fenced_div(),
1072 blockquote_depth: current_bq_depth,
1073 config: self.config,
1074 content_indent: 0,
1075 indent_to_emit: None,
1076 list_indent_info: None,
1077 in_list: lists::in_list(&self.containers),
1078 next_line: if self.pos + 1 < self.lines.len() {
1079 Some(self.lines[self.pos + 1])
1080 } else {
1081 None
1082 },
1083 })
1084 } else {
1085 None
1086 };
1087
1088 let blockquote_payload = if let Some(dispatcher_ctx) = dispatcher_ctx.as_ref() {
1089 self.block_registry
1090 .detect_prepared(dispatcher_ctx, &self.lines, self.pos)
1091 .and_then(|prepared| {
1092 if matches!(prepared.effect, BlockEffect::OpenBlockQuote) {
1093 blockquote_match = Some(prepared);
1094 blockquote_match.as_ref().and_then(|prepared| {
1095 prepared
1096 .payload
1097 .as_ref()
1098 .and_then(|payload| payload.downcast_ref::<BlockQuotePrepared>())
1099 .cloned()
1100 })
1101 } else {
1102 None
1103 }
1104 })
1105 } else {
1106 None
1107 };
1108
1109 log::trace!(
1110 "parse_line [{}]: bq_depth={}, current_bq={}, depth={}, line={:?}",
1111 self.pos,
1112 bq_depth,
1113 current_bq_depth,
1114 self.containers.depth(),
1115 line.trim_end()
1116 );
1117
1118 let is_blank = line.trim_end_matches('\n').trim().is_empty()
1121 || (bq_depth > 0 && inner_content.trim_end_matches('\n').trim().is_empty());
1122
1123 if is_blank {
1124 if self.is_paragraph_open()
1125 && paragraphs::has_open_inline_math_environment(&self.containers)
1126 {
1127 paragraphs::append_paragraph_line(
1128 &mut self.containers,
1129 &mut self.builder,
1130 line,
1131 self.config,
1132 );
1133 self.pos += 1;
1134 return true;
1135 }
1136
1137 self.close_paragraph_if_open();
1139
1140 self.emit_buffered_plain_if_needed();
1144
1145 if bq_depth > current_bq_depth {
1153 for _ in current_bq_depth..bq_depth {
1155 self.builder.start_node(SyntaxKind::BLOCK_QUOTE.into());
1156 self.containers.push(Container::BlockQuote {});
1157 }
1158 } else if bq_depth < current_bq_depth {
1159 self.close_blockquotes_to_depth(bq_depth);
1161 }
1162
1163 let mut peek = self.pos + 1;
1165 while peek < self.lines.len() && self.lines[peek].trim().is_empty() {
1166 peek += 1;
1167 }
1168
1169 let levels_to_keep = if peek < self.lines.len() {
1171 ContinuationPolicy::new(self.config, &self.block_registry).compute_levels_to_keep(
1172 self.current_blockquote_depth(),
1173 &self.containers,
1174 &self.lines,
1175 peek,
1176 self.lines[peek],
1177 )
1178 } else {
1179 0
1180 };
1181 log::trace!(
1182 "Blank line: depth={}, levels_to_keep={}, next='{}'",
1183 self.containers.depth(),
1184 levels_to_keep,
1185 if peek < self.lines.len() {
1186 self.lines[peek]
1187 } else {
1188 "<EOF>"
1189 }
1190 );
1191
1192 while self.containers.depth() > levels_to_keep {
1196 match self.containers.last() {
1197 Some(Container::ListItem { .. }) => {
1198 log::trace!(
1200 "Closing ListItem at blank line (levels_to_keep={} < depth={})",
1201 levels_to_keep,
1202 self.containers.depth()
1203 );
1204 self.close_containers_to(self.containers.depth() - 1);
1205 }
1206 Some(Container::List { .. })
1207 | Some(Container::FootnoteDefinition { .. })
1208 | Some(Container::Alert { .. })
1209 | Some(Container::Paragraph { .. })
1210 | Some(Container::Definition { .. })
1211 | Some(Container::DefinitionItem { .. })
1212 | Some(Container::DefinitionList { .. }) => {
1213 log::trace!(
1214 "Closing {:?} at blank line (depth {} > levels_to_keep {})",
1215 self.containers.last(),
1216 self.containers.depth(),
1217 levels_to_keep
1218 );
1219
1220 self.close_containers_to(self.containers.depth() - 1);
1221 }
1222 _ => break,
1223 }
1224 }
1225
1226 if matches!(self.containers.last(), Some(Container::ListItem { .. })) {
1230 self.emit_list_item_buffer_if_needed();
1231 }
1232
1233 if bq_depth > 0 {
1235 let marker_info = self.marker_info_for_line(
1236 blockquote_payload.as_ref(),
1237 line,
1238 bq_marker_line,
1239 shifted_bq_prefix,
1240 used_shifted_bq,
1241 );
1242 self.emit_blockquote_markers(&marker_info, bq_depth);
1243 }
1244
1245 self.builder.start_node(SyntaxKind::BLANK_LINE.into());
1246 self.builder
1247 .token(SyntaxKind::BLANK_LINE.into(), inner_content);
1248 self.builder.finish_node();
1249
1250 self.pos += 1;
1251 return true;
1252 }
1253
1254 if bq_depth > current_bq_depth {
1256 if self.config.extensions.blank_before_blockquote
1259 && current_bq_depth == 0
1260 && !used_shifted_bq
1261 && !blockquote_payload
1262 .as_ref()
1263 .map(|payload| payload.can_start)
1264 .unwrap_or_else(|| blockquotes::can_start_blockquote(self.pos, &self.lines))
1265 {
1266 self.emit_list_item_buffer_if_needed();
1270 paragraphs::start_paragraph_if_needed(&mut self.containers, &mut self.builder);
1271 paragraphs::append_paragraph_line(
1272 &mut self.containers,
1273 &mut self.builder,
1274 line,
1275 self.config,
1276 );
1277 self.pos += 1;
1278 return true;
1279 }
1280
1281 let can_nest = if current_bq_depth > 0 {
1284 if self.config.extensions.blank_before_blockquote {
1285 matches!(self.containers.last(), Some(Container::BlockQuote { .. }))
1287 || (self.pos > 0 && {
1288 let prev_line = self.lines[self.pos - 1];
1289 let (prev_bq_depth, prev_inner) = count_blockquote_markers(prev_line);
1290 prev_bq_depth >= current_bq_depth && prev_inner.trim().is_empty()
1291 })
1292 } else {
1293 true
1294 }
1295 } else {
1296 blockquote_payload
1297 .as_ref()
1298 .map(|payload| payload.can_nest)
1299 .unwrap_or(true)
1300 };
1301
1302 if !can_nest {
1303 let content_at_current_depth =
1306 blockquotes::strip_n_blockquote_markers(line, current_bq_depth);
1307
1308 let marker_info = self.marker_info_for_line(
1310 blockquote_payload.as_ref(),
1311 line,
1312 bq_marker_line,
1313 shifted_bq_prefix,
1314 used_shifted_bq,
1315 );
1316 for i in 0..current_bq_depth {
1317 if let Some(info) = marker_info.get(i) {
1318 self.emit_or_buffer_blockquote_marker(
1319 info.leading_spaces,
1320 info.has_trailing_space,
1321 );
1322 }
1323 }
1324
1325 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1326 paragraphs::append_paragraph_line(
1328 &mut self.containers,
1329 &mut self.builder,
1330 content_at_current_depth,
1331 self.config,
1332 );
1333 self.pos += 1;
1334 return true;
1335 } else {
1336 paragraphs::start_paragraph_if_needed(&mut self.containers, &mut self.builder);
1338 paragraphs::append_paragraph_line(
1339 &mut self.containers,
1340 &mut self.builder,
1341 content_at_current_depth,
1342 self.config,
1343 );
1344 self.pos += 1;
1345 return true;
1346 }
1347 }
1348
1349 self.emit_list_item_buffer_if_needed();
1352
1353 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1355 self.close_containers_to(self.containers.depth() - 1);
1356 }
1357
1358 let marker_info = self.marker_info_for_line(
1360 blockquote_payload.as_ref(),
1361 line,
1362 bq_marker_line,
1363 shifted_bq_prefix,
1364 used_shifted_bq,
1365 );
1366
1367 if let (Some(dispatcher_ctx), Some(prepared)) =
1368 (dispatcher_ctx.as_ref(), blockquote_match.as_ref())
1369 {
1370 let _ = self.block_registry.parse_prepared(
1371 prepared,
1372 dispatcher_ctx,
1373 &mut self.builder,
1374 &self.lines,
1375 self.pos,
1376 );
1377 for _ in 0..bq_depth {
1378 self.containers.push(Container::BlockQuote {});
1379 }
1380 } else {
1381 for level in 0..current_bq_depth {
1383 if let Some(info) = marker_info.get(level) {
1384 self.emit_or_buffer_blockquote_marker(
1385 info.leading_spaces,
1386 info.has_trailing_space,
1387 );
1388 }
1389 }
1390
1391 for level in current_bq_depth..bq_depth {
1393 self.builder.start_node(SyntaxKind::BLOCK_QUOTE.into());
1394
1395 if let Some(info) = marker_info.get(level) {
1397 blockquotes::emit_one_blockquote_marker(
1398 &mut self.builder,
1399 info.leading_spaces,
1400 info.has_trailing_space,
1401 );
1402 }
1403
1404 self.containers.push(Container::BlockQuote {});
1405 }
1406 }
1407
1408 return self.parse_inner_content(inner_content, Some(inner_content));
1411 } else if bq_depth < current_bq_depth {
1412 if bq_depth == 0 {
1415 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1417 paragraphs::append_paragraph_line(
1418 &mut self.containers,
1419 &mut self.builder,
1420 line,
1421 self.config,
1422 );
1423 self.pos += 1;
1424 return true;
1425 }
1426
1427 if lists::in_blockquote_list(&self.containers)
1430 && let Some(marker_match) = try_parse_list_marker(line, self.config)
1431 {
1432 let (indent_cols, indent_bytes) = leading_indent(line);
1433 if let Some(level) = lists::find_matching_list_level(
1434 &self.containers,
1435 &marker_match.marker,
1436 indent_cols,
1437 ) {
1438 self.close_containers_to(level + 1);
1441
1442 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1444 self.close_containers_to(self.containers.depth() - 1);
1445 }
1446 if matches!(self.containers.last(), Some(Container::ListItem { .. })) {
1447 self.close_containers_to(self.containers.depth() - 1);
1448 }
1449
1450 if let Some(nested_marker) = is_content_nested_bullet_marker(
1452 line,
1453 marker_match.marker_len,
1454 marker_match.spaces_after_bytes,
1455 ) {
1456 let list_item = ListItemEmissionInput {
1457 content: line,
1458 marker_len: marker_match.marker_len,
1459 spaces_after_cols: marker_match.spaces_after_cols,
1460 spaces_after_bytes: marker_match.spaces_after_bytes,
1461 indent_cols,
1462 indent_bytes,
1463 };
1464 lists::add_list_item_with_nested_empty_list(
1465 &mut self.containers,
1466 &mut self.builder,
1467 &list_item,
1468 nested_marker,
1469 );
1470 } else {
1471 let list_item = ListItemEmissionInput {
1472 content: line,
1473 marker_len: marker_match.marker_len,
1474 spaces_after_cols: marker_match.spaces_after_cols,
1475 spaces_after_bytes: marker_match.spaces_after_bytes,
1476 indent_cols,
1477 indent_bytes,
1478 };
1479 lists::add_list_item(
1480 &mut self.containers,
1481 &mut self.builder,
1482 &list_item,
1483 );
1484 }
1485 self.pos += 1;
1486 return true;
1487 }
1488 }
1489 }
1490
1491 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1493 self.close_containers_to(self.containers.depth() - 1);
1494 }
1495
1496 self.close_blockquotes_to_depth(bq_depth);
1498
1499 if bq_depth > 0 {
1501 let marker_info = self.marker_info_for_line(
1503 blockquote_payload.as_ref(),
1504 line,
1505 bq_marker_line,
1506 shifted_bq_prefix,
1507 used_shifted_bq,
1508 );
1509 for i in 0..bq_depth {
1510 if let Some(info) = marker_info.get(i) {
1511 self.emit_or_buffer_blockquote_marker(
1512 info.leading_spaces,
1513 info.has_trailing_space,
1514 );
1515 }
1516 }
1517 return self.parse_inner_content(inner_content, Some(inner_content));
1519 } else {
1520 return self.parse_inner_content(line, None);
1522 }
1523 } else if bq_depth > 0 {
1524 let mut list_item_continuation = false;
1526 let same_depth_marker_info = self.marker_info_for_line(
1527 blockquote_payload.as_ref(),
1528 line,
1529 bq_marker_line,
1530 shifted_bq_prefix,
1531 used_shifted_bq,
1532 );
1533 let has_explicit_same_depth_marker = same_depth_marker_info.len() >= bq_depth;
1534
1535 if matches!(
1538 self.containers.last(),
1539 Some(Container::ListItem { content_col: _, .. })
1540 ) {
1541 let (indent_cols, _) = leading_indent(inner_content);
1542 let content_indent = self.content_container_indent_to_strip();
1543 let effective_indent = indent_cols.saturating_sub(content_indent);
1544 let content_col = match self.containers.last() {
1545 Some(Container::ListItem { content_col, .. }) => *content_col,
1546 _ => 0,
1547 };
1548
1549 let is_new_item_at_outer_level =
1551 if try_parse_list_marker(inner_content, self.config).is_some() {
1552 effective_indent < content_col
1553 } else {
1554 false
1555 };
1556
1557 if is_new_item_at_outer_level
1561 || (effective_indent < content_col && !has_explicit_same_depth_marker)
1562 {
1563 log::trace!(
1564 "Closing ListItem: is_new_item={}, effective_indent={} < content_col={}",
1565 is_new_item_at_outer_level,
1566 effective_indent,
1567 content_col
1568 );
1569 self.close_containers_to(self.containers.depth() - 1);
1570 } else {
1571 log::trace!(
1572 "Keeping ListItem: effective_indent={} >= content_col={}",
1573 effective_indent,
1574 content_col
1575 );
1576 list_item_continuation = true;
1577 }
1578 }
1579
1580 if list_item_continuation && code_blocks::try_parse_fence_open(inner_content).is_some()
1584 {
1585 list_item_continuation = false;
1586 }
1587
1588 let continuation_has_explicit_marker = list_item_continuation && {
1589 if has_explicit_same_depth_marker {
1590 for i in 0..bq_depth {
1591 if let Some(info) = same_depth_marker_info.get(i) {
1592 self.emit_or_buffer_blockquote_marker(
1593 info.leading_spaces,
1594 info.has_trailing_space,
1595 );
1596 }
1597 }
1598 true
1599 } else {
1600 false
1601 }
1602 };
1603
1604 if !list_item_continuation {
1605 let marker_info = self.marker_info_for_line(
1606 blockquote_payload.as_ref(),
1607 line,
1608 bq_marker_line,
1609 shifted_bq_prefix,
1610 used_shifted_bq,
1611 );
1612 for i in 0..bq_depth {
1613 if let Some(info) = marker_info.get(i) {
1614 self.emit_or_buffer_blockquote_marker(
1615 info.leading_spaces,
1616 info.has_trailing_space,
1617 );
1618 }
1619 }
1620 }
1621 let line_to_append = if list_item_continuation {
1622 if continuation_has_explicit_marker {
1623 Some(inner_content)
1624 } else {
1625 Some(line)
1626 }
1627 } else {
1628 Some(inner_content)
1629 };
1630 return self.parse_inner_content(inner_content, line_to_append);
1631 }
1632
1633 if current_bq_depth > 0 {
1636 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1638 paragraphs::append_paragraph_line(
1639 &mut self.containers,
1640 &mut self.builder,
1641 line,
1642 self.config,
1643 );
1644 self.pos += 1;
1645 return true;
1646 }
1647
1648 if lists::in_blockquote_list(&self.containers)
1650 && let Some(marker_match) = try_parse_list_marker(line, self.config)
1651 {
1652 let (indent_cols, indent_bytes) = leading_indent(line);
1653 if let Some(level) = lists::find_matching_list_level(
1654 &self.containers,
1655 &marker_match.marker,
1656 indent_cols,
1657 ) {
1658 self.close_containers_to(level + 1);
1660
1661 if matches!(self.containers.last(), Some(Container::Paragraph { .. })) {
1663 self.close_containers_to(self.containers.depth() - 1);
1664 }
1665 if matches!(self.containers.last(), Some(Container::ListItem { .. })) {
1666 self.close_containers_to(self.containers.depth() - 1);
1667 }
1668
1669 if let Some(nested_marker) = is_content_nested_bullet_marker(
1671 line,
1672 marker_match.marker_len,
1673 marker_match.spaces_after_bytes,
1674 ) {
1675 let list_item = ListItemEmissionInput {
1676 content: line,
1677 marker_len: marker_match.marker_len,
1678 spaces_after_cols: marker_match.spaces_after_cols,
1679 spaces_after_bytes: marker_match.spaces_after_bytes,
1680 indent_cols,
1681 indent_bytes,
1682 };
1683 lists::add_list_item_with_nested_empty_list(
1684 &mut self.containers,
1685 &mut self.builder,
1686 &list_item,
1687 nested_marker,
1688 );
1689 } else {
1690 let list_item = ListItemEmissionInput {
1691 content: line,
1692 marker_len: marker_match.marker_len,
1693 spaces_after_cols: marker_match.spaces_after_cols,
1694 spaces_after_bytes: marker_match.spaces_after_bytes,
1695 indent_cols,
1696 indent_bytes,
1697 };
1698 lists::add_list_item(&mut self.containers, &mut self.builder, &list_item);
1699 }
1700 self.pos += 1;
1701 return true;
1702 }
1703 }
1704 }
1705
1706 self.parse_inner_content(line, None)
1708 }
1709
1710 fn content_container_indent_to_strip(&self) -> usize {
1712 self.containers
1713 .stack
1714 .iter()
1715 .filter_map(|c| match c {
1716 Container::FootnoteDefinition { content_col, .. } => Some(*content_col),
1717 Container::Definition { content_col, .. } => Some(*content_col),
1718 _ => None,
1719 })
1720 .sum()
1721 }
1722
1723 fn parse_inner_content(&mut self, content: &str, line_to_append: Option<&str>) -> bool {
1729 log::trace!(
1730 "parse_inner_content [{}]: depth={}, last={:?}, content={:?}",
1731 self.pos,
1732 self.containers.depth(),
1733 self.containers.last(),
1734 content.trim_end()
1735 );
1736 let content_indent = self.content_container_indent_to_strip();
1739 let (stripped_content, indent_to_emit) = if content_indent > 0 {
1740 let (indent_cols, _) = leading_indent(content);
1741 if indent_cols >= content_indent {
1742 let idx = byte_index_at_column(content, content_indent);
1743 (&content[idx..], Some(&content[..idx]))
1744 } else {
1745 let trimmed_start = content.trim_start();
1747 let ws_len = content.len() - trimmed_start.len();
1748 if ws_len > 0 {
1749 (trimmed_start, Some(&content[..ws_len]))
1750 } else {
1751 (content, None)
1752 }
1753 }
1754 } else {
1755 (content, None)
1756 };
1757
1758 if self.config.extensions.alerts
1759 && self.current_blockquote_depth() > 0
1760 && !self.in_active_alert()
1761 && !self.is_paragraph_open()
1762 && let Some(marker) = Self::alert_marker_from_content(stripped_content)
1763 {
1764 let (_, newline_str) = strip_newline(stripped_content);
1765 self.builder.start_node(SyntaxKind::ALERT.into());
1766 self.builder.token(SyntaxKind::ALERT_MARKER.into(), marker);
1767 if !newline_str.is_empty() {
1768 self.builder.token(SyntaxKind::NEWLINE.into(), newline_str);
1769 }
1770 self.containers.push(Container::Alert {
1771 blockquote_depth: self.current_blockquote_depth(),
1772 });
1773 self.pos += 1;
1774 return true;
1775 }
1776
1777 if matches!(self.containers.last(), Some(Container::Definition { .. })) {
1781 let is_definition_marker =
1782 definition_lists::try_parse_definition_marker(stripped_content).is_some()
1783 && !stripped_content.starts_with(':');
1784 if content_indent == 0 && is_definition_marker {
1785 } else {
1787 let policy = ContinuationPolicy::new(self.config, &self.block_registry);
1788
1789 if policy.definition_plain_can_continue(
1790 stripped_content,
1791 content,
1792 content_indent,
1793 &BlockContext {
1794 content: stripped_content,
1795 has_blank_before: self.pos == 0
1796 || self.lines[self.pos - 1].trim().is_empty(),
1797 has_blank_before_strict: self.pos == 0
1798 || self.lines[self.pos - 1].trim().is_empty(),
1799 at_document_start: self.pos == 0 && self.current_blockquote_depth() == 0,
1800 in_fenced_div: self.in_fenced_div(),
1801 blockquote_depth: self.current_blockquote_depth(),
1802 config: self.config,
1803 content_indent,
1804 indent_to_emit: None,
1805 list_indent_info: None,
1806 in_list: lists::in_list(&self.containers),
1807 next_line: if self.pos + 1 < self.lines.len() {
1808 Some(self.lines[self.pos + 1])
1809 } else {
1810 None
1811 },
1812 },
1813 &self.lines,
1814 self.pos,
1815 ) {
1816 let content_line = stripped_content;
1817 let (text_without_newline, newline_str) = strip_newline(content_line);
1818 let indent_prefix = if !text_without_newline.trim().is_empty() {
1819 indent_to_emit.unwrap_or("")
1820 } else {
1821 ""
1822 };
1823 let content_line = format!("{}{}", indent_prefix, text_without_newline);
1824
1825 if let Some(Container::Definition {
1826 plain_open,
1827 plain_buffer,
1828 ..
1829 }) = self.containers.stack.last_mut()
1830 {
1831 let line_with_newline = if !newline_str.is_empty() {
1832 format!("{}{}", content_line, newline_str)
1833 } else {
1834 content_line
1835 };
1836 plain_buffer.push_line(line_with_newline);
1837 *plain_open = true;
1838 }
1839
1840 self.pos += 1;
1841 return true;
1842 }
1843 }
1844 }
1845
1846 if content_indent > 0 {
1849 let (bq_depth, inner_content) = count_blockquote_markers(stripped_content);
1850 let current_bq_depth = self.current_blockquote_depth();
1851 let in_footnote_definition = self
1852 .containers
1853 .stack
1854 .iter()
1855 .any(|container| matches!(container, Container::FootnoteDefinition { .. }));
1856
1857 if bq_depth > 0 {
1858 if in_footnote_definition
1859 && self.config.extensions.blank_before_blockquote
1860 && current_bq_depth == 0
1861 && !blockquotes::can_start_blockquote(self.pos, &self.lines)
1862 {
1863 } else {
1867 self.emit_buffered_plain_if_needed();
1870 self.emit_list_item_buffer_if_needed();
1871
1872 self.close_paragraph_if_open();
1875
1876 if bq_depth > current_bq_depth {
1877 let marker_info = parse_blockquote_marker_info(stripped_content);
1878
1879 for level in current_bq_depth..bq_depth {
1881 self.builder.start_node(SyntaxKind::BLOCK_QUOTE.into());
1882
1883 if level == current_bq_depth
1884 && let Some(indent_str) = indent_to_emit
1885 {
1886 self.builder
1887 .token(SyntaxKind::WHITESPACE.into(), indent_str);
1888 }
1889
1890 if let Some(info) = marker_info.get(level) {
1891 blockquotes::emit_one_blockquote_marker(
1892 &mut self.builder,
1893 info.leading_spaces,
1894 info.has_trailing_space,
1895 );
1896 }
1897
1898 self.containers.push(Container::BlockQuote {});
1899 }
1900 } else if bq_depth < current_bq_depth {
1901 self.close_blockquotes_to_depth(bq_depth);
1902 } else {
1903 let marker_info = parse_blockquote_marker_info(stripped_content);
1905 self.emit_blockquote_markers(&marker_info, bq_depth);
1906 }
1907
1908 return self.parse_inner_content(inner_content, Some(inner_content));
1909 }
1910 }
1911 }
1912
1913 let content = stripped_content;
1915
1916 if self.is_paragraph_open()
1917 && (paragraphs::has_open_inline_math_environment(&self.containers)
1918 || paragraphs::has_open_display_math_dollars(&self.containers))
1919 {
1920 paragraphs::append_paragraph_line(
1921 &mut self.containers,
1922 &mut self.builder,
1923 line_to_append.unwrap_or(self.lines[self.pos]),
1924 self.config,
1925 );
1926 self.pos += 1;
1927 return true;
1928 }
1929
1930 use super::blocks::lists;
1934 use super::blocks::paragraphs;
1935 let list_indent_info = if lists::in_list(&self.containers) {
1936 let content_col = paragraphs::current_content_col(&self.containers);
1937 if content_col > 0 {
1938 Some(super::block_dispatcher::ListIndentInfo { content_col })
1939 } else {
1940 None
1941 }
1942 } else {
1943 None
1944 };
1945
1946 let next_line = if self.pos + 1 < self.lines.len() {
1947 Some(count_blockquote_markers(self.lines[self.pos + 1]).1)
1950 } else {
1951 None
1952 };
1953
1954 let current_bq_depth = self.current_blockquote_depth();
1955 if let Some(alert_bq_depth) = self.active_alert_blockquote_depth()
1956 && current_bq_depth < alert_bq_depth
1957 {
1958 while matches!(self.containers.last(), Some(Container::Alert { .. })) {
1959 self.close_containers_to(self.containers.depth() - 1);
1960 }
1961 }
1962
1963 let dispatcher_ctx = BlockContext {
1964 content,
1965 has_blank_before: false, has_blank_before_strict: false, at_document_start: false, in_fenced_div: self.in_fenced_div(),
1969 blockquote_depth: current_bq_depth,
1970 config: self.config,
1971 content_indent,
1972 indent_to_emit,
1973 list_indent_info,
1974 in_list: lists::in_list(&self.containers),
1975 next_line,
1976 };
1977
1978 let mut dispatcher_ctx = dispatcher_ctx;
1981
1982 let dispatcher_match =
1985 self.block_registry
1986 .detect_prepared(&dispatcher_ctx, &self.lines, self.pos);
1987
1988 let after_metadata_block = std::mem::replace(&mut self.after_metadata_block, false);
1994 let has_blank_before = if self.pos == 0 || after_metadata_block {
1995 true
1996 } else {
1997 let prev_line = self.lines[self.pos - 1];
1998 let (prev_bq_depth, prev_inner) = count_blockquote_markers(prev_line);
1999 let (prev_inner_no_nl, _) = strip_newline(prev_inner);
2000 let prev_is_fenced_div_open = self.config.extensions.fenced_divs
2001 && fenced_divs::try_parse_div_fence_open(
2002 strip_n_blockquote_markers(prev_inner_no_nl, prev_bq_depth).trim_start(),
2003 )
2004 .is_some();
2005
2006 let prev_line_blank = prev_line.trim().is_empty();
2007 prev_line_blank
2008 || prev_is_fenced_div_open
2009 || matches!(self.containers.last(), Some(Container::BlockQuote { .. }))
2010 || !self.previous_block_requires_blank_before_heading()
2011 };
2012
2013 let at_document_start = self.pos == 0 && current_bq_depth == 0;
2016
2017 let prev_line_blank = if self.pos > 0 {
2018 let prev_line = self.lines[self.pos - 1];
2019 let (prev_bq_depth, prev_inner) = count_blockquote_markers(prev_line);
2020 prev_line.trim().is_empty() || (prev_bq_depth > 0 && prev_inner.trim().is_empty())
2021 } else {
2022 false
2023 };
2024 let has_blank_before_strict = at_document_start || prev_line_blank;
2025
2026 dispatcher_ctx.has_blank_before = has_blank_before;
2027 dispatcher_ctx.has_blank_before_strict = has_blank_before_strict;
2028 dispatcher_ctx.at_document_start = at_document_start;
2029
2030 let dispatcher_match =
2031 if dispatcher_ctx.has_blank_before || dispatcher_ctx.at_document_start {
2032 self.block_registry
2034 .detect_prepared(&dispatcher_ctx, &self.lines, self.pos)
2035 } else {
2036 dispatcher_match
2037 };
2038
2039 if has_blank_before {
2040 if let Some(env_name) = extract_environment_name(content)
2041 && is_inline_math_environment(&env_name)
2042 {
2043 if !self.is_paragraph_open() {
2044 paragraphs::start_paragraph_if_needed(&mut self.containers, &mut self.builder);
2045 }
2046 paragraphs::append_paragraph_line(
2047 &mut self.containers,
2048 &mut self.builder,
2049 line_to_append.unwrap_or(self.lines[self.pos]),
2050 self.config,
2051 );
2052 self.pos += 1;
2053 return true;
2054 }
2055
2056 if let Some(block_match) = dispatcher_match.as_ref() {
2057 let detection = block_match.detection;
2058
2059 match detection {
2060 BlockDetectionResult::YesCanInterrupt => {
2061 self.emit_list_item_buffer_if_needed();
2062 if self.is_paragraph_open() {
2063 self.close_containers_to(self.containers.depth() - 1);
2064 }
2065 }
2066 BlockDetectionResult::Yes => {
2067 self.prepare_for_block_element();
2068 }
2069 BlockDetectionResult::No => unreachable!(),
2070 }
2071
2072 if matches!(block_match.effect, BlockEffect::CloseFencedDiv) {
2073 self.close_containers_to_fenced_div();
2074 }
2075
2076 if matches!(block_match.effect, BlockEffect::OpenFootnoteDefinition) {
2077 self.close_open_footnote_definition();
2078 }
2079
2080 let lines_consumed = self.block_registry.parse_prepared(
2081 block_match,
2082 &dispatcher_ctx,
2083 &mut self.builder,
2084 &self.lines,
2085 self.pos,
2086 );
2087
2088 if matches!(
2089 self.block_registry.parser_name(block_match),
2090 "yaml_metadata" | "pandoc_title_block" | "mmd_title_block"
2091 ) {
2092 self.after_metadata_block = true;
2093 }
2094
2095 match block_match.effect {
2096 BlockEffect::None => {}
2097 BlockEffect::OpenFencedDiv => {
2098 self.containers.push(Container::FencedDiv {});
2099 }
2100 BlockEffect::CloseFencedDiv => {
2101 self.close_fenced_div();
2102 }
2103 BlockEffect::OpenFootnoteDefinition => {
2104 self.handle_footnote_open_effect(block_match, content);
2105 }
2106 BlockEffect::OpenList => {
2107 self.handle_list_open_effect(block_match, content, indent_to_emit);
2108 }
2109 BlockEffect::OpenDefinitionList => {
2110 self.handle_definition_list_effect(block_match, content, indent_to_emit);
2111 }
2112 BlockEffect::OpenBlockQuote => {
2113 }
2115 }
2116
2117 if lines_consumed == 0 {
2118 log::warn!(
2119 "block parser made no progress at line {} (parser={})",
2120 self.pos + 1,
2121 self.block_registry.parser_name(block_match)
2122 );
2123 return false;
2124 }
2125
2126 self.pos += lines_consumed;
2127 return true;
2128 }
2129 } else if let Some(block_match) = dispatcher_match.as_ref() {
2130 let parser_name = self.block_registry.parser_name(block_match);
2133 match block_match.detection {
2134 BlockDetectionResult::YesCanInterrupt => {
2135 if matches!(block_match.effect, BlockEffect::OpenFencedDiv)
2136 && self.is_paragraph_open()
2137 {
2138 if !self.is_paragraph_open() {
2140 paragraphs::start_paragraph_if_needed(
2141 &mut self.containers,
2142 &mut self.builder,
2143 );
2144 }
2145 paragraphs::append_paragraph_line(
2146 &mut self.containers,
2147 &mut self.builder,
2148 line_to_append.unwrap_or(self.lines[self.pos]),
2149 self.config,
2150 );
2151 self.pos += 1;
2152 return true;
2153 }
2154
2155 if matches!(block_match.effect, BlockEffect::OpenList)
2156 && self.is_paragraph_open()
2157 && !lists::in_list(&self.containers)
2158 && self.content_container_indent_to_strip() == 0
2159 {
2160 paragraphs::append_paragraph_line(
2162 &mut self.containers,
2163 &mut self.builder,
2164 line_to_append.unwrap_or(self.lines[self.pos]),
2165 self.config,
2166 );
2167 self.pos += 1;
2168 return true;
2169 }
2170
2171 self.emit_list_item_buffer_if_needed();
2172 if self.is_paragraph_open() {
2173 self.close_containers_to(self.containers.depth() - 1);
2174 }
2175 }
2176 BlockDetectionResult::Yes => {
2177 if parser_name == "fenced_div_open" && self.is_paragraph_open() {
2180 if !self.is_paragraph_open() {
2181 paragraphs::start_paragraph_if_needed(
2182 &mut self.containers,
2183 &mut self.builder,
2184 );
2185 }
2186 paragraphs::append_paragraph_line(
2187 &mut self.containers,
2188 &mut self.builder,
2189 line_to_append.unwrap_or(self.lines[self.pos]),
2190 self.config,
2191 );
2192 self.pos += 1;
2193 return true;
2194 }
2195 }
2196 BlockDetectionResult::No => unreachable!(),
2197 }
2198
2199 if !matches!(block_match.detection, BlockDetectionResult::No) {
2200 if matches!(block_match.effect, BlockEffect::CloseFencedDiv) {
2201 self.close_containers_to_fenced_div();
2202 }
2203
2204 if matches!(block_match.effect, BlockEffect::OpenFootnoteDefinition) {
2205 self.close_open_footnote_definition();
2206 }
2207
2208 let lines_consumed = self.block_registry.parse_prepared(
2209 block_match,
2210 &dispatcher_ctx,
2211 &mut self.builder,
2212 &self.lines,
2213 self.pos,
2214 );
2215
2216 match block_match.effect {
2217 BlockEffect::None => {}
2218 BlockEffect::OpenFencedDiv => {
2219 self.containers.push(Container::FencedDiv {});
2220 }
2221 BlockEffect::CloseFencedDiv => {
2222 self.close_fenced_div();
2223 }
2224 BlockEffect::OpenFootnoteDefinition => {
2225 self.handle_footnote_open_effect(block_match, content);
2226 }
2227 BlockEffect::OpenList => {
2228 self.handle_list_open_effect(block_match, content, indent_to_emit);
2229 }
2230 BlockEffect::OpenDefinitionList => {
2231 self.handle_definition_list_effect(block_match, content, indent_to_emit);
2232 }
2233 BlockEffect::OpenBlockQuote => {
2234 }
2236 }
2237
2238 if lines_consumed == 0 {
2239 log::warn!(
2240 "block parser made no progress at line {} (parser={})",
2241 self.pos + 1,
2242 self.block_registry.parser_name(block_match)
2243 );
2244 return false;
2245 }
2246
2247 self.pos += lines_consumed;
2248 return true;
2249 }
2250 }
2251
2252 if self.config.extensions.line_blocks
2254 && (has_blank_before || self.pos == 0)
2255 && try_parse_line_block_start(content).is_some()
2256 && try_parse_line_block_start(self.lines[self.pos]).is_some()
2260 {
2261 log::trace!("Parsed line block at line {}", self.pos);
2262 self.close_paragraph_if_open();
2264
2265 let new_pos = parse_line_block(&self.lines, self.pos, &mut self.builder, self.config);
2266 if new_pos > self.pos {
2267 self.pos = new_pos;
2268 return true;
2269 }
2270 }
2271
2272 if matches!(self.containers.last(), Some(Container::ListItem { .. })) {
2275 log::trace!(
2276 "Inside ListItem - buffering content: {:?}",
2277 line_to_append.unwrap_or(self.lines[self.pos]).trim_end()
2278 );
2279 let line = line_to_append.unwrap_or(self.lines[self.pos]);
2281
2282 if let Some(Container::ListItem { buffer, .. }) = self.containers.stack.last_mut() {
2284 buffer.push_text(line);
2285 }
2286
2287 self.pos += 1;
2288 return true;
2289 }
2290
2291 log::trace!(
2292 "Not in ListItem - creating paragraph for: {:?}",
2293 line_to_append.unwrap_or(self.lines[self.pos]).trim_end()
2294 );
2295 paragraphs::start_paragraph_if_needed(&mut self.containers, &mut self.builder);
2297 let line = line_to_append.unwrap_or(self.lines[self.pos]);
2300 paragraphs::append_paragraph_line(
2301 &mut self.containers,
2302 &mut self.builder,
2303 line,
2304 self.config,
2305 );
2306 self.pos += 1;
2307 true
2308 }
2309
2310 fn fenced_div_container_index(&self) -> Option<usize> {
2311 self.containers
2312 .stack
2313 .iter()
2314 .rposition(|c| matches!(c, Container::FencedDiv { .. }))
2315 }
2316
2317 fn close_containers_to_fenced_div(&mut self) {
2318 if let Some(index) = self.fenced_div_container_index() {
2319 self.close_containers_to(index + 1);
2320 }
2321 }
2322
2323 fn close_fenced_div(&mut self) {
2324 if let Some(index) = self.fenced_div_container_index() {
2325 self.close_containers_to(index);
2326 }
2327 }
2328
2329 fn in_fenced_div(&self) -> bool {
2330 self.containers
2331 .stack
2332 .iter()
2333 .any(|c| matches!(c, Container::FencedDiv { .. }))
2334 }
2335}