From ee44982198d1f95185b03d8ea12788b332025c74 Mon Sep 17 00:00:00 2001 From: ConnorSkees <39542938+ConnorSkees@users.noreply.github.com> Date: Sun, 2 Feb 2020 15:06:46 -0500 Subject: [PATCH] Handle pseudo elements like `::before` --- src/selector.rs | 59 ++++++++++++++++++++++++++++------------------ tests/selectors.rs | 13 ++++++++++ 2 files changed, 49 insertions(+), 23 deletions(-) diff --git a/src/selector.rs b/src/selector.rs index 215913c..6d531cd 100644 --- a/src/selector.rs +++ b/src/selector.rs @@ -20,6 +20,7 @@ impl Display for Selector { SelectorKind::Whitespace => continue, SelectorKind::Attribute(_) | SelectorKind::Pseudo(_) + | SelectorKind::PseudoElement(_) | SelectorKind::PseudoParen(..) | SelectorKind::Class | SelectorKind::Id @@ -31,6 +32,7 @@ impl Display for Selector { match iter.peek() { Some(SelectorKind::Attribute(_)) | Some(SelectorKind::Pseudo(_)) + | Some(SelectorKind::PseudoElement(_)) | Some(SelectorKind::PseudoParen(..)) | Some(SelectorKind::Class) | Some(SelectorKind::Id) @@ -83,6 +85,8 @@ pub(crate) enum SelectorKind { Attribute(Attribute), /// Pseudo selector: `:hover` Pseudo(String), + /// Pseudo element selector: `::before` + PseudoElement(String), /// Pseudo selector with additional parens: `:any(h1, h2, h3, h4, h5, h6)` PseudoParen(String, Vec), /// Use the super selector: `&.red` @@ -122,6 +126,7 @@ impl Display for SelectorKind { SelectorKind::Preceding => write!(f, " ~ "), SelectorKind::Attribute(attr) => write!(f, "{}", attr), SelectorKind::Pseudo(s) => write!(f, ":{}", s), + SelectorKind::PseudoElement(s) => write!(f, "::{}", s), SelectorKind::PseudoParen(s, toks) => write!( f, ":{}({})", @@ -212,32 +217,40 @@ impl<'a> SelectorParser<'a> { } fn consume_pseudo_selector(&mut self, tokens: &'_ mut Peekable>) { - if let Some(Token { - kind: TokenKind::Ident(s), - .. - }) = tokens.next() - { - if let Some(Token { - kind: TokenKind::Symbol(Symbol::OpenParen), - .. - }) = tokens.peek() - { - tokens.next(); - let mut toks = Vec::new(); - while let Some(Token { kind, .. }) = tokens.peek() { - if kind == &TokenKind::Symbol(Symbol::CloseParen) { - break; + if let Some(tok) = tokens.next() { + match tok.kind { + TokenKind::Ident(s) => { + if let Some(Token { + kind: TokenKind::Symbol(Symbol::OpenParen), + .. + }) = tokens.peek() + { + tokens.next(); + let mut toks = Vec::new(); + while let Some(Token { kind, .. }) = tokens.peek() { + if kind == &TokenKind::Symbol(Symbol::CloseParen) { + break; + } + let tok = tokens.next().unwrap(); + toks.push(tok.kind); + } + tokens.next(); + self.selectors.push(SelectorKind::PseudoParen(s, toks)) + } else { + self.selectors.push(SelectorKind::Pseudo(s)) } - let tok = tokens.next().unwrap(); - toks.push(tok.kind); } - tokens.next(); - self.selectors.push(SelectorKind::PseudoParen(s, toks)) - } else { - self.selectors.push(SelectorKind::Pseudo(s)) + TokenKind::Symbol(Symbol::Colon) => { + if let Some(Token { + kind: TokenKind::Ident(s), + .. + }) = tokens.next() + { + self.selectors.push(SelectorKind::PseudoElement(s)) + } + } + _ => todo!("expected ident or `:` after `:` in selector"), } - } else { - todo!("expected ident after `:` in selector") } } diff --git a/tests/selectors.rs b/tests/selectors.rs index e174111..3df4451 100644 --- a/tests/selectors.rs +++ b/tests/selectors.rs @@ -106,6 +106,19 @@ test!( selector_pseudo_el_descendant, ":pseudo a {\n color: red;\n}\n" ); +test!(selector_pseudoelement, "::before {\n color: red;\n}\n"); +test!( + selector_el_and_pseudoelement, + "a::before {\n color: red;\n}\n" +); +test!( + selector_el_pseudoelement_descendant, + "a ::before {\n color: red;\n}\n" +); +test!( + selector_pseudoelement_el_descendant, + "::before a {\n color: red;\n}\n" +); test!( selector_pseudo_paren_comma, ":pseudo(a, b, c) {\n color: red;\n}\n"