MVP implementation of @supports

This commit is contained in:
ConnorSkees 2020-06-20 15:52:53 -04:00
parent c9a68c55b9
commit 207aa1bf38
2 changed files with 118 additions and 28 deletions

View File

@ -18,6 +18,10 @@ enum Toplevel {
params: String,
body: Vec<Stmt>,
},
Supports {
params: String,
body: Vec<Stmt>,
},
Newline,
Style(Box<Style>),
}
@ -105,6 +109,9 @@ impl Css {
Stmt::Media { params, body, .. } => {
vals.push(Toplevel::Media { params, body })
}
Stmt::Supports { params, body, .. } => {
vals.push(Toplevel::Supports { params, body })
}
Stmt::UnknownAtRule {
params, body, name, ..
} => vals.push(Toplevel::UnknownAtRule { params, body, name }),
@ -120,6 +127,7 @@ impl Css {
Stmt::Comment(s) => vec![Toplevel::MultilineComment(s)],
Stmt::Style(s) => vec![Toplevel::Style(s)],
Stmt::Media { params, body, .. } => vec![Toplevel::Media { params, body }],
Stmt::Supports { params, body, .. } => vec![Toplevel::Supports { params, body }],
Stmt::UnknownAtRule {
params, name, body, ..
} => vec![Toplevel::UnknownAtRule { params, name, body }],
@ -216,6 +224,33 @@ impl Css {
)?;
writeln!(buf, "{}}}", padding)?;
}
Toplevel::Supports { params, body } => {
if should_emit_newline {
should_emit_newline = false;
writeln!(buf)?;
}
if params.is_empty() {
write!(buf, "{}@supports", padding)?;
} else {
write!(buf, "{}@supports {}", padding, params)?;
}
if body.is_empty() {
writeln!(buf, ";")?;
continue;
} else {
writeln!(buf, " {{")?;
}
Css::from_stmts(body, extender)?._inner_pretty_print(
buf,
map,
extender,
nesting + 1,
)?;
writeln!(buf, "{}}}", padding)?;
}
Toplevel::Media { params, body } => {
if body.is_empty() {
continue;

View File

@ -56,6 +56,10 @@ pub(crate) enum Stmt {
params: String,
body: Vec<Stmt>,
},
Supports {
params: String,
body: Vec<Stmt>,
},
AtRoot {
body: Vec<Stmt>,
},
@ -1040,31 +1044,7 @@ impl<'a> Parser<'a> {
}
fn parse_media(&mut self) -> SassResult<Stmt> {
let mut params = String::new();
self.whitespace();
while let Some(tok) = self.toks.next() {
match tok.kind {
'{' => break,
'#' => {
if let Some(Token { kind: '{', pos }) = self.toks.peek().cloned() {
self.toks.next();
self.span_before = pos;
let interpolation = self.parse_interpolation()?;
params.push_str(&interpolation.node.to_css_string(interpolation.span)?);
continue;
} else {
params.push(tok.kind);
}
}
'\n' | ' ' | '\t' => {
self.whitespace();
params.push(' ');
continue;
}
_ => {}
}
params.push(tok.kind);
}
let params = self.parse_media_args()?;
if params.is_empty() {
return Err(("Expected identifier.", self.span_before).into());
@ -1179,7 +1159,6 @@ impl<'a> Parser<'a> {
Ok(stmts)
}
#[allow(clippy::unused_self)]
fn parse_extend(&mut self) -> SassResult<()> {
// todo: track when inside ruleset or `@content`
// if !self.in_style_rule && !self.in_mixin && !self.in_content_block {
@ -1256,15 +1235,91 @@ impl<'a> Parser<'a> {
Ok(())
}
#[allow(clippy::unused_self)]
fn parse_supports(&mut self) -> SassResult<Stmt> {
todo!("@supports not yet implemented")
let params = self.parse_media_args()?;
if params.is_empty() {
return Err(("Expected \"not\".", self.span_before).into());
}
let raw_body = Parser {
toks: self.toks,
map: self.map,
path: self.path,
scopes: self.scopes,
global_scope: self.global_scope,
super_selectors: self.super_selectors,
span_before: self.span_before,
content: self.content.clone(),
in_mixin: self.in_mixin,
in_function: self.in_function,
in_control_flow: self.in_control_flow,
at_root: false,
at_root_has_selector: self.at_root_has_selector,
extender: self.extender,
}
.parse()?;
let mut rules = Vec::with_capacity(raw_body.len());
let mut body = Vec::new();
for stmt in raw_body {
match stmt {
Stmt::Style(..) => body.push(stmt),
_ => rules.push(stmt),
}
}
if !self.super_selectors.last().is_empty() {
body = vec![Stmt::RuleSet {
selector: self.super_selectors.last().clone(),
body,
super_selector: Selector::new(),
}];
}
body.append(&mut rules);
Ok(Stmt::Supports {
params: params.trim().to_owned(),
body,
})
}
#[allow(clippy::unused_self)]
fn parse_keyframes(&mut self) -> SassResult<Stmt> {
todo!("@keyframes not yet implemented")
}
// todo: we should use a specialized struct to represent these
fn parse_media_args(&mut self) -> SassResult<String> {
let mut params = String::new();
self.whitespace();
while let Some(tok) = self.toks.next() {
match tok.kind {
'{' => break,
'#' => {
if let Some(Token { kind: '{', pos }) = self.toks.peek().cloned() {
self.toks.next();
self.span_before = pos;
let interpolation = self.parse_interpolation()?;
params.push_str(&interpolation.node.to_css_string(interpolation.span)?);
continue;
} else {
params.push(tok.kind);
}
}
'\n' | ' ' | '\t' => {
self.whitespace();
params.push(' ');
continue;
}
_ => {}
}
params.push(tok.kind);
}
Ok(params)
}
}
impl<'a> Parser<'a> {