Skip to content

Commit 42e7432

Browse files
committed
Support for comment parsing
* origin/pr/110: Handle comments glued to block and rules Primary support for comment parsing
2 parents e0458bb + 665d957 commit 42e7432

File tree

12 files changed

+295
-23
lines changed

12 files changed

+295
-23
lines changed

lib/Sabberworm/CSS/CSSList/CSSList.php

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,20 @@
66
use Sabberworm\CSS\RuleSet\DeclarationBlock;
77
use Sabberworm\CSS\RuleSet\RuleSet;
88
use Sabberworm\CSS\Property\Selector;
9+
use Sabberworm\CSS\Comment\Commentable;
910

1011
/**
1112
* A CSSList is the most generic container available. Its contents include RuleSet as well as other CSSList objects.
1213
* Also, it may contain Import and Charset objects stemming from @-rules.
1314
*/
14-
abstract class CSSList implements Renderable {
15+
abstract class CSSList implements Renderable, Commentable {
1516

17+
protected $aComments;
1618
protected $aContents;
1719
protected $iLineNo;
1820

1921
public function __construct($iLineNo = 0) {
22+
$this->aComments = array();
2023
$this->aContents = array();
2124
$this->iLineNo = $iLineNo;
2225
}
@@ -118,4 +121,26 @@ public abstract function isRootList();
118121
public function getContents() {
119122
return $this->aContents;
120123
}
124+
125+
/**
126+
* @param array $aComments Array of comments.
127+
*/
128+
public function addComments(array $aComments) {
129+
$this->aComments = array_merge($this->aComments, $aComments);
130+
}
131+
132+
/**
133+
* @return array
134+
*/
135+
public function getComments() {
136+
return $this->aComments;
137+
}
138+
139+
/**
140+
* @param array $aComments Array containing Comment objects.
141+
*/
142+
public function setComments(array $aComments) {
143+
$this->aComments = $aComments;
144+
}
145+
121146
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
namespace Sabberworm\CSS\Comment;
4+
5+
use Sabberworm\CSS\Renderable;
6+
7+
class Comment implements Renderable {
8+
protected $iLineNo;
9+
protected $sComment;
10+
11+
public function __construct($sComment = '', $iLineNo = 0) {
12+
$this->sComment = $sComment;
13+
$this->iLineNo = $iLineNo;
14+
}
15+
16+
/**
17+
* @return string
18+
*/
19+
public function getComment() {
20+
return $this->sComment;
21+
}
22+
23+
/**
24+
* @return int
25+
*/
26+
public function getLineNo() {
27+
return $this->iLineNo;
28+
}
29+
30+
/**
31+
* @return string
32+
*/
33+
public function setComment($sComment) {
34+
$this->sComment = $sComment;
35+
}
36+
37+
/**
38+
* @return string
39+
*/
40+
public function __toString() {
41+
return $this->render(new \Sabberworm\CSS\OutputFormat());
42+
}
43+
44+
/**
45+
* @return string
46+
*/
47+
public function render(\Sabberworm\CSS\OutputFormat $oOutputFormat) {
48+
return '/*' . $this->sComment . '*/';
49+
}
50+
51+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace Sabberworm\CSS\Comment;
4+
5+
interface Commentable {
6+
7+
/**
8+
* @param array $aComments Array of comments.
9+
*/
10+
public function addComments(array $aComments);
11+
12+
/**
13+
* @return array
14+
*/
15+
public function getComments();
16+
17+
/**
18+
* @param array $aComments Array containing Comment objects.
19+
*/
20+
public function setComments(array $aComments);
21+
22+
23+
}

lib/Sabberworm/CSS/Parser.php

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use Sabberworm\CSS\Value\CSSString;
2222
use Sabberworm\CSS\Rule\Rule;
2323
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
24+
use Sabberworm\CSS\Comment\Comment;
2425

2526
/**
2627
* Parser class parses CSS from text into a data structure.
@@ -83,12 +84,12 @@ public function parse() {
8384
}
8485

8586
private function parseDocument(Document $oDocument) {
86-
$this->consumeWhiteSpace();
8787
$this->parseList($oDocument, true);
8888
}
8989

9090
private function parseList(CSSList $oList, $bIsRoot = false) {
9191
while (!$this->isEnd()) {
92+
$comments = $this->consumeWhiteSpace();
9293
$oListItem = null;
9394
if($this->oParserSettings->bLenientParsing) {
9495
try {
@@ -104,9 +105,9 @@ private function parseList(CSSList $oList, $bIsRoot = false) {
104105
return;
105106
}
106107
if($oListItem) {
108+
$oListItem->setComments($comments);
107109
$oList->append($oListItem);
108110
}
109-
$this->consumeWhiteSpace();
110111
}
111112
if (!$bIsRoot) {
112113
throw new SourceException("Unexpected end of document", $this->iLineNo);
@@ -161,7 +162,6 @@ private function parseAtRule() {
161162
$oResult = new KeyFrame($iIdentifierLineNum);
162163
$oResult->setVendorKeyFrame($sIdentifier);
163164
$oResult->setAnimationName(trim($this->consumeUntil('{', false, true)));
164-
$this->consumeWhiteSpace();
165165
$this->parseList($oResult);
166166
return $oResult;
167167
} else if ($sIdentifier === 'namespace') {
@@ -182,7 +182,6 @@ private function parseAtRule() {
182182
} else {
183183
//Unknown other at rule (font-face or such)
184184
$sArgs = trim($this->consumeUntil('{', false, true));
185-
$this->consumeWhiteSpace();
186185
$bUseRuleSet = true;
187186
foreach($this->blockRules as $sBlockRuleName) {
188187
if($this->identifierIs($sIdentifier, $sBlockRuleName)) {
@@ -303,17 +302,17 @@ private function parseCharacter($bIsForIdentifier) {
303302
}
304303

305304
private function parseSelector() {
305+
$aComments = array();
306306
$oResult = new DeclarationBlock($this->iLineNo);
307-
$oResult->setSelector($this->consumeUntil('{', false, true));
308-
$this->consumeWhiteSpace();
307+
$oResult->setSelector($this->consumeUntil('{', false, true, $aComments));
308+
$oResult->setComments($aComments);
309309
$this->parseRuleSet($oResult);
310310
return $oResult;
311311
}
312312

313313
private function parseRuleSet($oRuleSet) {
314314
while ($this->comes(';')) {
315315
$this->consume(';');
316-
$this->consumeWhiteSpace();
317316
}
318317
while (!$this->comes('}')) {
319318
$oRule = null;
@@ -327,7 +326,6 @@ private function parseRuleSet($oRuleSet) {
327326
if($this->streql(substr($sConsume, -1), '}')) {
328327
--$this->iCurrentPosition;
329328
} else {
330-
$this->consumeWhiteSpace();
331329
while ($this->comes(';')) {
332330
$this->consume(';');
333331
}
@@ -343,14 +341,15 @@ private function parseRuleSet($oRuleSet) {
343341
if($oRule) {
344342
$oRuleSet->addRule($oRule);
345343
}
346-
$this->consumeWhiteSpace();
347344
}
348345
$this->consume('}');
349346
}
350347

351348
private function parseRule() {
349+
$aComments = $this->consumeWhiteSpace();
352350
$oRule = new Rule($this->parseIdentifier(), $this->iLineNo);
353-
$this->consumeWhiteSpace();
351+
$oRule->setComments($aComments);
352+
$oRule->addComments($this->consumeWhiteSpace());
354353
$this->consume(':');
355354
$oValue = $this->parseValue(self::listDelimiterForRule($oRule->getRule()));
356355
$oRule->setValue($oValue);
@@ -369,7 +368,6 @@ private function parseRule() {
369368
}
370369
while ($this->comes(';')) {
371370
$this->consume(';');
372-
$this->consumeWhiteSpace();
373371
}
374372
return $oRule;
375373
}
@@ -568,48 +566,65 @@ private function consumeExpression($mExpression) {
568566
}
569567

570568
private function consumeWhiteSpace() {
569+
$comments = array();
571570
do {
572571
while (preg_match('/\\s/isSu', $this->peek()) === 1) {
573572
$this->consume(1);
574573
}
575574
if($this->oParserSettings->bLenientParsing) {
576575
try {
577-
$bHasComment = $this->consumeComment();
576+
$oComment = $this->consumeComment();
578577
} catch(UnexpectedTokenException $e) {
579578
// When we can’t find the end of a comment, we assume the document is finished.
580579
$this->iCurrentPosition = $this->iLength;
581580
return;
582581
}
583582
} else {
584-
$bHasComment = $this->consumeComment();
583+
$oComment = $this->consumeComment();
585584
}
586-
} while($bHasComment);
585+
if ($oComment !== false) {
586+
$comments[] = $oComment;
587+
}
588+
} while($oComment !== false);
589+
return $comments;
587590
}
588591

592+
/**
593+
* @return false|Comment
594+
*/
589595
private function consumeComment() {
596+
$mComment = false;
590597
if ($this->comes('/*')) {
598+
$iLineNo = $this->iLineNo;
591599
$this->consume(1);
592-
while ($this->consume(1) !== '') {
600+
$mComment = '';
601+
while (($char = $this->consume(1)) !== '') {
602+
$mComment .= $char;
593603
if ($this->comes('*/')) {
594604
$this->consume(2);
595-
return true;
605+
break;
596606
}
597607
}
598608
}
599-
return false;
609+
610+
if ($mComment !== false) {
611+
// We skip the * which was included in the comment.
612+
return new Comment(substr($mComment, 1), $iLineNo);
613+
}
614+
615+
return $mComment;
600616
}
601617

602618
private function isEnd() {
603619
return $this->iCurrentPosition >= $this->iLength;
604620
}
605621

606-
private function consumeUntil($aEnd, $bIncludeEnd = false, $consumeEnd = false) {
622+
private function consumeUntil($aEnd, $bIncludeEnd = false, $consumeEnd = false, array &$comments = array()) {
607623
$aEnd = is_array($aEnd) ? $aEnd : array($aEnd);
608624
$out = '';
609625
$start = $this->iCurrentPosition;
610626

611627
while (($char = $this->consume(1)) !== '') {
612-
$this->consumeComment();
613628
if (in_array($char, $aEnd)) {
614629
if ($bIncludeEnd) {
615630
$out .= $char;
@@ -619,6 +634,9 @@ private function consumeUntil($aEnd, $bIncludeEnd = false, $consumeEnd = false)
619634
return $out;
620635
}
621636
$out .= $char;
637+
if ($comment = $this->consumeComment()) {
638+
$comments[] = $comment;
639+
}
622640
}
623641

624642
$this->iCurrentPosition = $start;

lib/Sabberworm/CSS/Property/AtRule.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
namespace Sabberworm\CSS\Property;
44

55
use Sabberworm\CSS\Renderable;
6+
use Sabberworm\CSS\Comment\Commentable;
67

7-
interface AtRule extends Renderable {
8+
interface AtRule extends Renderable, Commentable {
89
const BLOCK_RULES = 'media/document/supports/region-style/font-feature-values';
910
// Since there are more set rules than block rules, we’re whitelisting the block rules and have anything else be treated as a set rule.
1011
const SET_RULES = 'font-face/counter-style/page/swash/styleset/annotation'; //…and more font-specific ones (to be used inside font-feature-values)

lib/Sabberworm/CSS/Property/CSSNamespace.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ class CSSNamespace implements AtRule {
99
private $mUrl;
1010
private $sPrefix;
1111
private $iLineNo;
12+
protected $aComments;
1213

1314
public function __construct($mUrl, $sPrefix = null, $iLineNo = 0) {
1415
$this->mUrl = $mUrl;
1516
$this->sPrefix = $sPrefix;
1617
$this->iLineNo = $iLineNo;
18+
$this->aComments = array();
1719
}
1820

1921
/**
@@ -58,4 +60,16 @@ public function atRuleArgs() {
5860
}
5961
return $aResult;
6062
}
63+
64+
public function addComments(array $aComments) {
65+
$this->aComments = array_merge($this->aComments, $aComments);
66+
}
67+
68+
public function getComments() {
69+
return $this->aComments;
70+
}
71+
72+
public function setComments(array $aComments) {
73+
$this->aComments = $aComments;
74+
}
6175
}

lib/Sabberworm/CSS/Property/Charset.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ class Charset implements AtRule {
1313

1414
private $sCharset;
1515
protected $iLineNo;
16+
protected $aComment;
1617

1718
public function __construct($sCharset, $iLineNo = 0) {
1819
$this->sCharset = $sCharset;
1920
$this->iLineNo = $iLineNo;
21+
$this->aComments = array();
2022
}
2123

2224
/**
@@ -49,4 +51,16 @@ public function atRuleName() {
4951
public function atRuleArgs() {
5052
return $this->sCharset;
5153
}
54+
55+
public function addComments(array $aComments) {
56+
$this->aComments = array_merge($this->aComments, $aComments);
57+
}
58+
59+
public function getComments() {
60+
return $this->aComments;
61+
}
62+
63+
public function setComments(array $aComments) {
64+
$this->aComments = $aComments;
65+
}
5266
}

0 commit comments

Comments
 (0)