separate media and unknown at rules
This commit is contained in:
parent
d61f8abd41
commit
d39a45090a
@ -41,6 +41,8 @@ pub enum AtRuleKind {
|
||||
For,
|
||||
While,
|
||||
|
||||
Media,
|
||||
|
||||
// CSS @rules
|
||||
/// Defines the character set used by the style sheet
|
||||
Charset,
|
||||
@ -78,6 +80,7 @@ impl From<&str> for AtRuleKind {
|
||||
"supports" => Self::Supports,
|
||||
"keyframes" => Self::Keyframes,
|
||||
"content" => Self::Content,
|
||||
"media" => Self::Media,
|
||||
s => Self::Unknown(s.to_owned()),
|
||||
}
|
||||
}
|
||||
|
87
src/atrule/media.rs
Normal file
87
src/atrule/media.rs
Normal file
@ -0,0 +1,87 @@
|
||||
use codemap::{Span, Spanned};
|
||||
|
||||
use peekmore::PeekMoreIterator;
|
||||
|
||||
use super::parse::ruleset_eval;
|
||||
use crate::error::SassResult;
|
||||
use crate::scope::Scope;
|
||||
use crate::selector::Selector;
|
||||
use crate::utils::{devour_whitespace, parse_interpolation};
|
||||
use crate::{RuleSet, Stmt, Token};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct Media {
|
||||
pub super_selector: Selector,
|
||||
pub params: String,
|
||||
pub body: Vec<Spanned<Stmt>>,
|
||||
}
|
||||
|
||||
impl Media {
|
||||
pub fn from_tokens<I: Iterator<Item = Token>>(
|
||||
toks: &mut PeekMoreIterator<I>,
|
||||
scope: &mut Scope,
|
||||
super_selector: &Selector,
|
||||
kind_span: Span,
|
||||
content: Option<&[Spanned<Stmt>]>,
|
||||
) -> SassResult<Media> {
|
||||
let mut params = String::new();
|
||||
while let Some(tok) = toks.next() {
|
||||
match tok.kind {
|
||||
'{' => break,
|
||||
'#' => {
|
||||
if toks.peek().unwrap().kind == '{' {
|
||||
toks.next();
|
||||
let interpolation = parse_interpolation(toks, scope, super_selector)?;
|
||||
params.push_str(&interpolation.node.to_css_string(interpolation.span)?);
|
||||
continue;
|
||||
} else {
|
||||
params.push(tok.kind);
|
||||
}
|
||||
}
|
||||
'\n' | ' ' | '\t' => {
|
||||
devour_whitespace(toks);
|
||||
params.push(' ');
|
||||
continue;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
params.push(tok.kind);
|
||||
}
|
||||
|
||||
if params.is_empty() {
|
||||
return Err(("Expected identifier.", kind_span).into());
|
||||
}
|
||||
|
||||
let mut raw_body = Vec::new();
|
||||
ruleset_eval(toks, scope, super_selector, false, content, &mut raw_body)?;
|
||||
let mut rules = Vec::with_capacity(raw_body.len());
|
||||
let mut body = Vec::new();
|
||||
|
||||
for stmt in raw_body {
|
||||
match stmt.node {
|
||||
Stmt::Style(..) => body.push(stmt),
|
||||
_ => rules.push(stmt),
|
||||
}
|
||||
}
|
||||
|
||||
if super_selector.is_empty() {
|
||||
body.append(&mut rules);
|
||||
} else {
|
||||
body = vec![Spanned {
|
||||
node: Stmt::RuleSet(RuleSet {
|
||||
selector: super_selector.clone(),
|
||||
rules: body,
|
||||
super_selector: Selector::new(),
|
||||
}),
|
||||
span: kind_span,
|
||||
}];
|
||||
body.append(&mut rules);
|
||||
}
|
||||
|
||||
Ok(Media {
|
||||
super_selector: Selector::new(),
|
||||
params: params.trim().to_owned(),
|
||||
body,
|
||||
})
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@ use for_rule::For;
|
||||
pub(crate) use function::Function;
|
||||
pub(crate) use if_rule::If;
|
||||
pub(crate) use kind::AtRuleKind;
|
||||
use media::Media;
|
||||
pub(crate) use mixin::{eat_include, Mixin};
|
||||
use parse::{eat_stmts, eat_stmts_at_root, ruleset_eval};
|
||||
use unknown::UnknownAtRule;
|
||||
@ -27,6 +28,7 @@ mod for_rule;
|
||||
mod function;
|
||||
mod if_rule;
|
||||
mod kind;
|
||||
mod media;
|
||||
mod mixin;
|
||||
mod parse;
|
||||
mod unknown;
|
||||
@ -47,12 +49,13 @@ pub(crate) enum AtRule {
|
||||
While(While),
|
||||
Include(Vec<Spanned<Stmt>>),
|
||||
If(If),
|
||||
Media(Media),
|
||||
AtRoot(Vec<Spanned<Stmt>>),
|
||||
}
|
||||
|
||||
impl AtRule {
|
||||
pub fn from_tokens<I: Iterator<Item = Token>>(
|
||||
rule: &AtRuleKind,
|
||||
rule: AtRuleKind,
|
||||
kind_span: Span,
|
||||
toks: &mut PeekMoreIterator<I>,
|
||||
scope: &mut Scope,
|
||||
@ -246,6 +249,16 @@ impl AtRule {
|
||||
)?),
|
||||
span: kind_span,
|
||||
},
|
||||
AtRuleKind::Media => Spanned {
|
||||
node: AtRule::Media(Media::from_tokens(
|
||||
toks,
|
||||
scope,
|
||||
super_selector,
|
||||
kind_span,
|
||||
content,
|
||||
)?),
|
||||
span: kind_span,
|
||||
},
|
||||
AtRuleKind::Import => todo!("@import not yet implemented"),
|
||||
AtRuleKind::Forward => todo!("@forward not yet implemented"),
|
||||
AtRuleKind::Supports => todo!("@supports not yet implemented"),
|
||||
|
@ -20,13 +20,23 @@ pub(crate) struct UnknownAtRule {
|
||||
impl UnknownAtRule {
|
||||
pub fn from_tokens<I: Iterator<Item = Token>>(
|
||||
toks: &mut PeekMoreIterator<I>,
|
||||
name: &str,
|
||||
name: String,
|
||||
scope: &mut Scope,
|
||||
super_selector: &Selector,
|
||||
kind_span: Span,
|
||||
content: Option<&[Spanned<Stmt>]>,
|
||||
) -> SassResult<UnknownAtRule> {
|
||||
let mut params = String::new();
|
||||
devour_whitespace(toks);
|
||||
if let Some(Token { kind: ';', .. }) | None = toks.peek() {
|
||||
toks.next();
|
||||
return Ok(UnknownAtRule {
|
||||
name,
|
||||
super_selector: Selector::new(),
|
||||
params: String::new(),
|
||||
body: Vec::new(),
|
||||
});
|
||||
}
|
||||
while let Some(tok) = toks.next() {
|
||||
match tok.kind {
|
||||
'{' => break,
|
||||
@ -77,7 +87,7 @@ impl UnknownAtRule {
|
||||
}
|
||||
|
||||
Ok(UnknownAtRule {
|
||||
name: name.to_owned(),
|
||||
name,
|
||||
super_selector: Selector::new(),
|
||||
params: params.trim().to_owned(),
|
||||
body,
|
||||
|
@ -341,7 +341,7 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
|
||||
let Spanned { node: ident, span } = eat_ident(toks, scope, super_selector, span)?;
|
||||
devour_whitespace(toks);
|
||||
let rule = AtRule::from_tokens(
|
||||
&AtRuleKind::from(ident.as_str()),
|
||||
AtRuleKind::from(ident.as_str()),
|
||||
span,
|
||||
toks,
|
||||
scope,
|
||||
@ -364,6 +364,7 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
|
||||
u @ AtRule::Unknown(..) => Expr::AtRule(u),
|
||||
u @ AtRule::AtRoot(..) => Expr::AtRule(u),
|
||||
u @ AtRule::Include(..) => Expr::AtRule(u),
|
||||
u @ AtRule::Media(..) => Expr::AtRule(u),
|
||||
},
|
||||
span,
|
||||
}));
|
||||
|
@ -185,15 +185,29 @@ impl Css {
|
||||
Toplevel::AtRule(r) => {
|
||||
match r {
|
||||
AtRule::Unknown(u) => {
|
||||
if u.params.is_empty() {
|
||||
write!(buf, "{}@{}", padding, u.name)?;
|
||||
} else {
|
||||
write!(buf, "{}@{} {}", padding, u.name, u.params)?;
|
||||
}
|
||||
|
||||
if u.body.is_empty() {
|
||||
writeln!(buf, ";")?;
|
||||
continue;
|
||||
} else {
|
||||
writeln!(buf, " {{")?;
|
||||
}
|
||||
|
||||
Css::from_stylesheet(StyleSheet::from_stmts(u.body))?
|
||||
._inner_pretty_print(buf, map, nesting + 1)?;
|
||||
writeln!(buf, "{}}}", padding)?;
|
||||
}
|
||||
AtRule::Media(m) => {
|
||||
if m.body.is_empty() {
|
||||
continue;
|
||||
}
|
||||
if u.params.is_empty() {
|
||||
writeln!(buf, "{}@{} {{", padding, u.name)?;
|
||||
} else {
|
||||
writeln!(buf, "{}@{} {} {{", padding, u.name, u.params)?;
|
||||
}
|
||||
Css::from_stylesheet(StyleSheet::from_stmts(u.body))?
|
||||
writeln!(buf, "{}@media {} {{", padding, m.params)?;
|
||||
Css::from_stylesheet(StyleSheet::from_stmts(m.body))?
|
||||
._inner_pretty_print(buf, map, nesting + 1)?;
|
||||
writeln!(buf, "{}}}", padding)?;
|
||||
}
|
||||
|
@ -251,7 +251,7 @@ impl<'a> StyleSheetParser<'a> {
|
||||
});
|
||||
}
|
||||
v => {
|
||||
let rule = AtRule::from_tokens(&v, span, &mut self.lexer, &mut Scope::new(), &Selector::new(), None)?;
|
||||
let rule = AtRule::from_tokens(v, span, &mut self.lexer, &mut Scope::new(), &Selector::new(), None)?;
|
||||
match rule.node {
|
||||
AtRule::Mixin(name, mixin) => {
|
||||
insert_global_mixin(&name, *mixin);
|
||||
@ -281,6 +281,7 @@ impl<'a> StyleSheetParser<'a> {
|
||||
}
|
||||
AtRule::AtRoot(root_rules) => rules.extend(root_rules),
|
||||
AtRule::Unknown(..) => rules.push(rule.map_node(Stmt::AtRule)),
|
||||
AtRule::Media(..) => rules.push(rule.map_node(Stmt::AtRule)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -339,6 +340,10 @@ impl<'a> StyleSheetParser<'a> {
|
||||
node: Stmt::AtRule(r),
|
||||
span,
|
||||
}),
|
||||
r @ AtRule::Media(..) => stmts.push(Spanned {
|
||||
node: Stmt::AtRule(r),
|
||||
span,
|
||||
}),
|
||||
},
|
||||
Expr::Styles(s) => stmts.extend(
|
||||
s.into_iter()
|
||||
|
@ -7,18 +7,13 @@ test!(
|
||||
basic_toplevel,
|
||||
"@media foo {\n a {\n color: red;\n }\n}\n"
|
||||
);
|
||||
test!(
|
||||
toplevel_no_params,
|
||||
"@media {\n a {\n color: red;\n }\n}\n"
|
||||
error!(
|
||||
no_params,
|
||||
"@media {\n a {\n color: red;\n }\n}\n", "Error: Expected identifier."
|
||||
);
|
||||
test!(
|
||||
basic_nested,
|
||||
"a {\n @media foo {\n color: red;\n }\n}\n",
|
||||
"@media foo {\n a {\n color: red;\n }\n}\n"
|
||||
);
|
||||
test!(
|
||||
basic_unknown_at_rule,
|
||||
"@foo {\n a {\n color: red;\n }\n}\n"
|
||||
);
|
||||
test!(unknown_at_rule_no_selector, "@foo {\n color: red;\n}\n");
|
||||
test!(empty, "@media (min-width: 2px) {}", "");
|
||||
test!(empty_body, "@media (min-width: 2px) {}", "");
|
||||
|
17
tests/unknown-at-rule.rs
Normal file
17
tests/unknown-at-rule.rs
Normal file
@ -0,0 +1,17 @@
|
||||
#![cfg(test)]
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
test!(
|
||||
basic_unknown_at_rule,
|
||||
"@foo {\n a {\n color: red;\n }\n}\n"
|
||||
);
|
||||
test!(unknown_at_rule_no_selector, "@foo {\n color: red;\n}\n");
|
||||
test!(unknown_at_rule_no_body, "@foo;\n");
|
||||
test!(unknown_at_rule_no_body_eof, "@foo", "@foo;\n");
|
||||
test!(
|
||||
unknown_at_rule_interpolated_eof_no_body,
|
||||
"@#{()if(0,0<0,0)}",
|
||||
"@false;\n"
|
||||
);
|
Loading…
x
Reference in New Issue
Block a user