separate media and unknown at rules
This commit is contained in:
parent
d61f8abd41
commit
d39a45090a
@ -41,6 +41,8 @@ pub enum AtRuleKind {
|
|||||||
For,
|
For,
|
||||||
While,
|
While,
|
||||||
|
|
||||||
|
Media,
|
||||||
|
|
||||||
// CSS @rules
|
// CSS @rules
|
||||||
/// Defines the character set used by the style sheet
|
/// Defines the character set used by the style sheet
|
||||||
Charset,
|
Charset,
|
||||||
@ -78,6 +80,7 @@ impl From<&str> for AtRuleKind {
|
|||||||
"supports" => Self::Supports,
|
"supports" => Self::Supports,
|
||||||
"keyframes" => Self::Keyframes,
|
"keyframes" => Self::Keyframes,
|
||||||
"content" => Self::Content,
|
"content" => Self::Content,
|
||||||
|
"media" => Self::Media,
|
||||||
s => Self::Unknown(s.to_owned()),
|
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 function::Function;
|
||||||
pub(crate) use if_rule::If;
|
pub(crate) use if_rule::If;
|
||||||
pub(crate) use kind::AtRuleKind;
|
pub(crate) use kind::AtRuleKind;
|
||||||
|
use media::Media;
|
||||||
pub(crate) use mixin::{eat_include, Mixin};
|
pub(crate) use mixin::{eat_include, Mixin};
|
||||||
use parse::{eat_stmts, eat_stmts_at_root, ruleset_eval};
|
use parse::{eat_stmts, eat_stmts_at_root, ruleset_eval};
|
||||||
use unknown::UnknownAtRule;
|
use unknown::UnknownAtRule;
|
||||||
@ -27,6 +28,7 @@ mod for_rule;
|
|||||||
mod function;
|
mod function;
|
||||||
mod if_rule;
|
mod if_rule;
|
||||||
mod kind;
|
mod kind;
|
||||||
|
mod media;
|
||||||
mod mixin;
|
mod mixin;
|
||||||
mod parse;
|
mod parse;
|
||||||
mod unknown;
|
mod unknown;
|
||||||
@ -47,12 +49,13 @@ pub(crate) enum AtRule {
|
|||||||
While(While),
|
While(While),
|
||||||
Include(Vec<Spanned<Stmt>>),
|
Include(Vec<Spanned<Stmt>>),
|
||||||
If(If),
|
If(If),
|
||||||
|
Media(Media),
|
||||||
AtRoot(Vec<Spanned<Stmt>>),
|
AtRoot(Vec<Spanned<Stmt>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AtRule {
|
impl AtRule {
|
||||||
pub fn from_tokens<I: Iterator<Item = Token>>(
|
pub fn from_tokens<I: Iterator<Item = Token>>(
|
||||||
rule: &AtRuleKind,
|
rule: AtRuleKind,
|
||||||
kind_span: Span,
|
kind_span: Span,
|
||||||
toks: &mut PeekMoreIterator<I>,
|
toks: &mut PeekMoreIterator<I>,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -246,6 +249,16 @@ impl AtRule {
|
|||||||
)?),
|
)?),
|
||||||
span: kind_span,
|
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::Import => todo!("@import not yet implemented"),
|
||||||
AtRuleKind::Forward => todo!("@forward not yet implemented"),
|
AtRuleKind::Forward => todo!("@forward not yet implemented"),
|
||||||
AtRuleKind::Supports => todo!("@supports not yet implemented"),
|
AtRuleKind::Supports => todo!("@supports not yet implemented"),
|
||||||
|
@ -20,13 +20,23 @@ pub(crate) struct UnknownAtRule {
|
|||||||
impl UnknownAtRule {
|
impl UnknownAtRule {
|
||||||
pub fn from_tokens<I: Iterator<Item = Token>>(
|
pub fn from_tokens<I: Iterator<Item = Token>>(
|
||||||
toks: &mut PeekMoreIterator<I>,
|
toks: &mut PeekMoreIterator<I>,
|
||||||
name: &str,
|
name: String,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
super_selector: &Selector,
|
super_selector: &Selector,
|
||||||
kind_span: Span,
|
kind_span: Span,
|
||||||
content: Option<&[Spanned<Stmt>]>,
|
content: Option<&[Spanned<Stmt>]>,
|
||||||
) -> SassResult<UnknownAtRule> {
|
) -> SassResult<UnknownAtRule> {
|
||||||
let mut params = String::new();
|
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() {
|
while let Some(tok) = toks.next() {
|
||||||
match tok.kind {
|
match tok.kind {
|
||||||
'{' => break,
|
'{' => break,
|
||||||
@ -77,7 +87,7 @@ impl UnknownAtRule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(UnknownAtRule {
|
Ok(UnknownAtRule {
|
||||||
name: name.to_owned(),
|
name,
|
||||||
super_selector: Selector::new(),
|
super_selector: Selector::new(),
|
||||||
params: params.trim().to_owned(),
|
params: params.trim().to_owned(),
|
||||||
body,
|
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)?;
|
let Spanned { node: ident, span } = eat_ident(toks, scope, super_selector, span)?;
|
||||||
devour_whitespace(toks);
|
devour_whitespace(toks);
|
||||||
let rule = AtRule::from_tokens(
|
let rule = AtRule::from_tokens(
|
||||||
&AtRuleKind::from(ident.as_str()),
|
AtRuleKind::from(ident.as_str()),
|
||||||
span,
|
span,
|
||||||
toks,
|
toks,
|
||||||
scope,
|
scope,
|
||||||
@ -364,6 +364,7 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
|
|||||||
u @ AtRule::Unknown(..) => Expr::AtRule(u),
|
u @ AtRule::Unknown(..) => Expr::AtRule(u),
|
||||||
u @ AtRule::AtRoot(..) => Expr::AtRule(u),
|
u @ AtRule::AtRoot(..) => Expr::AtRule(u),
|
||||||
u @ AtRule::Include(..) => Expr::AtRule(u),
|
u @ AtRule::Include(..) => Expr::AtRule(u),
|
||||||
|
u @ AtRule::Media(..) => Expr::AtRule(u),
|
||||||
},
|
},
|
||||||
span,
|
span,
|
||||||
}));
|
}));
|
||||||
|
@ -185,15 +185,29 @@ impl Css {
|
|||||||
Toplevel::AtRule(r) => {
|
Toplevel::AtRule(r) => {
|
||||||
match r {
|
match r {
|
||||||
AtRule::Unknown(u) => {
|
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() {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
if u.params.is_empty() {
|
writeln!(buf, "{}@media {} {{", padding, m.params)?;
|
||||||
writeln!(buf, "{}@{} {{", padding, u.name)?;
|
Css::from_stylesheet(StyleSheet::from_stmts(m.body))?
|
||||||
} else {
|
|
||||||
writeln!(buf, "{}@{} {} {{", padding, u.name, u.params)?;
|
|
||||||
}
|
|
||||||
Css::from_stylesheet(StyleSheet::from_stmts(u.body))?
|
|
||||||
._inner_pretty_print(buf, map, nesting + 1)?;
|
._inner_pretty_print(buf, map, nesting + 1)?;
|
||||||
writeln!(buf, "{}}}", padding)?;
|
writeln!(buf, "{}}}", padding)?;
|
||||||
}
|
}
|
||||||
|
@ -251,7 +251,7 @@ impl<'a> StyleSheetParser<'a> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
v => {
|
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 {
|
match rule.node {
|
||||||
AtRule::Mixin(name, mixin) => {
|
AtRule::Mixin(name, mixin) => {
|
||||||
insert_global_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::AtRoot(root_rules) => rules.extend(root_rules),
|
||||||
AtRule::Unknown(..) => rules.push(rule.map_node(Stmt::AtRule)),
|
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),
|
node: Stmt::AtRule(r),
|
||||||
span,
|
span,
|
||||||
}),
|
}),
|
||||||
|
r @ AtRule::Media(..) => stmts.push(Spanned {
|
||||||
|
node: Stmt::AtRule(r),
|
||||||
|
span,
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
Expr::Styles(s) => stmts.extend(
|
Expr::Styles(s) => stmts.extend(
|
||||||
s.into_iter()
|
s.into_iter()
|
||||||
|
@ -7,18 +7,13 @@ test!(
|
|||||||
basic_toplevel,
|
basic_toplevel,
|
||||||
"@media foo {\n a {\n color: red;\n }\n}\n"
|
"@media foo {\n a {\n color: red;\n }\n}\n"
|
||||||
);
|
);
|
||||||
test!(
|
error!(
|
||||||
toplevel_no_params,
|
no_params,
|
||||||
"@media {\n a {\n color: red;\n }\n}\n"
|
"@media {\n a {\n color: red;\n }\n}\n", "Error: Expected identifier."
|
||||||
);
|
);
|
||||||
test!(
|
test!(
|
||||||
basic_nested,
|
basic_nested,
|
||||||
"a {\n @media foo {\n color: red;\n }\n}\n",
|
"a {\n @media foo {\n color: red;\n }\n}\n",
|
||||||
"@media foo {\n a {\n color: red;\n }\n}\n"
|
"@media foo {\n a {\n color: red;\n }\n}\n"
|
||||||
);
|
);
|
||||||
test!(
|
test!(empty_body, "@media (min-width: 2px) {}", "");
|
||||||
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) {}", "");
|
|
||||||
|
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