initial implementation of @ at-root

This commit is contained in:
ConnorSkees 2020-04-06 13:13:03 -04:00
parent 526ed4fde0
commit 648dc04c33
5 changed files with 50 additions and 8 deletions

View File

@ -9,7 +9,7 @@ use crate::utils::{
read_until_semicolon_or_closing_curly_brace, read_until_semicolon_or_closing_curly_brace,
}; };
use crate::value::Value; use crate::value::Value;
use crate::{Stmt, Token}; use crate::{RuleSet, Stmt, Token};
pub(crate) use function::Function; pub(crate) use function::Function;
pub(crate) use if_rule::If; pub(crate) use if_rule::If;
@ -40,6 +40,7 @@ pub(crate) enum AtRule {
Each(Vec<Stmt>), Each(Vec<Stmt>),
While(Vec<Stmt>), While(Vec<Stmt>),
If(If), If(If),
AtRoot(Vec<Stmt>),
} }
impl AtRule { impl AtRule {
@ -103,7 +104,39 @@ impl AtRule {
} }
AtRuleKind::Use => todo!("@use not yet implemented"), AtRuleKind::Use => todo!("@use not yet implemented"),
AtRuleKind::Annotation => todo!("@annotation 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::<Vec<Stmt>>();
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 => { AtRuleKind::Charset => {
read_until_semicolon_or_closing_curly_brace(toks); read_until_semicolon_or_closing_curly_brace(toks);
if toks.peek().unwrap().kind == ';' { if toks.peek().unwrap().kind == ';' {

View File

@ -382,6 +382,7 @@ impl<'a> StyleSheetParser<'a> {
AtRule::If(i) => { AtRule::If(i) => {
rules.extend(i.eval(&mut Scope::new(), &Selector::new())?); 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)), 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()) return Err("@content is only allowed within mixin declarations.".into())
} }
AtRule::Return(..) => return Err("This at-rule is not allowed here.".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)), r => stmts.push(Stmt::AtRule(r)),
}, },
Expr::Styles(s) => stmts.extend(s.into_iter().map(Box::new).map(Stmt::Style)), Expr::Styles(s) => stmts.extend(s.into_iter().map(Box::new).map(Stmt::Style)),
@ -463,7 +465,7 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
) -> SassResult<Option<Expr>> { ) -> SassResult<Option<Expr>> {
let mut values = Vec::with_capacity(5); let mut values = Vec::with_capacity(5);
while let Some(tok) = toks.peek() { while let Some(tok) = toks.peek() {
match &tok.kind { match tok.kind {
':' => { ':' => {
let tok = toks.next(); let tok = toks.next();
if devour_whitespace(toks) { if devour_whitespace(toks) {
@ -600,6 +602,7 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
f @ AtRule::While(..) => Ok(Some(Expr::AtRule(f))), f @ AtRule::While(..) => Ok(Some(Expr::AtRule(f))),
f @ AtRule::Each(..) => Ok(Some(Expr::AtRule(f))), f @ AtRule::Each(..) => Ok(Some(Expr::AtRule(f))),
u @ AtRule::Unknown(..) => Ok(Some(Expr::AtRule(u))), u @ AtRule::Unknown(..) => Ok(Some(Expr::AtRule(u))),
u @ AtRule::AtRoot(..) => Ok(Some(Expr::AtRule(u))),
}; };
} }
} }

View File

@ -89,6 +89,10 @@ impl Css {
.get_mut(0) .get_mut(0)
.expect("expected block to exist") .expect("expected block to exist")
.push_comment(s), .push_comment(s),
Stmt::AtRule(AtRule::AtRoot(stmts)) => stmts
.into_iter()
.map(|r| Ok(vals.extend(self.parse_stmt(r)?)))
.collect::<SassResult<()>>()?,
Stmt::AtRule(r) => vals.push(Toplevel::AtRule(r)), Stmt::AtRule(r) => vals.push(Toplevel::AtRule(r)),
}; };
} }

View File

@ -384,13 +384,10 @@ impl Selector {
} }
} }
fn replace(super_selector: Selector, this: Selector) -> Selector { pub fn replace(super_selector: Selector, this: Selector) -> Selector {
if super_selector.0.is_empty() { if super_selector.0.is_empty() || this.0.is_empty() {
return this; return this;
} }
if this.0.is_empty() {
return super_selector;
}
let mut parts = Vec::with_capacity(super_selector.0.len()); let mut parts = Vec::with_capacity(super_selector.0.len());
for (idx, part) in super_selector.clone().0.into_iter().enumerate() { for (idx, part) in super_selector.clone().0.into_iter().enumerate() {
let mut found_inner = false; let mut found_inner = false;

View File

@ -28,3 +28,8 @@ test!(
".foo {\n @at-root & {\n .bar {\n @at-root & {\n a: b;\n }\n }\n }\n}\n", ".foo {\n @at-root & {\n .bar {\n @at-root & {\n a: b;\n }\n }\n }\n}\n",
".foo .bar {\n a: b;\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"
);