Skip to content

Commit 73531c7

Browse files
committed
[BUGFIX] Parse comment(s) immediately preceding selector
Also parse consecutive comments.
1 parent 8d123a2 commit 73531c7

File tree

4 files changed

+124
-8
lines changed

4 files changed

+124
-8
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ Please also have a look at our
2424

2525
### Fixed
2626

27+
- Parse comment(s) immediately preceding a selector (#1421)
28+
- Parse consecutive comments (#1421)
2729
- Support attribute selectors with values containing commas in
2830
`DeclarationBlock::setSelectors()` (#1419)
2931
- Allow `removeDeclarationBlockBySelector()` to be order-insensitve (#1406)

src/Parsing/ParserState.php

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ public function consumeUntil(
343343
$consumedCharacters = '';
344344
$start = $this->currentPosition;
345345

346+
$comments = \array_merge($comments, $this->consumeComments());
346347
while (!$this->isEnd()) {
347348
$character = $this->consume(1);
348349
if (\in_array($character, $stopCharacters, true)) {
@@ -354,10 +355,7 @@ public function consumeUntil(
354355
return $consumedCharacters;
355356
}
356357
$consumedCharacters .= $character;
357-
$comment = $this->consumeComment();
358-
if ($comment instanceof Comment) {
359-
$comments[] = $comment;
360-
}
358+
$comments = \array_merge($comments, $this->consumeComments());
361359
}
362360

363361
if (\in_array(self::EOF, $stopCharacters, true)) {
@@ -455,4 +453,21 @@ private function strsplit(string $string): array
455453

456454
return $result;
457455
}
456+
457+
/**
458+
* @return list<Comment>
459+
*/
460+
private function consumeComments(): array
461+
{
462+
$comments = [];
463+
464+
while (true) {
465+
$comment = $this->consumeComment();
466+
if ($comment instanceof Comment) {
467+
$comments[] = $comment;
468+
} else {
469+
return $comments;
470+
}
471+
}
472+
}
458473
}

tests/ParserTest.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,10 +1048,9 @@ public function commentExtracting(): void
10481048
$fooBarBlock = $nodes[1];
10491049
self::assertInstanceOf(Commentable::class, $fooBarBlock);
10501050
$fooBarBlockComments = $fooBarBlock->getComments();
1051-
// TODO Support comments in selectors.
1052-
// $this->assertCount(2, $fooBarBlockComments);
1053-
// $this->assertSame("* Number 4 *", $fooBarBlockComments[0]->getComment());
1054-
// $this->assertSame("* Number 5 *", $fooBarBlockComments[1]->getComment());
1051+
self::assertCount(2, $fooBarBlockComments);
1052+
self::assertSame(' Number 4 ', $fooBarBlockComments[0]->getComment());
1053+
self::assertSame(' Number 5 ', $fooBarBlockComments[1]->getComment());
10551054

10561055
// Declaration rules.
10571056
self::assertInstanceOf(DeclarationBlock::class, $fooBarBlock);
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sabberworm\CSS\Tests\Unit\Parsing;
6+
7+
use PHPUnit\Framework\TestCase;
8+
use Sabberworm\CSS\Comment\Comment;
9+
use Sabberworm\CSS\Parsing\ParserState;
10+
use Sabberworm\CSS\Settings;
11+
12+
/**
13+
* @covers \Sabberworm\CSS\Parsing\ParserState
14+
*/
15+
final class ParserStateTest extends TestCase
16+
{
17+
/**
18+
* @return array<
19+
* string,
20+
* array{
21+
* text: non-empty-string,
22+
* stopCharacter: non-empty-string,
23+
* expectedConsumedText: non-empty-string,
24+
* expectedComments: non-empty-list<non-empty-string>
25+
* }
26+
* >
27+
*/
28+
public static function provideTextForConsumptionWithComments(): array
29+
{
30+
return [
31+
'comment at start' => [
32+
'text' => '/*comment*/hello{',
33+
'stopCharacter' => '{',
34+
'expectedConsumedText' => 'hello',
35+
'expectedComments' => ['comment'],
36+
],
37+
'comment at end' => [
38+
'text' => 'hello/*comment*/{',
39+
'stopCharacter' => '{',
40+
'expectedConsumedText' => 'hello',
41+
'expectedComments' => ['comment'],
42+
],
43+
'comment in middle' => [
44+
'text' => 'hell/*comment*/o{',
45+
'stopCharacter' => '{',
46+
'expectedConsumedText' => 'hello',
47+
'expectedComments' => ['comment'],
48+
],
49+
'two comments at start' => [
50+
'text' => '/*comment1*//*comment2*/hello{',
51+
'stopCharacter' => '{',
52+
'expectedConsumedText' => 'hello',
53+
'expectedComments' => ['comment1', 'comment2'],
54+
],
55+
'two comments at end' => [
56+
'text' => 'hello/*comment1*//*comment2*/{',
57+
'stopCharacter' => '{',
58+
'expectedConsumedText' => 'hello',
59+
'expectedComments' => ['comment1', 'comment2'],
60+
],
61+
'two comments interspersed' => [
62+
'text' => 'he/*comment1*/ll/*comment2*/o{',
63+
'stopCharacter' => '{',
64+
'expectedConsumedText' => 'hello',
65+
'expectedComments' => ['comment1', 'comment2'],
66+
],
67+
];
68+
}
69+
70+
/**
71+
* @test
72+
*
73+
* @param non-empty-string $text
74+
* @param non-empty-string $stopCharacter
75+
* @param non-empty-string $expectedConsumedText
76+
* @param non-empty-list<non-empty-string> $expectedComments
77+
*
78+
* @dataProvider provideTextForConsumptionWithComments
79+
*/
80+
public function consumeUntilExtractsComments(
81+
string $text,
82+
string $stopCharacter,
83+
string $expectedConsumedText,
84+
array $expectedComments
85+
): void {
86+
$subject = new ParserState($text, Settings::create());
87+
88+
$comments = [];
89+
$result = $subject->consumeUntil($stopCharacter, false, false, $comments);
90+
$commentsAsText = \array_map(
91+
static function (Comment $comment): string {
92+
return $comment->getComment();
93+
},
94+
$comments
95+
);
96+
97+
self::assertSame($expectedConsumedText, $result);
98+
self::assertSame($expectedComments, $commentsAsText);
99+
}
100+
}

0 commit comments

Comments
 (0)