diff --git a/src/Tokens/TextToken.php b/src/Tokens/TextToken.php
index 98cfef4..56e953b 100644
--- a/src/Tokens/TextToken.php
+++ b/src/Tokens/TextToken.php
@@ -13,7 +13,23 @@ public function __construct(
public function parse(Parser $parser): string
{
- return $this->content;
+ // A line ending preceded by two or more spaces, or by a backslash,
+ // is a hard line break — unless it ends the block, where it is
+ // dropped rather than rendered.
+ if (
+ ! str_contains($this->content, "\n")
+ && ! str_contains($this->content, "\r")
+ ) {
+ return $this->content;
+ }
+
+ return (
+ preg_replace(
+ '/(?: {2,}|\\\\)(\r\n|\n|\r)(?!\z)/',
+ '
$1',
+ $this->content,
+ ) ?? $this->content
+ );
}
public function append(string $content): void
diff --git a/tests/Tokens/TextTokenTest.php b/tests/Tokens/TextTokenTest.php
index 708fcf7..826b769 100644
--- a/tests/Tokens/TextTokenTest.php
+++ b/tests/Tokens/TextTokenTest.php
@@ -4,6 +4,8 @@
use PHPUnit\Framework\Attributes\Test;
use Tempest\Markdown\Parser;
+use Tempest\Markdown\Rules\BoldRule;
+use Tempest\Markdown\Rules\TextRule;
use Tempest\Markdown\Tests\ParserTestCase;
use Tempest\Markdown\Tokens\TextToken;
@@ -16,4 +18,58 @@ public function test_parse(): void
$this->assertEquals('Hello, world!', $token->parse(new Parser()));
}
+
+ #[Test]
+ public function parse_renders_a_hard_break_after_two_spaces(): void
+ {
+ $parser = new Parser(highlighter: null, rules: [new TextRule()]);
+
+ $this->assertSame(
+ "a
\nb",
+ new TextToken("a \nb")->parse($parser),
+ );
+ $this->assertSame(
+ "a
\nb",
+ new TextToken("a \nb")->parse($parser),
+ );
+ }
+
+ #[Test]
+ public function parse_renders_a_hard_break_after_a_backslash(): void
+ {
+ $parser = new Parser(highlighter: null, rules: [new TextRule()]);
+
+ $this->assertSame(
+ "a
\nb",
+ new TextToken("a\\\nb")->parse($parser),
+ );
+ }
+
+ #[Test]
+ public function parse_keeps_a_soft_break_as_is(): void
+ {
+ $parser = new Parser(highlighter: null, rules: [new TextRule()]);
+
+ $this->assertSame("a\nb", new TextToken("a\nb")->parse($parser));
+ $this->assertSame("a \nb", new TextToken("a \nb")->parse($parser));
+ }
+
+ #[Test]
+ public function parse_drops_a_hard_break_at_the_end_of_the_content(): void
+ {
+ $parser = new Parser(highlighter: null, rules: [new TextRule()]);
+
+ $this->assertSame("a \n", new TextToken("a \n")->parse($parser));
+ }
+
+ #[Test]
+ public function lex_renders_a_hard_break_around_other_inline_tokens(): void
+ {
+ $html = (string) new Parser(highlighter: null, rules: [
+ new BoldRule(),
+ new TextRule(),
+ ])->parse("a **b** \nc");
+
+ $this->assertSame("a b
\nc", $html);
+ }
}