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
16 changes: 15 additions & 1 deletion src/Rules/HeadingRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,21 @@ public function __construct(

public function shouldParse(Parser $parser): bool
{
return $parser->comesNext('#', 1);
if (! $parser->comesNext('#', 1)) {
return false;
}

// An ATX heading is one to six `#` followed by whitespace or by the
// end of the line; `#title` and `#######` are paragraphs.
$level = strspn($parser->content, '#', $parser->position);

if ($level > 6) {
return false;
}

$next = $parser->content[$parser->position + $level] ?? null;

return $next === null || str_contains(Parser::WHITESPACE, $next);
}

public function parse(Parser $parser): Token
Expand Down
37 changes: 37 additions & 0 deletions tests/Rules/HeadingRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use PHPUnit\Framework\Attributes\Test;
use Tempest\Markdown\Parser;
use Tempest\Markdown\Rules\HeadingRule;
use Tempest\Markdown\Rules\ParagraphRule;
use Tempest\Markdown\Tests\ParserTestCase;

class HeadingRuleTest extends ParserTestCase
Expand Down Expand Up @@ -111,4 +112,40 @@ public function lex_keeps_an_explicit_id_without_generated_ids(): void

$this->assertSame('<h2 id="custom-id">A heading</h2>', $html);
}

#[Test]
public function lex_without_a_space_after_the_marker_is_not_a_heading(): void
{
$parser = new Parser(highlighter: null, rules: [
new HeadingRule(),
new ParagraphRule(),
]);

$this->assertSame('<p>#titre</p>', (string) $parser->parse('#titre'));
$this->assertSame(
'<p>#hashtag</p>',
(string) $parser->parse('#hashtag'),
);
$this->assertSame('<p>#5 bolt</p>', (string) $parser->parse('#5 bolt'));
}

#[Test]
public function lex_more_than_six_markers_is_not_a_heading(): void
{
$html = (string) new Parser(highlighter: null, rules: [
new HeadingRule(),
new ParagraphRule(),
])->parse('####### seven');

$this->assertSame('<p>####### seven</p>', $html);
}

#[Test]
public function lex_marker_alone_is_an_empty_heading(): void
{
$parser = new Parser(highlighter: null, rules: [new HeadingRule()]);

$this->assertSame('<h1></h1>', (string) $parser->parse('#'));
$this->assertSame('<h2></h2>', (string) $parser->parse('##'));
}
}
Loading