From 648dc04c33bc463238495a5ace55dc8ed5f55c74 Mon Sep 17 00:00:00 2001 From: ConnorSkees <39542938+ConnorSkees@users.noreply.github.com> Date: Mon, 6 Apr 2020 13:13:03 -0400 Subject: [PATCH] initial implementation of @ at-root --- src/atrule/mod.rs | 37 +++++++++++++++++++++++++++++++++++-- src/lib.rs | 5 ++++- src/output.rs | 4 ++++ src/selector/mod.rs | 7 ++----- tests/at-root.rs | 5 +++++ 5 files changed, 50 insertions(+), 8 deletions(-) diff --git a/src/atrule/mod.rs b/src/atrule/mod.rs index 6323469..d14fdc0 100644 --- a/src/atrule/mod.rs +++ b/src/atrule/mod.rs @@ -9,7 +9,7 @@ use crate::utils::{ read_until_semicolon_or_closing_curly_brace, }; use crate::value::Value; -use crate::{Stmt, Token}; +use crate::{RuleSet, Stmt, Token}; pub(crate) use function::Function; pub(crate) use if_rule::If; @@ -40,6 +40,7 @@ pub(crate) enum AtRule { Each(Vec), While(Vec), If(If), + AtRoot(Vec), } impl AtRule { @@ -103,7 +104,39 @@ impl AtRule { } AtRuleKind::Use => todo!("@use not yet implemented"), AtRuleKind::Annotation => todo!("@annotation not yet implemented"), - AtRuleKind::AtRoot => todo!("@at-root not yet implemented"), + AtRuleKind::AtRoot => { + let selector = &Selector::replace( + super_selector.clone(), + Selector::from_tokens( + &mut read_until_open_curly_brace(toks).into_iter().peekable(), + scope, + super_selector, + )?, + ); + toks.next(); + devour_whitespace(toks); + let mut body = read_until_closing_curly_brace(toks); + body.push(toks.next().unwrap()); + devour_whitespace(toks); + let mut styles = Vec::new(); + let raw_stmts = eat_stmts(&mut body.into_iter().peekable(), scope, &selector)? + .into_iter() + .filter_map(|s| match s { + Stmt::Style(..) => { + styles.push(s); + None + } + _ => Some(s), + }) + .collect::>(); + let mut stmts = vec![Stmt::RuleSet(RuleSet { + selector: selector.clone(), + rules: styles, + super_selector: Selector::new(), + })]; + stmts.extend(raw_stmts); + AtRule::AtRoot(stmts) + } AtRuleKind::Charset => { read_until_semicolon_or_closing_curly_brace(toks); if toks.peek().unwrap().kind == ';' { diff --git a/src/lib.rs b/src/lib.rs index d061401..cee1028 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -382,6 +382,7 @@ impl<'a> StyleSheetParser<'a> { AtRule::If(i) => { rules.extend(i.eval(&mut Scope::new(), &Selector::new())?); } + AtRule::AtRoot(root_rules) => rules.extend(root_rules), u @ AtRule::Unknown(..) => rules.push(Stmt::AtRule(u)), } } @@ -416,6 +417,7 @@ impl<'a> StyleSheetParser<'a> { return Err("@content is only allowed within mixin declarations.".into()) } AtRule::Return(..) => return Err("This at-rule is not allowed here.".into()), + AtRule::AtRoot(root_stmts) => stmts.extend(root_stmts), r => stmts.push(Stmt::AtRule(r)), }, Expr::Styles(s) => stmts.extend(s.into_iter().map(Box::new).map(Stmt::Style)), @@ -463,7 +465,7 @@ pub(crate) fn eat_expr>( ) -> SassResult> { let mut values = Vec::with_capacity(5); while let Some(tok) = toks.peek() { - match &tok.kind { + match tok.kind { ':' => { let tok = toks.next(); if devour_whitespace(toks) { @@ -600,6 +602,7 @@ pub(crate) fn eat_expr>( f @ AtRule::While(..) => Ok(Some(Expr::AtRule(f))), f @ AtRule::Each(..) => Ok(Some(Expr::AtRule(f))), u @ AtRule::Unknown(..) => Ok(Some(Expr::AtRule(u))), + u @ AtRule::AtRoot(..) => Ok(Some(Expr::AtRule(u))), }; } } diff --git a/src/output.rs b/src/output.rs index 997052b..76912a4 100644 --- a/src/output.rs +++ b/src/output.rs @@ -89,6 +89,10 @@ impl Css { .get_mut(0) .expect("expected block to exist") .push_comment(s), + Stmt::AtRule(AtRule::AtRoot(stmts)) => stmts + .into_iter() + .map(|r| Ok(vals.extend(self.parse_stmt(r)?))) + .collect::>()?, Stmt::AtRule(r) => vals.push(Toplevel::AtRule(r)), }; } diff --git a/src/selector/mod.rs b/src/selector/mod.rs index 7773951..4c67e59 100644 --- a/src/selector/mod.rs +++ b/src/selector/mod.rs @@ -384,13 +384,10 @@ impl Selector { } } - fn replace(super_selector: Selector, this: Selector) -> Selector { - if super_selector.0.is_empty() { + pub fn replace(super_selector: Selector, this: Selector) -> Selector { + if super_selector.0.is_empty() || this.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; diff --git a/tests/at-root.rs b/tests/at-root.rs index 6d49d3d..3aeb444 100644 --- a/tests/at-root.rs +++ b/tests/at-root.rs @@ -28,3 +28,8 @@ test!( ".foo {\n @at-root & {\n .bar {\n @at-root & {\n a: b;\n }\n }\n }\n}\n", ".foo .bar {\n a: b;\n}\n" ); +test!( + deeply_nested_with_rulesets_and_styles, + ".foo {\n @at-root .bar {\n a: b;\n c {\n d: e;\n foo {\n bar: baz;\n }\n h: j;\n }\n f: g;\n }\n}\n", + ".bar {\n a: b;\n f: g;\n}\n.bar c {\n d: e;\n h: j;\n}\n.bar c foo {\n bar: baz;\n}\n" +);