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,
};
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<Stmt>),
While(Vec<Stmt>),
If(If),
AtRoot(Vec<Stmt>),
}
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::<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 => {
read_until_semicolon_or_closing_curly_brace(toks);
if toks.peek().unwrap().kind == ';' {

View File

@ -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<I: Iterator<Item = Token>>(
) -> SassResult<Option<Expr>> {
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<I: Iterator<Item = Token>>(
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))),
};
}
}

View File

@ -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::<SassResult<()>>()?,
Stmt::AtRule(r) => vals.push(Toplevel::AtRule(r)),
};
}

View File

@ -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;

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 .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"
);