diff --git a/src/Parser/BlockParser.php b/src/Parser/BlockParser.php index e9a89bf..bbade6d 100644 --- a/src/Parser/BlockParser.php +++ b/src/Parser/BlockParser.php @@ -2269,7 +2269,13 @@ protected function tryParseList(Node $parent, array $lines, int $start): ?int $this->parseBlocks($listItem, $itemLines, 0); } else { $paragraph = new Paragraph(); - $this->inlineParser->parse($paragraph, implode("\n", $itemLines), $start); + // Plain-paragraph item: continuation lines strip ALL leading + // whitespace (djot soft-break rule), matching tryParseParagraph(). + // itemLines keep indent relative to the item for the block-parse + // branch above; here that surplus indent would leak into the + // inline text (e.g. an over-indented continuation rendering as + // " c" instead of "c"). + $this->inlineParser->parse($paragraph, implode("\n", array_map('ltrim', $itemLines)), $start); $listItem->appendChild($paragraph); } } elseif ($itemLines !== ['']) { diff --git a/tests/TestCase/NestedListEdgeCasesTest.php b/tests/TestCase/NestedListEdgeCasesTest.php index 4474d8e..3d9c4b4 100644 --- a/tests/TestCase/NestedListEdgeCasesTest.php +++ b/tests/TestCase/NestedListEdgeCasesTest.php @@ -5,6 +5,7 @@ namespace Djot\Test\TestCase; use Djot\DjotConverter; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; /** @@ -732,4 +733,43 @@ public function testSingleBlankLineAllowsBlockElement(): void $this->assertNotFalse($headingPos); $this->assertGreaterThan($lastUlPos, $headingPos); } + + // ============ Over-indented paragraph continuation in a list item ============ + + /** + * A tight list-item paragraph continuation line indented MORE than the item + * content indent must strip all leading whitespace (djot soft-break rule), + * not just the item indent. Matches canonical djot.js, which renders every + * variant below identically. + * + * @return array + */ + public static function overIndentedContinuationProvider(): array + { + return [ + 'flush continuation' => ["- a\n - b\nc\n"], + 'one-space continuation' => ["- a\n - b\n c\n"], + 'content-indent continuation' => ["- a\n - b\n c\n"], + 'over-indented continuation' => ["- a\n - b\n c\n"], + ]; + } + + #[DataProvider('overIndentedContinuationProvider')] + public function testOverIndentedParagraphContinuationStripsSurplusIndent(string $djot): void + { + $result = $this->converter->convert($djot); + + // The "- b" line and "c" line are plain paragraph continuation text of + // item "a"; surplus indentation must not leak into the inline output. + $this->assertStringContainsString("a\n- b\nc", $result); + $this->assertStringNotContainsString(' c', $result); + } + + public function testListItemOverIndentedContinuationWithoutNestedMarker(): void + { + $result = $this->converter->convert("- a\n c\n"); + + $this->assertStringContainsString("a\nc", $result); + $this->assertStringNotContainsString(' c', $result); + } }