From 26fdcfdf1767c3c6bca9c4e6438a22e50fc5c97a Mon Sep 17 00:00:00 2001 From: ConnorSkees <39542938+ConnorSkees@users.noreply.github.com> Date: Mon, 6 Apr 2020 14:30:36 -0400 Subject: [PATCH] interpolated ampersand in at-root --- src/atrule/mod.rs | 49 +++++++++++++++++++++------------------------ src/atrule/parse.rs | 45 +++++++++++++++++++++++++++++++++++++++++ tests/at-root.rs | 25 +++++++++++++++++++++++ 3 files changed, 93 insertions(+), 26 deletions(-) diff --git a/src/atrule/mod.rs b/src/atrule/mod.rs index c4aa61d..e5c0ffa 100644 --- a/src/atrule/mod.rs +++ b/src/atrule/mod.rs @@ -15,7 +15,7 @@ pub(crate) use function::Function; pub(crate) use if_rule::If; pub(crate) use kind::AtRuleKind; pub(crate) use mixin::{eat_include, Mixin}; -use parse::eat_stmts; +use parse::{eat_stmts, eat_stmts_at_root}; use unknown::UnknownAtRule; mod for_rule; @@ -105,7 +105,7 @@ impl AtRule { AtRuleKind::Use => todo!("@use not yet implemented"), AtRuleKind::Annotation => todo!("@annotation not yet implemented"), AtRuleKind::AtRoot => { - let selector = &Selector::replace( + let mut selector = &Selector::replace( super_selector.clone(), Selector::from_tokens( &mut read_until_open_curly_brace(toks).into_iter().peekable(), @@ -113,36 +113,33 @@ impl AtRule { super_selector, )?, ); + let mut is_some = true; + if selector.is_empty() { + is_some = false; + selector = 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 - } - Stmt::RuleSet(RuleSet { - selector: mut selector2, - rules, - super_selector: super_selector2, - }) => { - if selector.is_empty() { - selector2 = Selector::replace(super_selector.clone(), selector2); - } - Some(Stmt::RuleSet(RuleSet { - selector: selector2, - rules, - super_selector: super_selector2, - })) - } - _ => Some(s), - }) - .collect::>(); + let raw_stmts = eat_stmts_at_root( + &mut body.into_iter().peekable(), + scope, + &selector, + 0, + is_some, + )? + .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, diff --git a/src/atrule/parse.rs b/src/atrule/parse.rs index af88976..5bea5c6 100644 --- a/src/atrule/parse.rs +++ b/src/atrule/parse.rs @@ -36,3 +36,48 @@ pub(crate) fn eat_stmts>( } Ok(stmts) } + +pub(crate) fn eat_stmts_at_root>( + toks: &mut Peekable, + scope: &mut Scope, + super_selector: &Selector, + mut nesting: usize, + is_some: bool, +) -> SassResult> { + let mut stmts = Vec::new(); + while let Some(expr) = eat_expr(toks, scope, super_selector)? { + match expr { + Expr::AtRule(a) => stmts.push(Stmt::AtRule(a)), + Expr::Style(s) => stmts.push(Stmt::Style(s)), + Expr::Styles(s) => stmts.extend(s.into_iter().map(Box::new).map(Stmt::Style)), + Expr::Include(s) => stmts.extend(s), + Expr::MixinDecl(..) | Expr::FunctionDecl(..) | Expr::Debug(..) | Expr::Warn(..) => { + todo!() + } + Expr::Selector(mut selector) => { + if nesting > 1 || is_some { + selector = super_selector.zip(&selector); + } else { + selector = Selector::replace(super_selector.clone(), selector); + } + nesting += 1; + let rules = eat_stmts_at_root(toks, scope, &selector, nesting, true)?; + nesting -= 1; + stmts.push(Stmt::RuleSet(RuleSet { + super_selector: if nesting > 1 { + super_selector.clone() + } else { + Selector::new() + }, + selector, + rules, + })); + } + Expr::VariableDecl(name, val) => { + scope.insert_var(&name, *val)?; + } + Expr::MultilineComment(s) => stmts.push(Stmt::MultilineComment(s)), + } + } + Ok(stmts) +} diff --git a/tests/at-root.rs b/tests/at-root.rs index 6bde249..e16d4e5 100644 --- a/tests/at-root.rs +++ b/tests/at-root.rs @@ -38,3 +38,28 @@ test!( "foo {\n @at-root {\n & {\n color: bar;\n }\n }\n}\n", "foo {\n color: bar;\n}\n" ); +test!( + interpolated_super_selector_with_nothing, + "test {\n @at-root {\n #{&}post {\n foo {\n bar: baz;\n }\n }\n }\n}\n", + "testpost foo {\n bar: baz;\n}\n" +); +test!( + with_ampersand_single, + "test {\n @at-root {\n #{&}post {\n foo {\n bar: baz;\n }\n }\n }\n}\n", + "testpost foo {\n bar: baz;\n}\n" +); +test!( + root_interpolated_ampersand, + "@at-root {\n #{&}post {\n foo {\n bar: baz;\n }\n }\n}\n", + "post foo {\n bar: baz;\n}\n" +); +test!( + nested_prefix_interpolated_ampersand, + "test {\n @at-root {\n pre#{&} {\n foo {\n bar: baz;\n }\n }\n }\n}\n", + "pretest foo {\n bar: baz;\n}\n" +); +test!( + nested_alone_interpolated_ampersand, + "test {\n @at-root {\n #{&} {\n foo {\n bar: baz;\n }\n }\n }\n}\n", + "test foo {\n bar: baz;\n}\n" +);