Implement & only inside selectors

This commit is contained in:
ConnorSkees 2020-01-11 16:12:23 -05:00
parent ebfeb35341
commit 97958b01a7
2 changed files with 72 additions and 11 deletions

View File

@ -22,7 +22,8 @@
clippy::too_many_lines, clippy::too_many_lines,
clippy::integer_arithmetic, clippy::integer_arithmetic,
clippy::missing_errors_doc, clippy::missing_errors_doc,
clippy::let_underscore_must_use clippy::let_underscore_must_use,
clippy::module_name_repetitions
)] )]
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::{self, Display}; use std::fmt::{self, Display};
@ -593,4 +594,25 @@ mod test_css {
"// a { color: red }\na {\n height: 1 1px;\n}\n", "// a { color: red }\na {\n height: 1 1px;\n}\n",
"a {\n height: 1 1px;\n}\n" "a {\n height: 1 1px;\n}\n"
); );
test!(
outer_ampersand,
"a, b {\n& c {\n color: red;\n}\n}\n",
"a c, b c {\n color: red;\n}\n"
);
test!(
inner_ampersand,
"a, b {\na & c {\n color: red;\n}\n}\n",
"a a c, a b c {\n color: red;\n}\n"
);
test!(
ampersand_multiple_whitespace,
" a , b {\n&c {\n color: red;\n}\n}\n",
"ac, bc {\n color: red;\n}\n"
);
test!(
ampersand_alone,
"a, b {\n& {\n color: red;\n}\n}\n",
"a, b {\n color: red;\n}\n"
);
} }

View File

@ -38,7 +38,7 @@ impl Display for Selector {
| Some(SelectorKind::Id) | Some(SelectorKind::Id)
| Some(SelectorKind::Universal) | Some(SelectorKind::Universal)
| Some(SelectorKind::Element(_)) => { | Some(SelectorKind::Element(_)) => {
write!(f, " {}", iter.next().expect("already peeked here"))?; write!(f, " ")?;
} }
_ => {} _ => {}
} }
@ -74,7 +74,7 @@ pub enum SelectorKind {
/// Pseudo selector: `:hover` /// Pseudo selector: `:hover`
Pseudo(String), Pseudo(String),
/// Use the super selector: `&.red` /// Use the super selector: `&.red`
// Super, Super,
/// Used to signify no selector (when there is no super_selector of a rule) /// Used to signify no selector (when there is no super_selector of a rule)
None, None,
Whitespace, Whitespace,
@ -94,8 +94,7 @@ impl Display for SelectorKind {
SelectorKind::Preceding => write!(f, " ~ "), SelectorKind::Preceding => write!(f, " ~ "),
SelectorKind::Attribute(attr) => write!(f, "{}", attr), SelectorKind::Attribute(attr) => write!(f, "{}", attr),
SelectorKind::Pseudo(s) => write!(f, ":{}", s), SelectorKind::Pseudo(s) => write!(f, ":{}", s),
// SelectorKind::Super => write!(f, "{}"), SelectorKind::Super | SelectorKind::None => write!(f, ""),
SelectorKind::None => write!(f, ""),
} }
} }
} }
@ -135,6 +134,17 @@ mod test_selector_display {
)), )),
"a c" "a c"
); );
test_selector_display!(
keeps_one_whitespace_with_three_els,
Selector((
Element("a".to_string()),
Whitespace,
Element("a".to_string()),
Whitespace,
Element("c".to_string()),
)),
"a a c"
);
} }
struct SelectorParser<'a> { struct SelectorParser<'a> {
@ -154,15 +164,29 @@ impl<'a> SelectorParser<'a> {
} }
fn all_selectors(&mut self) -> Selector { fn all_selectors(&mut self) -> Selector {
let mut v = Vec::new(); let mut v = Vec::with_capacity(self.tokens.len());
while let Some(s) = self.consume_selector() { while let Some(s) = self.consume_selector() {
v.push(s); v.push(s);
} }
while let Some(x) = v.pop() {
if x != SelectorKind::Whitespace {
v.push(x);
break;
}
}
Selector(v) Selector(v)
} }
fn consume_selector(&mut self) -> Option<SelectorKind> { fn consume_selector(&mut self) -> Option<SelectorKind> {
if self.devour_whitespace() { if self.devour_whitespace() {
if let Some(&&Token {
kind: TokenKind::Symbol(Symbol::Comma),
..
}) = self.tokens.peek()
{
self.tokens.next();
return Some(SelectorKind::Multiple);
}
return Some(SelectorKind::Whitespace); return Some(SelectorKind::Whitespace);
} }
if let Some(Token { kind, .. }) = self.tokens.next() { if let Some(Token { kind, .. }) = self.tokens.next() {
@ -186,7 +210,7 @@ impl<'a> SelectorParser<'a> {
TokenKind::Symbol(Symbol::Plus) => SelectorKind::Following, TokenKind::Symbol(Symbol::Plus) => SelectorKind::Following,
TokenKind::Symbol(Symbol::Tilde) => SelectorKind::Preceding, TokenKind::Symbol(Symbol::Tilde) => SelectorKind::Preceding,
TokenKind::Symbol(Symbol::Mul) => SelectorKind::Universal, TokenKind::Symbol(Symbol::Mul) => SelectorKind::Universal,
// TokenKind::Symbol(Symbol::BitAnd) => SelectorKind::Super, TokenKind::Symbol(Symbol::BitAnd) => SelectorKind::Super,
TokenKind::Attribute(attr) => SelectorKind::Attribute(attr.clone()), TokenKind::Attribute(attr) => SelectorKind::Attribute(attr.clone()),
_ => todo!("unimplemented selector"), _ => todo!("unimplemented selector"),
}); });
@ -221,7 +245,7 @@ impl Selector {
if self.0.is_empty() { if self.0.is_empty() {
return Selector(other.0); return Selector(other.0);
} }
let mut rules: Vec<SelectorKind> = Vec::with_capacity(self.0.len()); let mut rules: Vec<SelectorKind> = Vec::with_capacity(self.0.len() + other.0.len());
let sel1_split: Vec<Vec<SelectorKind>> = self let sel1_split: Vec<Vec<SelectorKind>> = self
.0 .0
.split(|sel| sel == &SelectorKind::Multiple) .split(|sel| sel == &SelectorKind::Multiple)
@ -234,9 +258,24 @@ impl Selector {
.collect(); .collect();
for (idx, sel1) in sel1_split.iter().enumerate() { for (idx, sel1) in sel1_split.iter().enumerate() {
for (idx2, sel2) in sel2_split.iter().enumerate() { for (idx2, sel2) in sel2_split.iter().enumerate() {
rules.extend(sel1.iter().cloned()); let mut this_selector = Vec::with_capacity(other.0.len());
rules.push(SelectorKind::Whitespace); let mut found_super = false;
rules.extend(sel2.iter().cloned());
for sel in sel2 {
if sel == &SelectorKind::Super {
this_selector.extend(sel1.iter().cloned());
found_super = true;
} else {
this_selector.push(sel.clone());
}
}
if !found_super {
rules.extend(sel1.iter().cloned());
rules.push(SelectorKind::Whitespace);
}
rules.extend(this_selector);
if !(idx + 1 == sel1_split.len() && idx2 + 1 == sel2_split.len()) { if !(idx + 1 == sel1_split.len() && idx2 + 1 == sel2_split.len()) {
rules.push(SelectorKind::Multiple); rules.push(SelectorKind::Multiple);
} }