Handle pseudo selectors with arguments

This commit is contained in:
ConnorSkees 2020-01-11 19:16:59 -05:00
parent 6746ac1619
commit c73be13024
5 changed files with 73 additions and 19 deletions

View File

@ -75,7 +75,7 @@ impl Css {
} else { } else {
self.blocks[self.idx + self.inner_rulesets - 1].push_style(s) self.blocks[self.idx + self.inner_rulesets - 1].push_style(s)
} }
}, }
Stmt::MultilineComment(s) => { Stmt::MultilineComment(s) => {
if self.idx == 0 { if self.idx == 0 {
self.blocks.push(Toplevel::MultilineComment(s)); self.blocks.push(Toplevel::MultilineComment(s));

View File

@ -3,11 +3,7 @@ use crate::StyleSheetParser;
pub struct Importer {} pub struct Importer {}
impl Importer { impl Importer {
pub fn import(path: &str) { pub fn import(path: &str) {}
} fn find_files() {}
fn find_files() {
}
} }

View File

@ -40,6 +40,8 @@ impl<'a> Iterator for Lexer<'a> {
',' => symbol!(self, Comma), ',' => symbol!(self, Comma),
'.' => symbol!(self, Period), '.' => symbol!(self, Period),
';' => symbol!(self, SemiColon), ';' => symbol!(self, SemiColon),
'(' => symbol!(self, OpenParen),
')' => symbol!(self, CloseParen),
'+' => symbol!(self, Plus), '+' => symbol!(self, Plus),
'~' => symbol!(self, Tilde), '~' => symbol!(self, Tilde),
'\'' => symbol!(self, SingleQuote), '\'' => symbol!(self, SingleQuote),

View File

@ -538,6 +538,31 @@ mod test_css {
selector_pseudo_el_descendant, selector_pseudo_el_descendant,
":pseudo a {\n color: red;\n}\n" ":pseudo a {\n color: red;\n}\n"
); );
test!(
selector_pseudo_paren_comma,
":pseudo(a, b, c) {\n color: red;\n}\n"
);
test!(
selector_pseudo_paren_space,
":pseudo(a b c) {\n color: red;\n}\n"
);
test!(
selector_el_pseudo_paren_and,
"a:pseudo(a, b, c) {\n color: red;\n}\n"
);
test!(
selector_el_pseudo_paren_descendant,
"a :pseudo(a, b, c) {\n color: red;\n}\n"
);
test!(
selector_pseudo_paren_el_descendant,
":pseudo(a, b, c) a {\n color: red;\n}\n"
);
test!(
selector_pseudo_paren_el_nested,
"a {\n :pseudo(a, b, c) {\n color: red;\n }\n}\n",
"a :pseudo(a, b, c) {\n color: red;\n}\n"
);
test!(basic_style, "a {\n color: red;\n}\n"); test!(basic_style, "a {\n color: red;\n}\n");
test!(two_styles, "a {\n color: red;\n color: blue;\n}\n"); test!(two_styles, "a {\n color: red;\n color: blue;\n}\n");

View File

@ -3,6 +3,7 @@ use crate::{Token, TokenKind};
use std::fmt::{self, Display}; use std::fmt::{self, Display};
use std::iter::Peekable; use std::iter::Peekable;
use std::slice::Iter; use std::slice::Iter;
use std::string::ToString;
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub struct Selector(pub Vec<SelectorKind>); pub struct Selector(pub Vec<SelectorKind>);
@ -25,6 +26,7 @@ impl Display for Selector {
SelectorKind::Whitespace => continue, SelectorKind::Whitespace => continue,
SelectorKind::Attribute(_) SelectorKind::Attribute(_)
| SelectorKind::Pseudo(_) | SelectorKind::Pseudo(_)
| SelectorKind::PseudoParen(..)
| SelectorKind::Class | SelectorKind::Class
| SelectorKind::Id | SelectorKind::Id
| SelectorKind::Universal | SelectorKind::Universal
@ -34,6 +36,7 @@ impl Display for Selector {
match iter.peek() { match iter.peek() {
Some(SelectorKind::Attribute(_)) Some(SelectorKind::Attribute(_))
| Some(SelectorKind::Pseudo(_)) | Some(SelectorKind::Pseudo(_))
| Some(SelectorKind::PseudoParen(..))
| Some(SelectorKind::Class) | Some(SelectorKind::Class)
| Some(SelectorKind::Id) | Some(SelectorKind::Id)
| Some(SelectorKind::Universal) | Some(SelectorKind::Universal)
@ -73,6 +76,8 @@ pub enum SelectorKind {
Attribute(Attribute), Attribute(Attribute),
/// Pseudo selector: `:hover` /// Pseudo selector: `:hover`
Pseudo(String), Pseudo(String),
/// Pseudo selector with additional parens: `:any(h1, h2, h3, h4, h5, h6)`
PseudoParen(String, Vec<TokenKind>),
/// 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)
@ -94,6 +99,12 @@ 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::PseudoParen(s, toks) => write!(
f,
":{}({})",
s,
toks.iter().map(ToString::to_string).collect::<String>()
),
SelectorKind::Super | SelectorKind::None => write!(f, ""), SelectorKind::Super | SelectorKind::None => write!(f, ""),
} }
} }
@ -177,6 +188,36 @@ impl<'a> SelectorParser<'a> {
Selector(v) Selector(v)
} }
fn consume_pseudo_selector(&mut self) -> Option<SelectorKind> {
if let Some(Token {
kind: TokenKind::Ident(s),
..
}) = self.tokens.next()
{
if let Some(Token {
kind: TokenKind::Symbol(Symbol::OpenParen),
..
}) = self.tokens.peek()
{
self.tokens.next();
let mut toks = Vec::new();
while let Some(Token { kind, .. }) = self.tokens.peek() {
if kind == &TokenKind::Symbol(Symbol::CloseParen) {
break;
}
let tok = self.tokens.next().unwrap();
toks.push(tok.kind.clone());
}
self.tokens.next();
Some(SelectorKind::PseudoParen(s.clone(), toks))
} else {
Some(SelectorKind::Pseudo(s.clone()))
}
} else {
todo!("expected ident after `:` in selector")
}
}
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 { if let Some(&&Token {
@ -194,17 +235,7 @@ impl<'a> SelectorParser<'a> {
TokenKind::Ident(tok) => SelectorKind::Element(tok.clone()), TokenKind::Ident(tok) => SelectorKind::Element(tok.clone()),
TokenKind::Symbol(Symbol::Period) => SelectorKind::Class, TokenKind::Symbol(Symbol::Period) => SelectorKind::Class,
TokenKind::Symbol(Symbol::Hash) => SelectorKind::Id, TokenKind::Symbol(Symbol::Hash) => SelectorKind::Id,
TokenKind::Symbol(Symbol::Colon) => { TokenKind::Symbol(Symbol::Colon) => return self.consume_pseudo_selector(),
if let Some(Token {
kind: TokenKind::Ident(s),
..
}) = self.tokens.next()
{
SelectorKind::Pseudo(s.clone())
} else {
todo!("expected ident after `:` in selector")
}
}
TokenKind::Symbol(Symbol::Comma) => SelectorKind::Multiple, TokenKind::Symbol(Symbol::Comma) => SelectorKind::Multiple,
TokenKind::Symbol(Symbol::Gt) => SelectorKind::ImmediateChild, TokenKind::Symbol(Symbol::Gt) => SelectorKind::ImmediateChild,
TokenKind::Symbol(Symbol::Plus) => SelectorKind::Following, TokenKind::Symbol(Symbol::Plus) => SelectorKind::Following,