diff --git a/crates/biome_markdown_parser/src/syntax/mod.rs b/crates/biome_markdown_parser/src/syntax/mod.rs index c47543489e44..d778917c0d86 100644 --- a/crates/biome_markdown_parser/src/syntax/mod.rs +++ b/crates/biome_markdown_parser/src/syntax/mod.rs @@ -858,6 +858,12 @@ fn is_quote_blank_line_after_newline(p: &mut MarkdownParser, quote_depth: usize) p.lookahead(|p| { p.bump(NEWLINE); + is_quote_blank_line_from_current(p, quote_depth) + }) +} + +fn is_quote_blank_line_from_current(p: &mut MarkdownParser, quote_depth: usize) -> bool { + p.lookahead(|p| { if is_quote_only_blank_line_from_source(p, quote_depth) { return true; } @@ -1009,8 +1015,22 @@ fn handle_inline_newline(p: &mut MarkdownParser, has_content: bool) -> InlineNew // Consume the NEWLINE as textual content (remap to MD_TEXTUAL_LITERAL) emit_inline_newline_as_text(p); - // If we're inside a block quote, only consume the quote prefix - // when it doesn't start a new block (e.g., a nested quote). + handle_line_continuation(p, has_content, true) +} + +/// Consume container continuation prefixes and check for block interrupts. +/// +/// Called after a line boundary has been crossed — either a NEWLINE (via +/// `handle_inline_newline`) or a hard line break (`MD_HARD_LINE_LITERAL`, +/// which embeds its own newline). Handles quote prefixes, list indent, +/// setext underlines, and other block-level constructs that can end or +/// interrupt a paragraph. +fn handle_line_continuation( + p: &mut MarkdownParser, + has_content: bool, + emit_indent_tokens: bool, +) -> InlineNewlineAction { + let quote_depth = p.state().block_quote_depth; if break_for_quote_prefix_after_inline_newline(p, quote_depth) { return InlineNewlineAction::Break; } @@ -1024,12 +1044,6 @@ fn handle_inline_newline(p: &mut MarkdownParser, has_content: bool) -> InlineNew return InlineNewlineAction::Break; } - // Inside a nested list item (depth >= 2), break the paragraph when the - // continuation line's indent drops to or below the marker column. - // Such lines belong to a parent item, not lazy continuation. - // We only check nesting depth, not marker_indent alone, because a - // top-level list with leading whitespace (e.g. ` 1. text`) still - // allows lazy continuation at indent 0 per CommonMark §5.2. if required_indent > 0 && p.state().list_nesting_depth >= 2 { let marker_indent = p.state().list_item_marker_indent; if marker_indent > 0 { @@ -1040,9 +1054,6 @@ fn handle_inline_newline(p: &mut MarkdownParser, has_content: bool) -> InlineNew } } - // Check for block-level constructs that can interrupt paragraphs. - // Textual fence tokens (e.g. "```") may not be caught by line_starts_with_fence - // because the lexer emits them as MD_TEXTUAL_LITERAL in inline context. if p.at(MD_TEXTUAL_LITERAL) { let text = p.cur_text(); if text.starts_with("```") || text.starts_with("~~~") { @@ -1053,7 +1064,9 @@ fn handle_inline_newline(p: &mut MarkdownParser, has_content: bool) -> InlineNew return InlineNewlineAction::Break; } - emit_inline_continuation_indent(p, required_indent); + if emit_indent_tokens { + emit_inline_continuation_indent(p, required_indent); + } InlineNewlineAction::Continue } @@ -1145,8 +1158,21 @@ pub(crate) fn parse_inline_item_list(p: &mut MarkdownParser) { after_hard_break = matches!(&parsed, Present(cm) if cm.kind(p) == MD_HARD_LINE); // Per CommonMark §6.7: after a hard line break, leading spaces on the - // next line are ignored. Consume as whitespace trivia (structural). + // next line are ignored. First, run container continuation handling so + // block quote/list prefixes remain part of the same paragraph. if after_hard_break { + let quote_depth = p.state().block_quote_depth; + if p.at(NEWLINE) || is_quote_blank_line_from_current(p, quote_depth) { + break; + } + + if matches!( + handle_line_continuation(p, has_content, false), + InlineNewlineAction::Break + ) { + break; + } + while p.at(MD_TEXTUAL_LITERAL) && p.cur_text().chars().all(|c| c == ' ' || c == '\t') { p.consume_as_whitespace_trivia(); } diff --git a/crates/biome_markdown_parser/src/to_html.rs b/crates/biome_markdown_parser/src/to_html.rs index 613118d0b3ca..2e8d049343de 100644 --- a/crates/biome_markdown_parser/src/to_html.rs +++ b/crates/biome_markdown_parser/src/to_html.rs @@ -2007,6 +2007,36 @@ mod tests { assert_eq!(html, "

foo\\

\n"); } + #[test] + fn test_hard_break_in_blockquote() { + let parsed = parse_markdown("> foo \n> bar \n>\n> baz"); + let html = document_to_html( + &parsed.tree(), + parsed.list_tightness(), + parsed.list_item_indents(), + parsed.quote_indents(), + ); + assert_eq!( + html, + "
\n

foo
\nbar

\n

baz

\n
\n" + ); + } + + #[test] + fn test_hard_break_nested_quote_in_list() { + let parsed = parse_markdown("- > quoted \n > line \n >\n > next para\n\n- after\n"); + let html = document_to_html( + &parsed.tree(), + parsed.list_tightness(), + parsed.list_item_indents(), + parsed.quote_indents(), + ); + assert_eq!( + html, + "\n" + ); + } + #[test] fn test_tight_list_marker_split() { // Two tight lists separated by blank line with different markers diff --git a/crates/biome_markdown_parser/tests/md_test_suite/ok/hard_break_in_blockquote.md b/crates/biome_markdown_parser/tests/md_test_suite/ok/hard_break_in_blockquote.md new file mode 100644 index 000000000000..ad78c37471ba --- /dev/null +++ b/crates/biome_markdown_parser/tests/md_test_suite/ok/hard_break_in_blockquote.md @@ -0,0 +1,4 @@ +> foo +> bar +> +> baz \ No newline at end of file diff --git a/crates/biome_markdown_parser/tests/md_test_suite/ok/hard_break_in_blockquote.md.snap b/crates/biome_markdown_parser/tests/md_test_suite/ok/hard_break_in_blockquote.md.snap new file mode 100644 index 000000000000..d43f890f1ef7 --- /dev/null +++ b/crates/biome_markdown_parser/tests/md_test_suite/ok/hard_break_in_blockquote.md.snap @@ -0,0 +1,123 @@ +--- +source: crates/biome_markdown_parser/tests/spec_test.rs +expression: snapshot +--- + +## Input + +``` +> foo +> bar +> +> baz +``` + + +## AST + +``` +MdDocument { + bom_token: missing (optional), + value: MdBlockList [ + MdQuote { + prefix: MdQuotePrefix { + pre_marker_indent: MdQuoteIndentList [], + marker_token: R_ANGLE@0..1 ">" [] [], + post_marker_space_token: MD_QUOTE_POST_MARKER_SPACE@1..2 " " [] [], + }, + content: MdBlockList [ + MdParagraph { + list: MdInlineItemList [ + MdTextual { + value_token: MD_TEXTUAL_LITERAL@2..5 "foo" [] [], + }, + MdHardLine { + value_token: MD_HARD_LINE_LITERAL@5..8 " \n" [] [], + }, + MdQuotePrefix { + pre_marker_indent: MdQuoteIndentList [], + marker_token: R_ANGLE@8..9 ">" [] [], + post_marker_space_token: MD_QUOTE_POST_MARKER_SPACE@9..10 " " [] [], + }, + MdTextual { + value_token: MD_TEXTUAL_LITERAL@10..13 "bar" [] [], + }, + MdHardLine { + value_token: MD_HARD_LINE_LITERAL@13..16 " \n" [] [], + }, + ], + hard_line: missing (optional), + }, + MdQuotePrefix { + pre_marker_indent: MdQuoteIndentList [], + marker_token: R_ANGLE@16..17 ">" [] [], + post_marker_space_token: missing (optional), + }, + MdNewline { + value_token: NEWLINE@17..18 "\n" [] [], + }, + MdQuotePrefix { + pre_marker_indent: MdQuoteIndentList [], + marker_token: R_ANGLE@18..19 ">" [] [], + post_marker_space_token: MD_QUOTE_POST_MARKER_SPACE@19..20 " " [] [], + }, + MdParagraph { + list: MdInlineItemList [ + MdTextual { + value_token: MD_TEXTUAL_LITERAL@20..23 "baz" [] [], + }, + ], + hard_line: missing (optional), + }, + ], + }, + ], + eof_token: EOF@23..23 "" [] [], +} +``` + +## CST + +``` +0: MD_DOCUMENT@0..23 + 0: (empty) + 1: MD_BLOCK_LIST@0..23 + 0: MD_QUOTE@0..23 + 0: MD_QUOTE_PREFIX@0..2 + 0: MD_QUOTE_INDENT_LIST@0..0 + 1: R_ANGLE@0..1 ">" [] [] + 2: MD_QUOTE_POST_MARKER_SPACE@1..2 " " [] [] + 1: MD_BLOCK_LIST@2..23 + 0: MD_PARAGRAPH@2..16 + 0: MD_INLINE_ITEM_LIST@2..16 + 0: MD_TEXTUAL@2..5 + 0: MD_TEXTUAL_LITERAL@2..5 "foo" [] [] + 1: MD_HARD_LINE@5..8 + 0: MD_HARD_LINE_LITERAL@5..8 " \n" [] [] + 2: MD_QUOTE_PREFIX@8..10 + 0: MD_QUOTE_INDENT_LIST@8..8 + 1: R_ANGLE@8..9 ">" [] [] + 2: MD_QUOTE_POST_MARKER_SPACE@9..10 " " [] [] + 3: MD_TEXTUAL@10..13 + 0: MD_TEXTUAL_LITERAL@10..13 "bar" [] [] + 4: MD_HARD_LINE@13..16 + 0: MD_HARD_LINE_LITERAL@13..16 " \n" [] [] + 1: (empty) + 1: MD_QUOTE_PREFIX@16..17 + 0: MD_QUOTE_INDENT_LIST@16..16 + 1: R_ANGLE@16..17 ">" [] [] + 2: (empty) + 2: MD_NEWLINE@17..18 + 0: NEWLINE@17..18 "\n" [] [] + 3: MD_QUOTE_PREFIX@18..20 + 0: MD_QUOTE_INDENT_LIST@18..18 + 1: R_ANGLE@18..19 ">" [] [] + 2: MD_QUOTE_POST_MARKER_SPACE@19..20 " " [] [] + 4: MD_PARAGRAPH@20..23 + 0: MD_INLINE_ITEM_LIST@20..23 + 0: MD_TEXTUAL@20..23 + 0: MD_TEXTUAL_LITERAL@20..23 "baz" [] [] + 1: (empty) + 2: EOF@23..23 "" [] [] + +``` diff --git a/crates/biome_markdown_parser/tests/md_test_suite/ok/hard_break_in_list_item.md b/crates/biome_markdown_parser/tests/md_test_suite/ok/hard_break_in_list_item.md new file mode 100644 index 000000000000..716e805085f0 --- /dev/null +++ b/crates/biome_markdown_parser/tests/md_test_suite/ok/hard_break_in_list_item.md @@ -0,0 +1,6 @@ +- foo + bar + + baz + +- simple \ No newline at end of file diff --git a/crates/biome_markdown_parser/tests/md_test_suite/ok/hard_break_in_list_item.md.snap b/crates/biome_markdown_parser/tests/md_test_suite/ok/hard_break_in_list_item.md.snap new file mode 100644 index 000000000000..9cc37c11b4c5 --- /dev/null +++ b/crates/biome_markdown_parser/tests/md_test_suite/ok/hard_break_in_list_item.md.snap @@ -0,0 +1,162 @@ +--- +source: crates/biome_markdown_parser/tests/spec_test.rs +expression: snapshot +--- + +## Input + +``` +- foo + bar + + baz + +- simple +``` + + +## AST + +``` +MdDocument { + bom_token: missing (optional), + value: MdBlockList [ + MdBulletListItem { + md_bullet_list: MdBulletList [ + MdBullet { + prefix: MdListMarkerPrefix { + pre_marker_indent: MdIndentTokenList [], + marker: MINUS@0..1 "-" [] [], + post_marker_space_token: missing (optional), + content_indent: MdIndentTokenList [], + }, + content: MdBlockList [ + MdParagraph { + list: MdInlineItemList [ + MdTextual { + value_token: MD_TEXTUAL_LITERAL@1..5 " foo" [] [], + }, + MdHardLine { + value_token: MD_HARD_LINE_LITERAL@5..8 " \n" [] [], + }, + MdTextual { + value_token: MD_TEXTUAL_LITERAL@8..13 "bar" [Whitespace(" "), Whitespace(" ")] [], + }, + MdHardLine { + value_token: MD_HARD_LINE_LITERAL@13..16 " \n" [] [], + }, + ], + hard_line: missing (optional), + }, + MdNewline { + value_token: NEWLINE@16..17 "\n" [] [], + }, + MdContinuationIndent { + indent: MdIndentTokenList [ + MdIndentToken { + md_indent_char_token: MD_INDENT_CHAR@17..18 " " [] [], + }, + MdIndentToken { + md_indent_char_token: MD_INDENT_CHAR@18..19 " " [] [], + }, + ], + }, + MdParagraph { + list: MdInlineItemList [ + MdTextual { + value_token: MD_TEXTUAL_LITERAL@19..22 "baz" [] [], + }, + MdTextual { + value_token: MD_TEXTUAL_LITERAL@22..23 "\n" [] [], + }, + ], + hard_line: missing (optional), + }, + MdNewline { + value_token: NEWLINE@23..24 "\n" [] [], + }, + ], + }, + MdBullet { + prefix: MdListMarkerPrefix { + pre_marker_indent: MdIndentTokenList [], + marker: MINUS@24..25 "-" [] [], + post_marker_space_token: missing (optional), + content_indent: MdIndentTokenList [], + }, + content: MdBlockList [ + MdParagraph { + list: MdInlineItemList [ + MdTextual { + value_token: MD_TEXTUAL_LITERAL@25..32 " simple" [] [], + }, + ], + hard_line: missing (optional), + }, + ], + }, + ], + }, + ], + eof_token: EOF@32..32 "" [] [], +} +``` + +## CST + +``` +0: MD_DOCUMENT@0..32 + 0: (empty) + 1: MD_BLOCK_LIST@0..32 + 0: MD_BULLET_LIST_ITEM@0..32 + 0: MD_BULLET_LIST@0..32 + 0: MD_BULLET@0..24 + 0: MD_LIST_MARKER_PREFIX@0..1 + 0: MD_INDENT_TOKEN_LIST@0..0 + 1: MINUS@0..1 "-" [] [] + 2: (empty) + 3: MD_INDENT_TOKEN_LIST@1..1 + 1: MD_BLOCK_LIST@1..24 + 0: MD_PARAGRAPH@1..16 + 0: MD_INLINE_ITEM_LIST@1..16 + 0: MD_TEXTUAL@1..5 + 0: MD_TEXTUAL_LITERAL@1..5 " foo" [] [] + 1: MD_HARD_LINE@5..8 + 0: MD_HARD_LINE_LITERAL@5..8 " \n" [] [] + 2: MD_TEXTUAL@8..13 + 0: MD_TEXTUAL_LITERAL@8..13 "bar" [Whitespace(" "), Whitespace(" ")] [] + 3: MD_HARD_LINE@13..16 + 0: MD_HARD_LINE_LITERAL@13..16 " \n" [] [] + 1: (empty) + 1: MD_NEWLINE@16..17 + 0: NEWLINE@16..17 "\n" [] [] + 2: MD_CONTINUATION_INDENT@17..19 + 0: MD_INDENT_TOKEN_LIST@17..19 + 0: MD_INDENT_TOKEN@17..18 + 0: MD_INDENT_CHAR@17..18 " " [] [] + 1: MD_INDENT_TOKEN@18..19 + 0: MD_INDENT_CHAR@18..19 " " [] [] + 3: MD_PARAGRAPH@19..23 + 0: MD_INLINE_ITEM_LIST@19..23 + 0: MD_TEXTUAL@19..22 + 0: MD_TEXTUAL_LITERAL@19..22 "baz" [] [] + 1: MD_TEXTUAL@22..23 + 0: MD_TEXTUAL_LITERAL@22..23 "\n" [] [] + 1: (empty) + 4: MD_NEWLINE@23..24 + 0: NEWLINE@23..24 "\n" [] [] + 1: MD_BULLET@24..32 + 0: MD_LIST_MARKER_PREFIX@24..25 + 0: MD_INDENT_TOKEN_LIST@24..24 + 1: MINUS@24..25 "-" [] [] + 2: (empty) + 3: MD_INDENT_TOKEN_LIST@25..25 + 1: MD_BLOCK_LIST@25..32 + 0: MD_PARAGRAPH@25..32 + 0: MD_INLINE_ITEM_LIST@25..32 + 0: MD_TEXTUAL@25..32 + 0: MD_TEXTUAL_LITERAL@25..32 " simple" [] [] + 1: (empty) + 2: EOF@32..32 "" [] [] + +``` diff --git a/crates/biome_markdown_parser/tests/md_test_suite/ok/hard_break_nested_quote_in_list.md b/crates/biome_markdown_parser/tests/md_test_suite/ok/hard_break_nested_quote_in_list.md new file mode 100644 index 000000000000..782f41770033 --- /dev/null +++ b/crates/biome_markdown_parser/tests/md_test_suite/ok/hard_break_nested_quote_in_list.md @@ -0,0 +1,6 @@ +- > quoted + > line + > + > next para + +- after diff --git a/crates/biome_markdown_parser/tests/md_test_suite/ok/hard_break_nested_quote_in_list.md.snap b/crates/biome_markdown_parser/tests/md_test_suite/ok/hard_break_nested_quote_in_list.md.snap new file mode 100644 index 000000000000..9800ce3572fd --- /dev/null +++ b/crates/biome_markdown_parser/tests/md_test_suite/ok/hard_break_nested_quote_in_list.md.snap @@ -0,0 +1,227 @@ +--- +source: crates/biome_markdown_parser/tests/spec_test.rs +expression: snapshot +--- + +## Input + +``` +- > quoted + > line + > + > next para + +- after + +``` + + +## AST + +``` +MdDocument { + bom_token: missing (optional), + value: MdBlockList [ + MdBulletListItem { + md_bullet_list: MdBulletList [ + MdBullet { + prefix: MdListMarkerPrefix { + pre_marker_indent: MdIndentTokenList [], + marker: MINUS@0..1 "-" [] [], + post_marker_space_token: MD_LIST_POST_MARKER_SPACE@1..2 " " [] [], + content_indent: MdIndentTokenList [], + }, + content: MdBlockList [ + MdQuote { + prefix: MdQuotePrefix { + pre_marker_indent: MdQuoteIndentList [], + marker_token: R_ANGLE@2..3 ">" [] [], + post_marker_space_token: missing (optional), + }, + content: MdBlockList [ + MdParagraph { + list: MdInlineItemList [ + MdTextual { + value_token: MD_TEXTUAL_LITERAL@3..10 " quoted" [] [], + }, + MdHardLine { + value_token: MD_HARD_LINE_LITERAL@10..13 " \n" [] [], + }, + MdQuotePrefix { + pre_marker_indent: MdQuoteIndentList [ + MdQuoteIndent { + md_quote_pre_marker_indent_token: MD_QUOTE_PRE_MARKER_INDENT@13..14 " " [] [], + }, + MdQuoteIndent { + md_quote_pre_marker_indent_token: MD_QUOTE_PRE_MARKER_INDENT@14..15 " " [] [], + }, + ], + marker_token: R_ANGLE@15..16 ">" [] [], + post_marker_space_token: MD_QUOTE_POST_MARKER_SPACE@16..17 " " [] [], + }, + MdTextual { + value_token: MD_TEXTUAL_LITERAL@17..21 "line" [] [], + }, + MdHardLine { + value_token: MD_HARD_LINE_LITERAL@21..24 " \n" [] [], + }, + ], + hard_line: missing (optional), + }, + MdQuotePrefix { + pre_marker_indent: MdQuoteIndentList [ + MdQuoteIndent { + md_quote_pre_marker_indent_token: MD_QUOTE_PRE_MARKER_INDENT@24..25 " " [] [], + }, + MdQuoteIndent { + md_quote_pre_marker_indent_token: MD_QUOTE_PRE_MARKER_INDENT@25..26 " " [] [], + }, + ], + marker_token: R_ANGLE@26..27 ">" [] [], + post_marker_space_token: missing (optional), + }, + MdNewline { + value_token: NEWLINE@27..28 "\n" [] [], + }, + MdQuotePrefix { + pre_marker_indent: MdQuoteIndentList [ + MdQuoteIndent { + md_quote_pre_marker_indent_token: MD_QUOTE_PRE_MARKER_INDENT@28..29 " " [] [], + }, + MdQuoteIndent { + md_quote_pre_marker_indent_token: MD_QUOTE_PRE_MARKER_INDENT@29..30 " " [] [], + }, + ], + marker_token: R_ANGLE@30..31 ">" [] [], + post_marker_space_token: MD_QUOTE_POST_MARKER_SPACE@31..32 " " [] [], + }, + MdParagraph { + list: MdInlineItemList [ + MdTextual { + value_token: MD_TEXTUAL_LITERAL@32..41 "next para" [] [], + }, + MdTextual { + value_token: MD_TEXTUAL_LITERAL@41..42 "\n" [] [], + }, + ], + hard_line: missing (optional), + }, + ], + }, + MdNewline { + value_token: NEWLINE@42..43 "\n" [] [], + }, + ], + }, + MdBullet { + prefix: MdListMarkerPrefix { + pre_marker_indent: MdIndentTokenList [], + marker: MINUS@43..44 "-" [] [], + post_marker_space_token: missing (optional), + content_indent: MdIndentTokenList [], + }, + content: MdBlockList [ + MdParagraph { + list: MdInlineItemList [ + MdTextual { + value_token: MD_TEXTUAL_LITERAL@44..50 " after" [] [], + }, + MdTextual { + value_token: MD_TEXTUAL_LITERAL@50..51 "\n" [] [], + }, + ], + hard_line: missing (optional), + }, + ], + }, + ], + }, + ], + eof_token: EOF@51..51 "" [] [], +} +``` + +## CST + +``` +0: MD_DOCUMENT@0..51 + 0: (empty) + 1: MD_BLOCK_LIST@0..51 + 0: MD_BULLET_LIST_ITEM@0..51 + 0: MD_BULLET_LIST@0..51 + 0: MD_BULLET@0..43 + 0: MD_LIST_MARKER_PREFIX@0..2 + 0: MD_INDENT_TOKEN_LIST@0..0 + 1: MINUS@0..1 "-" [] [] + 2: MD_LIST_POST_MARKER_SPACE@1..2 " " [] [] + 3: MD_INDENT_TOKEN_LIST@2..2 + 1: MD_BLOCK_LIST@2..43 + 0: MD_QUOTE@2..42 + 0: MD_QUOTE_PREFIX@2..3 + 0: MD_QUOTE_INDENT_LIST@2..2 + 1: R_ANGLE@2..3 ">" [] [] + 2: (empty) + 1: MD_BLOCK_LIST@3..42 + 0: MD_PARAGRAPH@3..24 + 0: MD_INLINE_ITEM_LIST@3..24 + 0: MD_TEXTUAL@3..10 + 0: MD_TEXTUAL_LITERAL@3..10 " quoted" [] [] + 1: MD_HARD_LINE@10..13 + 0: MD_HARD_LINE_LITERAL@10..13 " \n" [] [] + 2: MD_QUOTE_PREFIX@13..17 + 0: MD_QUOTE_INDENT_LIST@13..15 + 0: MD_QUOTE_INDENT@13..14 + 0: MD_QUOTE_PRE_MARKER_INDENT@13..14 " " [] [] + 1: MD_QUOTE_INDENT@14..15 + 0: MD_QUOTE_PRE_MARKER_INDENT@14..15 " " [] [] + 1: R_ANGLE@15..16 ">" [] [] + 2: MD_QUOTE_POST_MARKER_SPACE@16..17 " " [] [] + 3: MD_TEXTUAL@17..21 + 0: MD_TEXTUAL_LITERAL@17..21 "line" [] [] + 4: MD_HARD_LINE@21..24 + 0: MD_HARD_LINE_LITERAL@21..24 " \n" [] [] + 1: (empty) + 1: MD_QUOTE_PREFIX@24..27 + 0: MD_QUOTE_INDENT_LIST@24..26 + 0: MD_QUOTE_INDENT@24..25 + 0: MD_QUOTE_PRE_MARKER_INDENT@24..25 " " [] [] + 1: MD_QUOTE_INDENT@25..26 + 0: MD_QUOTE_PRE_MARKER_INDENT@25..26 " " [] [] + 1: R_ANGLE@26..27 ">" [] [] + 2: (empty) + 2: MD_NEWLINE@27..28 + 0: NEWLINE@27..28 "\n" [] [] + 3: MD_QUOTE_PREFIX@28..32 + 0: MD_QUOTE_INDENT_LIST@28..30 + 0: MD_QUOTE_INDENT@28..29 + 0: MD_QUOTE_PRE_MARKER_INDENT@28..29 " " [] [] + 1: MD_QUOTE_INDENT@29..30 + 0: MD_QUOTE_PRE_MARKER_INDENT@29..30 " " [] [] + 1: R_ANGLE@30..31 ">" [] [] + 2: MD_QUOTE_POST_MARKER_SPACE@31..32 " " [] [] + 4: MD_PARAGRAPH@32..42 + 0: MD_INLINE_ITEM_LIST@32..42 + 0: MD_TEXTUAL@32..41 + 0: MD_TEXTUAL_LITERAL@32..41 "next para" [] [] + 1: MD_TEXTUAL@41..42 + 0: MD_TEXTUAL_LITERAL@41..42 "\n" [] [] + 1: (empty) + 1: MD_NEWLINE@42..43 + 0: NEWLINE@42..43 "\n" [] [] + 1: MD_BULLET@43..51 + 0: MD_LIST_MARKER_PREFIX@43..44 + 0: MD_INDENT_TOKEN_LIST@43..43 + 1: MINUS@43..44 "-" [] [] + 2: (empty) + 3: MD_INDENT_TOKEN_LIST@44..44 + 1: MD_BLOCK_LIST@44..51 + 0: MD_PARAGRAPH@44..51 + 0: MD_INLINE_ITEM_LIST@44..51 + 0: MD_TEXTUAL@44..50 + 0: MD_TEXTUAL_LITERAL@44..50 " after" [] [] + 1: MD_TEXTUAL@50..51 + 0: MD_TEXTUAL_LITERAL@50..51 "\n" [] [] + 1: (empty) + 2: EOF@51..51 "" [] [] + +``` diff --git a/crates/biome_markdown_parser/tests/spec_test.rs b/crates/biome_markdown_parser/tests/spec_test.rs index 233a48fc2d4b..d4c34ce710bf 100644 --- a/crates/biome_markdown_parser/tests/spec_test.rs +++ b/crates/biome_markdown_parser/tests/spec_test.rs @@ -269,6 +269,11 @@ pub fn quick_test() { "> ```\n> hello\n> ```\n", "
\n
hello\n
\n
\n", ); + test_example( + 99922, + "> foo \n> bar\n", + "
\n

foo
\nbar

\n
\n", + ); // Quoted indented code must terminate before a quoted thematic break. test_example( 99921,