diff --git a/crates/biome_markdown_parser/src/to_html.rs b/crates/biome_markdown_parser/src/to_html.rs index 2e8d049343de..2c39544428c9 100644 --- a/crates/biome_markdown_parser/src/to_html.rs +++ b/crates/biome_markdown_parser/src/to_html.rs @@ -645,12 +645,12 @@ impl<'a> HtmlRenderer<'a> { let first_is_paragraph = blocks .iter() - .find(|b| !is_newline_block(b)) + .find(|b| !is_transparent_block(b)) .is_some_and(is_paragraph_block); let last_is_paragraph = blocks .iter() .rev() - .find(|b| !is_newline_block(b)) + .find(|b| !is_transparent_block(b)) .is_some_and(is_paragraph_block); let leading_newline = !is_tight || !first_is_paragraph; @@ -1773,9 +1773,22 @@ fn is_newline_block(block: &AnyMdBlock) -> bool { ) } +/// Check if a block is structural-only and produces no rendered content. +/// This includes newline blocks, continuation indents, and quote prefix markers +/// that can appear as children of list item content when lists are nested +/// inside blockquotes. +fn is_transparent_block(block: &AnyMdBlock) -> bool { + matches!( + block, + AnyMdBlock::AnyMdLeafBlock( + AnyMdLeafBlock::MdNewline(_) | AnyMdLeafBlock::MdContinuationIndent(_), + ) | AnyMdBlock::MdQuotePrefix(_) + ) +} + /// Check if blocks are effectively empty (empty or only newlines). fn is_empty_content(blocks: &[AnyMdBlock]) -> bool { - blocks.is_empty() || blocks.iter().all(is_newline_block) + blocks.is_empty() || blocks.iter().all(is_transparent_block) } fn list_item_required_indent(entry: &ListItemIndent) -> usize { @@ -2116,4 +2129,61 @@ mod tests { "\n
    \n
  1. ordered
  2. \n
\n" ); } + + #[test] + fn test_tight_bullet_list_in_blockquote() { + let parsed = parse_markdown("> - a\n> - b\n"); + let html = document_to_html( + &parsed.tree(), + parsed.list_tightness(), + parsed.list_item_indents(), + parsed.quote_indents(), + ); + assert_eq!( + html, + "
\n\n
\n" + ); + } + + #[test] + fn test_tight_ordered_list_in_blockquote() { + let parsed = parse_markdown("> 1. a\n> 2. b\n"); + let html = document_to_html( + &parsed.tree(), + parsed.list_tightness(), + parsed.list_item_indents(), + parsed.quote_indents(), + ); + assert_eq!( + html, + "
\n
    \n
  1. a
  2. \n
  3. b
  4. \n
\n
\n" + ); + } + + #[test] + fn test_tight_three_item_bullet_list_in_blockquote() { + let parsed = parse_markdown("> - a\n> - b\n> - c\n"); + let html = document_to_html( + &parsed.tree(), + parsed.list_tightness(), + parsed.list_item_indents(), + parsed.quote_indents(), + ); + assert_eq!( + html, + "
\n\n
\n" + ); + } + + #[test] + fn test_tight_list_not_in_blockquote() { + let parsed = parse_markdown("- a\n- b\n"); + let html = document_to_html( + &parsed.tree(), + parsed.list_tightness(), + parsed.list_item_indents(), + parsed.quote_indents(), + ); + assert_eq!(html, "\n"); + } }