Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/Renderer/HtmlRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -768,7 +768,9 @@ protected function renderListItem(ListItem $node, bool $tight = true): string
// This handles both:
// 1. Single paragraph: <p>text</p> -> text
// 2. Paragraph followed by nested list: <p>text</p>\n<ul>... -> text\n<ul>...
$content = preg_replace('/^<p>(.+?)<\/p>(\n)?/s', '$1$2', $content) ?? $content;
// A data-source-line-only wrapper is stripped too (the source-line
// option must never change structure; the <li> keeps the anchor).
$content = preg_replace('/^<p(?: data-source-line="\d+")?>(.+?)<\/p>(\n)?/s', '$1$2', $content) ?? $content;
}

// Handle task list items
Expand Down
23 changes: 18 additions & 5 deletions tests/TestCase/SourceLineTrackingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,9 @@ public function testListSyntheticSeparatorDoesNotBecomeSourceLine(): void
$converter = new DjotConverter(sourceLines: true);
$html = $converter->convert("- first\n {.note}\n second\n");

$this->assertStringContainsString('<p data-source-line="1">first</p>', $html);
// The tight item's lead stays unwrapped (anchor-only <p> wrappers are
// stripped like plain ones); the li carries the anchor.
$this->assertMatchesRegularExpression('/<li data-source-line="1">\nfirst\n/', $html);
$this->assertStringContainsString('<p class="note" data-source-line="3">second</p>', $html);
$this->assertStringNotContainsString('data-source-line="0"', $html);
}
Expand All @@ -99,11 +101,9 @@ public function testListInsideBlockquoteComposesLineMap(): void
$html = $converter->convert("> - quoted item\n> - nested item\n");

$this->assertMatchesRegularExpression('/<ul data-source-line="1">/', $html);
$this->assertMatchesRegularExpression('/<li data-source-line="1">/', $html);
$this->assertStringContainsString('<p data-source-line="1">quoted item</p>', $html);
$this->assertMatchesRegularExpression('/<li data-source-line="1">\nquoted item\n/', $html);
$this->assertMatchesRegularExpression('/<ul data-source-line="2">/', $html);
$this->assertMatchesRegularExpression('/<li data-source-line="2">/', $html);
$this->assertStringContainsString('<p data-source-line="2">nested item</p>', $html);
$this->assertMatchesRegularExpression('/<li data-source-line="2">\nnested item\n/', $html);
}

public function testFootnoteContentBlocksAreStamped(): void
Expand Down Expand Up @@ -189,4 +189,17 @@ public function testCustomBlockContentIsNotStampedWithLocalLines(): void
$this->assertStringContainsString('<div data-source-line="3">', $html);
$this->assertStringContainsString('<p>nested</p>', $html);
}

public function testEnabledOutputIsStructureIdenticalToDisabled(): void
{
// The option is attribute-only: stripping the stamped attributes must
// reproduce the default output byte for byte. Regression: tight list
// items used to grow <p> wrappers because the stamped paragraph no
// longer matched the renderer's tight-strip pattern.
$doc = "> - quoted item\n\n- a\n- b\n\n1. loose\n\n para\n\n:: nope\n";
$off = (new DjotConverter())->convert($doc);
$on = (new DjotConverter(sourceLines: true))->convert($doc);

$this->assertSame($off, preg_replace('/ data-source-line="\d+"/', '', $on));
}
}
Loading