Skip to content

Commit d62a9dc

Browse files
committed
CSS: update parseDeclaration to parse nested rulesets
1 parent 1aea605 commit d62a9dc

2 files changed

Lines changed: 44 additions & 28 deletions

File tree

css/parse.go

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -395,32 +395,51 @@ func (p *Parser) parseQualifiedRuleDeclarationList() GrammarType {
395395
}
396396

397397
func (p *Parser) parseDeclaration() GrammarType {
398+
var offset int
398399
p.initBuf()
399-
p.data = parse.ToLower(parse.Copy(p.data))
400-
401-
ttName, dataName := p.tt, p.data
402-
tt, data := p.popToken(false)
403-
if tt != ColonToken {
404-
p.l.r.Move(-len(data))
405-
p.err, p.errPos = "expected colon in declaration", p.l.r.Offset()
406-
p.l.r.Move(len(data))
407-
p.pushBuf(ttName, dataName)
408-
return p.parseDeclarationError(tt, data)
409-
}
410-
wsBeforeColon := p.prevWS
411-
412-
skipWS := true
400+
p.pushBuf(p.tt, p.data)
413401
for {
414402
tt, data := p.popToken(false)
415403
if (tt == SemicolonToken || tt == RightBraceToken) && p.level == 0 || tt == ErrorToken {
404+
// regular declaration
405+
p.data = parse.ToLower(parse.Copy(p.data))
406+
407+
p.buf = p.buf[1:]
408+
for 0 < len(p.buf) && p.buf[0].TokenType == WhitespaceToken {
409+
p.buf = p.buf[1:]
410+
}
411+
if len(p.buf) == 0 || p.buf[0].TokenType != ColonToken {
412+
if offset == 0 {
413+
offset = p.l.r.Offset()
414+
}
415+
p.err, p.errPos = "expected colon in declaration", offset
416+
return p.parseDeclarationError(tt, data)
417+
}
418+
p.buf = p.buf[1:]
419+
for 0 < len(p.buf) && p.buf[0].TokenType == WhitespaceToken {
420+
p.buf = p.buf[1:]
421+
}
422+
for i := 0; i < len(p.buf); i++ {
423+
if p.buf[i].TokenType == WhitespaceToken {
424+
if 0 < i {
425+
if data := p.buf[i-1].Data; len(data) == 1 && (data[0] == ',' || data[0] == '/' || data[0] == ':' || data[0] == '!' || data[0] == '=') {
426+
p.buf = append(p.buf[:i], p.buf[i+1:]...)
427+
continue
428+
}
429+
}
430+
if i+1 < len(p.buf) {
431+
if data := p.buf[i+1].Data; len(data) == 1 && (data[0] == ',' || data[0] == '/' || data[0] == ':' || data[0] == '!' || data[0] == '=') {
432+
p.buf = append(p.buf[:i], p.buf[i+1:]...)
433+
continue
434+
}
435+
}
436+
i++
437+
}
438+
}
416439
p.prevEnd = (tt == RightBraceToken)
417440
return DeclarationGrammar
418441
} else if tt == LeftBraceToken && p.level == 0 && p.isStylesheet {
419-
if wsBeforeColon {
420-
p.buf = append([]Token{{ttName, dataName}, {WhitespaceToken, wsBytes}, {ColonToken, colonBytes}}, p.buf...)
421-
} else {
422-
p.buf = append([]Token{{ttName, dataName}, {ColonToken, colonBytes}}, p.buf...)
423-
}
442+
// nested ruleset
424443
p.tt = WhitespaceToken
425444
p.data = emptyBytes
426445
p.state = append(p.state, (*Parser).parseQualifiedRuleDeclarationList)
@@ -429,22 +448,18 @@ func (p *Parser) parseDeclaration() GrammarType {
429448
p.level++
430449
} else if tt == RightParenthesisToken || tt == RightBraceToken || tt == RightBracketToken {
431450
if p.level == 0 {
432-
// TODO: buggy
433451
p.err, p.errPos = "unexpected ending in declaration", p.l.r.Offset()
434-
p.pushBuf(ttName, dataName)
435-
p.pushBuf(ColonToken, []byte{':'})
436452
return p.parseDeclarationError(tt, data)
437453
}
438454
p.level--
439455
}
440-
if len(data) == 1 && (data[0] == ',' || data[0] == '/' || data[0] == ':' || data[0] == '!' || data[0] == '=') {
441-
skipWS = true
442-
} else if (p.prevWS || p.prevComment) && !skipWS {
456+
if (p.prevWS || p.prevComment) && p.buf[len(p.buf)-1].TokenType != WhitespaceToken {
443457
p.pushBuf(WhitespaceToken, wsBytes)
444-
} else {
445-
skipWS = false
446458
}
447459
p.pushBuf(tt, data)
460+
if offset == 0 {
461+
offset = p.l.r.Offset() - len(data)
462+
}
448463
}
449464
}
450465

css/parse_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,9 @@ func TestParse(t *testing.T) {
8989
{false, "[class*=\"column\"]+[class*=\"column\"]:last-child{a:b;}", "[class*=\"column\"]+[class*=\"column\"]:last-child{a:b;}"},
9090
{false, "@media { @viewport }", "@media{@viewport;}"},
9191
{false, "table { @unknown }", "table{@unknown;}"},
92-
{false, "a{@media{width:70%;} b{width:60%;}}", "a{@media{ERROR(unexpected ending in qualified rule})ERROR(expected colon in declaration)}"},
92+
{false, "a{@media{width:70%;} b{width:60%;}}", "a{@media{ERROR(unexpected ending in qualified rule)b{width:60%;}}"},
9393
{false, "a{& :is(b) { }}", "a{& :is(b){}}"},
94+
{false, "a{&:is(b) :is(c) { }}", "a{&:is(b) :is(c){}}"},
9495

9596
// early endings
9697
{false, "selector{", "selector{"},

0 commit comments

Comments
 (0)