selector unification of pseudo parens

This commit is contained in:
ConnorSkees 2020-04-05 02:39:38 -04:00
parent b2451b45c6
commit b7b58c2ac6
2 changed files with 127 additions and 13 deletions

View File

@ -22,6 +22,10 @@ impl Selector {
pub const fn new() -> Selector { pub const fn new() -> Selector {
Selector(Vec::new()) Selector(Vec::new())
} }
pub fn contains_super_selector(&self) -> bool {
self.0.iter().any(|s| s.contains_super_selector)
}
} }
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
@ -29,6 +33,7 @@ struct SelectorPart {
pub inner: Vec<SelectorKind>, pub inner: Vec<SelectorKind>,
pub is_invisible: bool, pub is_invisible: bool,
pub has_newline: bool, pub has_newline: bool,
pub contains_super_selector: bool,
} }
impl Display for SelectorPart { impl Display for SelectorPart {
@ -167,7 +172,7 @@ impl Display for SelectorKind {
SelectorKind::PseudoElement(s) => write!(f, "::{}", s), SelectorKind::PseudoElement(s) => write!(f, "::{}", s),
SelectorKind::PseudoParen(s, val) => write!(f, ":{}({})", s, val), SelectorKind::PseudoParen(s, val) => write!(f, ":{}({})", s, val),
SelectorKind::Placeholder(s) => write!(f, "%{}", s), SelectorKind::Placeholder(s) => write!(f, "%{}", s),
SelectorKind::Super => todo!(), SelectorKind::Super => unreachable!("& selector should not be emitted"),
SelectorKind::Whitespace => f.write_char(' '), SelectorKind::Whitespace => f.write_char(' '),
} }
} }
@ -240,6 +245,7 @@ impl Selector {
let mut inner = Vec::new(); let mut inner = Vec::new();
let mut is_invisible = false; let mut is_invisible = false;
let mut has_newline = false; let mut has_newline = false;
let mut contains_super_selector = false;
let mut parts = Vec::new(); let mut parts = Vec::new();
// HACK: we re-lex here to get access to generic helper functions that // HACK: we re-lex here to get access to generic helper functions that
@ -255,7 +261,10 @@ impl Selector {
)?)); )?));
continue; continue;
} }
'&' => SelectorKind::Super, '&' => {
contains_super_selector = true;
SelectorKind::Super
}
'.' => { '.' => {
iter.next(); iter.next();
inner.push(SelectorKind::Class(eat_ident_no_interpolation(&mut iter)?)); inner.push(SelectorKind::Class(eat_ident_no_interpolation(&mut iter)?));
@ -288,11 +297,13 @@ impl Selector {
inner: inner.clone(), inner: inner.clone(),
is_invisible, is_invisible,
has_newline, has_newline,
contains_super_selector,
}); });
inner.clear(); inner.clear();
} }
is_invisible = false; is_invisible = false;
has_newline = false; has_newline = false;
contains_super_selector = false;
devour_whitespace(&mut iter); devour_whitespace(&mut iter);
continue; continue;
} }
@ -303,11 +314,16 @@ impl Selector {
} }
':' => { ':' => {
iter.next(); iter.next();
inner.push(Self::consume_pseudo_selector( let sel = Self::consume_pseudo_selector(&mut iter, scope, super_selector)?;
&mut iter, match &sel {
scope, SelectorKind::PseudoParen(_, s) => {
super_selector, if s.contains_super_selector() {
)?); contains_super_selector = true;
}
}
_ => {}
}
inner.push(sel);
continue; continue;
} }
c if c.is_whitespace() => { c if c.is_whitespace() => {
@ -326,6 +342,7 @@ impl Selector {
inner, inner,
is_invisible, is_invisible,
has_newline, has_newline,
contains_super_selector,
}); });
} }
@ -367,6 +384,55 @@ impl Selector {
} }
} }
fn replace(super_selector: Selector, this: Selector) -> Selector {
if super_selector.0.is_empty() {
return this;
}
if this.0.is_empty() {
return super_selector;
}
let mut parts = Vec::with_capacity(super_selector.0.len());
for (idx, part) in super_selector.clone().0.into_iter().enumerate() {
let mut found_inner = false;
for part2 in this.clone().0 {
if !part2.contains_super_selector {
if idx == 0 {
parts.push(part2);
}
continue;
}
let mut kinds = Vec::new();
for kind in part2.clone().inner {
match kind {
SelectorKind::Super => kinds.extend(part.inner.clone()),
SelectorKind::PseudoParen(name, inner) => {
if inner.contains_super_selector() {
found_inner = true;
kinds.push(SelectorKind::PseudoParen(
name,
Selector::replace(super_selector.clone(), inner),
))
} else {
kinds.push(SelectorKind::PseudoParen(name, inner));
}
}
_ => kinds.push(kind),
}
}
parts.push(SelectorPart {
inner: kinds,
is_invisible: part2.is_invisible,
has_newline: part2.has_newline,
contains_super_selector: false,
});
}
if found_inner {
break;
}
}
Selector(parts)
}
pub fn zip(&self, other: &Selector) -> Selector { pub fn zip(&self, other: &Selector) -> Selector {
if self.0.is_empty() { if self.0.is_empty() {
return Selector(other.0.clone()); return Selector(other.0.clone());
@ -375,16 +441,30 @@ impl Selector {
} }
let mut rules = Vec::with_capacity(self.0.len()); let mut rules = Vec::with_capacity(self.0.len());
for sel1 in self.clone().0 { for sel1 in self.clone().0 {
let mut found_inner = false;
for sel2 in other.clone().0 { for sel2 in other.clone().0 {
let mut this_selector: Vec<SelectorKind> = Vec::with_capacity(other.0.len()); let mut this_selector: Vec<SelectorKind> = Vec::with_capacity(other.0.len());
let mut found_super = false; let mut found_super = false;
for sel in sel2.inner { for sel in sel2.inner {
if sel == SelectorKind::Super { match sel {
this_selector.extend(sel1.clone().inner); SelectorKind::Super => {
this_selector.extend(sel1.inner.clone());
found_super = true; found_super = true;
}
SelectorKind::PseudoParen(s, inner_selector) => {
if inner_selector.contains_super_selector() {
found_super = true;
found_inner = true;
this_selector.push(SelectorKind::PseudoParen(
s,
Selector::replace(self.clone(), inner_selector),
))
} else { } else {
this_selector.push(sel.clone()); this_selector.push(SelectorKind::PseudoParen(s, inner_selector));
}
}
_ => this_selector.push(sel),
} }
} }
@ -398,9 +478,13 @@ impl Selector {
rules.push(SelectorPart { rules.push(SelectorPart {
inner: this_selector, inner: this_selector,
is_invisible: sel1.is_invisible || sel2.is_invisible, is_invisible: sel1.is_invisible || sel2.is_invisible,
has_newline: sel1.has_newline || sel2.has_newline, has_newline: (sel1.has_newline || sel2.has_newline) && !found_inner,
contains_super_selector: false,
}); });
} }
if found_inner {
break;
}
} }
Selector(rules) Selector(rules)
} }

View File

@ -333,3 +333,33 @@ error!(
modifier_on_any_attr, modifier_on_any_attr,
"[attr i] {color: foo;}", "Error: Expected \"]\"." "[attr i] {color: foo;}", "Error: Expected \"]\"."
); );
test!(
psuedo_paren_child_contains_ampersand,
".a, .b {\n :not(&-c) {\n d: e\n }\n}\n",
":not(.a-c, .b-c) {\n d: e;\n}\n"
);
test!(
psuedo_paren_child_no_ampersand_two_newlines__this_test_confounds_me,
".a, .b {\n :not(-c),\n :not(-c) {\n d: e\n }\n}\n",
".a :not(-c),\n.a :not(-c), .b :not(-c),\n.b :not(-c) {\n d: e;\n}\n"
);
test!(
psuedo_paren_child_ampersand_two_newlines__this_test_confounds_me,
".a, .b {\n :not(&-c, &-d),\n :not(&-c) {\n d: e\n }\n}\n",
":not(.a-c, .a-d, .b-c, .b-d), :not(.a-c, .b-c) {\n d: e;\n}\n"
);
test!(
psuedo_paren_child_ampersand_inner_psuedo_paren,
".a, .b {\n :not(:not(&-c)) {\n d: e\n }\n}\n",
":not(:not(.a-c, .b-c)) {\n d: e;\n}\n"
);
test!(
psuedo_paren_child_psuedo_paren_ampersand_inner_psuedo_paren,
".a, .b {\n :not(:not(c), &-d) {\n d: e\n }\n}\n",
":not(:not(c), .a-d, .b-d) {\n d: e;\n}\n"
);
test!(
psuedo_paren_child_ampersand_psuedo_paren__inner_psuedo_paren,
".a, .b {\n :not(&-d, :not(c)) {\n d: e\n }\n}\n",
":not(.a-d, :not(c), .b-d) {\n d: e;\n}\n"
);