From 987b08a5a8b282169bc55d3115acc5787b3ac36c Mon Sep 17 00:00:00 2001 From: Evan Wallace Date: Sun, 16 Jul 2023 18:33:07 -0400 Subject: [PATCH] css: wrap subclass selectors in a struct --- internal/css_ast/css_ast.go | 23 ++++++++------ internal/css_parser/css_nesting.go | 32 ++++++++++++-------- internal/css_parser/css_parser.go | 2 +- internal/css_parser/css_parser_selector.go | 35 ++++++++++++++-------- internal/css_printer/css_printer.go | 4 +-- 5 files changed, 59 insertions(+), 37 deletions(-) diff --git a/internal/css_ast/css_ast.go b/internal/css_ast/css_ast.go index 81cf809ff30..183fcb5d2a9 100644 --- a/internal/css_ast/css_ast.go +++ b/internal/css_ast/css_ast.go @@ -617,8 +617,8 @@ func HashComplexSelectors(hash uint32, selectors []ComplexSelector) uint32 { hash = helpers.HashCombine(hash, 0) } hash = helpers.HashCombine(hash, uint32(len(sel.SubclassSelectors))) - for _, sub := range sel.SubclassSelectors { - hash = helpers.HashCombine(hash, sub.Hash()) + for _, ss := range sel.SubclassSelectors { + hash = helpers.HashCombine(hash, ss.Data.Hash()) } hash = helpers.HashCombine(hash, uint32(sel.Combinator.Byte)) } @@ -644,7 +644,7 @@ func (sel ComplexSelector) IsRelative() bool { return false } for _, ss := range inner.SubclassSelectors { - if pseudo, ok := ss.(*SSPseudoClassWithSelectorList); ok { + if pseudo, ok := ss.Data.(*SSPseudoClassWithSelectorList); ok { for _, nested := range pseudo.Selectors { if !nested.IsRelative() { return false @@ -671,8 +671,8 @@ func tokensContainAmpersandRecursive(tokens []Token) bool { func (sel ComplexSelector) UsesPseudoElement() bool { for _, sel := range sel.Selectors { - for _, sub := range sel.SubclassSelectors { - if class, ok := sub.(*SSPseudoClass); ok { + for _, ss := range sel.SubclassSelectors { + if class, ok := ss.Data.(*SSPseudoClass); ok { if class.IsElement { return true } @@ -713,7 +713,7 @@ func (a ComplexSelector) Equal(b ComplexSelector, check *CrossFileEqualityCheck) return false } for j, aj := range ai.SubclassSelectors { - if !aj.Equal(bi.SubclassSelectors[j], check) { + if !aj.Data.Equal(bi.SubclassSelectors[j].Data, check) { return false } } @@ -729,7 +729,7 @@ type Combinator struct { type CompoundSelector struct { TypeSelector *NamespacedName - SubclassSelectors []SS + SubclassSelectors []SubclassSelector Combinator Combinator // Optional, may be 0 HasNestingSelector bool // "&" } @@ -747,9 +747,10 @@ func (sel CompoundSelector) Clone() CompoundSelector { } if sel.SubclassSelectors != nil { - selectors := make([]SS, len(sel.SubclassSelectors)) + selectors := make([]SubclassSelector, len(sel.SubclassSelectors)) for i, ss := range sel.SubclassSelectors { - selectors[i] = ss.Clone() + ss.Data = ss.Data.Clone() + selectors[i] = ss } clone.SubclassSelectors = selectors } @@ -789,6 +790,10 @@ func (a NamespacedName) Equal(b NamespacedName) bool { (a.NamespacePrefix == nil || b.NamespacePrefix == nil || a.NamespacePrefix.Equal(b.Name)) } +type SubclassSelector struct { + Data SS +} + type SS interface { Equal(ss SS, check *CrossFileEqualityCheck) bool Hash() uint32 diff --git a/internal/css_parser/css_nesting.go b/internal/css_parser/css_nesting.go index d0b42cde11c..a73d36f9abf 100644 --- a/internal/css_parser/css_nesting.go +++ b/internal/css_parser/css_nesting.go @@ -9,7 +9,9 @@ func lowerNestingInRule(rule css_ast.Rule, results []css_ast.Rule) []css_ast.Rul case *css_ast.RSelector: scope := css_ast.ComplexSelector{ Selectors: []css_ast.CompoundSelector{{ - SubclassSelectors: []css_ast.SS{&css_ast.SSPseudoClass{Name: "scope"}}, + SubclassSelectors: []css_ast.SubclassSelector{{ + Data: &css_ast.SSPseudoClass{Name: "scope"}, + }}, }}, } @@ -260,21 +262,25 @@ func substituteAmpersandsInCompoundSelector(sel css_ast.CompoundSelector, replac // ".foo .bar { :hover & {} }" => ":hover :is(.foo .bar) {}" // ".foo .bar { > &:hover {} }" => ".foo .bar > :is(.foo .bar):hover {}" single = css_ast.CompoundSelector{ - SubclassSelectors: []css_ast.SS{&css_ast.SSPseudoClassWithSelectorList{ - Kind: css_ast.PseudoClassIs, - Selectors: []css_ast.ComplexSelector{replacement.CloneWithoutLeadingCombinator()}, + SubclassSelectors: []css_ast.SubclassSelector{{ + Data: &css_ast.SSPseudoClassWithSelectorList{ + Kind: css_ast.PseudoClassIs, + Selectors: []css_ast.ComplexSelector{replacement.CloneWithoutLeadingCombinator()}, + }, }}, } } - var subclassSelectorPrefix []css_ast.SS + var subclassSelectorPrefix []css_ast.SubclassSelector // Insert the type selector if single.TypeSelector != nil { if sel.TypeSelector != nil { - subclassSelectorPrefix = append(subclassSelectorPrefix, &css_ast.SSPseudoClassWithSelectorList{ - Kind: css_ast.PseudoClassIs, - Selectors: []css_ast.ComplexSelector{{Selectors: []css_ast.CompoundSelector{{TypeSelector: sel.TypeSelector}}}}, + subclassSelectorPrefix = append(subclassSelectorPrefix, css_ast.SubclassSelector{ + Data: &css_ast.SSPseudoClassWithSelectorList{ + Kind: css_ast.PseudoClassIs, + Selectors: []css_ast.ComplexSelector{{Selectors: []css_ast.CompoundSelector{{TypeSelector: sel.TypeSelector}}}}, + }, }) } sel.TypeSelector = single.TypeSelector @@ -291,7 +297,7 @@ func substituteAmpersandsInCompoundSelector(sel css_ast.CompoundSelector, replac // "div { :is(&.foo) {} }" => ":is(div.foo) {}" for _, ss := range sel.SubclassSelectors { - if class, ok := ss.(*css_ast.SSPseudoClassWithSelectorList); ok { + if class, ok := ss.Data.(*css_ast.SSPseudoClassWithSelectorList); ok { outer := make([]css_ast.ComplexSelector, 0, len(class.Selectors)) for _, complex := range class.Selectors { inner := make([]css_ast.CompoundSelector, 0, len(complex.Selectors)) @@ -327,9 +333,11 @@ func multipleComplexSelectorsToSingleComplexSelector(selectors []css_ast.Complex return css_ast.ComplexSelector{ Selectors: []css_ast.CompoundSelector{{ Combinator: leadingCombinator, - SubclassSelectors: []css_ast.SS{&css_ast.SSPseudoClassWithSelectorList{ - Kind: css_ast.PseudoClassIs, - Selectors: clones, + SubclassSelectors: []css_ast.SubclassSelector{{ + Data: &css_ast.SSPseudoClassWithSelectorList{ + Kind: css_ast.PseudoClassIs, + Selectors: clones, + }, }}, }}, } diff --git a/internal/css_parser/css_parser.go b/internal/css_parser/css_parser.go index 8078dcdd46c..7df7f87d533 100644 --- a/internal/css_parser/css_parser.go +++ b/internal/css_parser/css_parser.go @@ -811,7 +811,7 @@ func isSafeSelectors(complexSelectors []css_ast.ComplexSelector) bool { } for _, ss := range compound.SubclassSelectors { - switch s := ss.(type) { + switch s := ss.Data.(type) { case *css_ast.SSAttribute: if s.MatcherModifier != 0 { // Bail if we hit a case modifier, which doesn't work in IE at all diff --git a/internal/css_parser/css_parser_selector.go b/internal/css_parser/css_parser_selector.go index 32af425a1c9..38e9e7867f3 100644 --- a/internal/css_parser/css_parser_selector.go +++ b/internal/css_parser/css_parser_selector.go @@ -86,7 +86,7 @@ func flattenLocalAndGlobalSelectors(list []css_ast.ComplexSelector, sel css_ast. // multiple complex selectors. if len(sel.Selectors) == 1 { if single := sel.Selectors[0]; !single.HasNestingSelector && single.TypeSelector == nil && len(single.SubclassSelectors) == 1 && single.Combinator.Byte == 0 { - if pseudo, ok := single.SubclassSelectors[0].(*css_ast.SSPseudoClassWithSelectorList); ok && (pseudo.Kind == css_ast.PseudoClassGlobal || pseudo.Kind == css_ast.PseudoClassLocal) { + if pseudo, ok := single.SubclassSelectors[0].Data.(*css_ast.SSPseudoClassWithSelectorList); ok && (pseudo.Kind == css_ast.PseudoClassGlobal || pseudo.Kind == css_ast.PseudoClassLocal) { // ":local(.a, .b)" => ".a, .b" return append(list, pseudo.Selectors...) } @@ -99,7 +99,7 @@ func flattenLocalAndGlobalSelectors(list []css_ast.ComplexSelector, sel css_ast. // But if we can't, we just turn it into an ":is()" instead. for _, s := range sel.Selectors { for _, ss := range s.SubclassSelectors { - if pseudo, ok := ss.(*css_ast.SSPseudoClassWithSelectorList); ok && (pseudo.Kind == css_ast.PseudoClassGlobal || pseudo.Kind == css_ast.PseudoClassLocal) { + if pseudo, ok := ss.Data.(*css_ast.SSPseudoClassWithSelectorList); ok && (pseudo.Kind == css_ast.PseudoClassGlobal || pseudo.Kind == css_ast.PseudoClassLocal) { // Only do the work to flatten the whole list if there's a ":local" or a ":global" var selectors []css_ast.CompoundSelector for _, s := range sel.Selectors { @@ -108,7 +108,7 @@ func flattenLocalAndGlobalSelectors(list []css_ast.ComplexSelector, sel css_ast. // done separately from the loop below because inlining may produce // multiple compound selectors. if !s.HasNestingSelector && s.TypeSelector == nil && len(s.SubclassSelectors) == 1 { - if pseudo, ok := s.SubclassSelectors[0].(*css_ast.SSPseudoClassWithSelectorList); ok && + if pseudo, ok := s.SubclassSelectors[0].Data.(*css_ast.SSPseudoClassWithSelectorList); ok && (pseudo.Kind == css_ast.PseudoClassGlobal || pseudo.Kind == css_ast.PseudoClassLocal) && len(pseudo.Selectors) == 1 { if nested := pseudo.Selectors[0].Selectors; ok && (s.Combinator.Byte == 0 || nested[0].Combinator.Byte == 0) { if s.Combinator.Byte != 0 { @@ -122,9 +122,9 @@ func flattenLocalAndGlobalSelectors(list []css_ast.ComplexSelector, sel css_ast. } } - var subclassSelectors []css_ast.SS + var subclassSelectors []css_ast.SubclassSelector for _, ss := range s.SubclassSelectors { - if pseudo, ok := ss.(*css_ast.SSPseudoClassWithSelectorList); ok && (pseudo.Kind == css_ast.PseudoClassGlobal || pseudo.Kind == css_ast.PseudoClassLocal) { + if pseudo, ok := ss.Data.(*css_ast.SSPseudoClassWithSelectorList); ok && (pseudo.Kind == css_ast.PseudoClassGlobal || pseudo.Kind == css_ast.PseudoClassLocal) { // If the contents are a single compound selector, try to merge the contents into this compound selector if len(pseudo.Selectors) == 1 && len(pseudo.Selectors[0].Selectors) == 1 { if single := pseudo.Selectors[0].Selectors[0]; single.Combinator.Byte == 0 && (s.TypeSelector == nil || single.TypeSelector == nil) { @@ -303,8 +303,10 @@ subclassSelectors: } nameLoc := logger.Loc{Start: p.current().Range.Loc.Start + 1} name := p.decoded() - sel.SubclassSelectors = append(sel.SubclassSelectors, &css_ast.SSHash{ - Name: ast.LocRef{Loc: nameLoc, Ref: p.symbolForName(name)}, + sel.SubclassSelectors = append(sel.SubclassSelectors, css_ast.SubclassSelector{ + Data: &css_ast.SSHash{ + Name: ast.LocRef{Loc: nameLoc, Ref: p.symbolForName(name)}, + }, }) p.advance() @@ -312,8 +314,10 @@ subclassSelectors: p.advance() nameLoc := p.current().Range.Loc name := p.decoded() - sel.SubclassSelectors = append(sel.SubclassSelectors, &css_ast.SSClass{ - Name: ast.LocRef{Loc: nameLoc, Ref: p.symbolForName(name)}, + sel.SubclassSelectors = append(sel.SubclassSelectors, css_ast.SubclassSelector{ + Data: &css_ast.SSClass{ + Name: ast.LocRef{Loc: nameLoc, Ref: p.symbolForName(name)}, + }, }) if !p.expect(css_lexer.TIdent) { return @@ -324,7 +328,9 @@ subclassSelectors: if !good { return } - sel.SubclassSelectors = append(sel.SubclassSelectors, &attr) + sel.SubclassSelectors = append(sel.SubclassSelectors, css_ast.SubclassSelector{ + Data: &attr, + }) case css_lexer.TColon: if p.next().Kind == css_lexer.TColon { @@ -350,12 +356,15 @@ subclassSelectors: } } - sel.SubclassSelectors = append(sel.SubclassSelectors, pseudo) + sel.SubclassSelectors = append(sel.SubclassSelectors, css_ast.SubclassSelector{ + Data: pseudo, + }) } break subclassSelectors } - pseudo := p.parsePseudoClassSelector(false) - sel.SubclassSelectors = append(sel.SubclassSelectors, pseudo) + sel.SubclassSelectors = append(sel.SubclassSelectors, css_ast.SubclassSelector{ + Data: p.parsePseudoClassSelector(false), + }) case css_lexer.TDelimAmpersand: // This is an extension: https://drafts.csswg.org/css-nesting-1/ diff --git a/internal/css_printer/css_printer.go b/internal/css_printer/css_printer.go index 71ce59d4c85..f50407cc15e 100644 --- a/internal/css_printer/css_printer.go +++ b/internal/css_printer/css_printer.go @@ -453,7 +453,7 @@ func (p *printer) printCompoundSelector(sel css_ast.CompoundSelector, isFirst bo p.print("&") } - for i, sub := range sel.SubclassSelectors { + for i, ss := range sel.SubclassSelectors { whitespace := mayNeedWhitespaceAfter // There is no chance of whitespace between subclass selectors @@ -461,7 +461,7 @@ func (p *printer) printCompoundSelector(sel css_ast.CompoundSelector, isFirst bo whitespace = canDiscardWhitespaceAfter } - switch s := sub.(type) { + switch s := ss.Data.(type) { case *css_ast.SSHash: p.print("#")