refactor parsing of unknown at rules
This commit is contained in:
parent
3ab2aa961a
commit
86a1ffec42
@ -1,3 +1,3 @@
|
||||
body {
|
||||
background: red;
|
||||
}
|
||||
}
|
||||
|
@ -6,4 +6,8 @@ pub(crate) struct UnknownAtRule {
|
||||
pub super_selector: Selector,
|
||||
pub params: String,
|
||||
pub body: Vec<Stmt>,
|
||||
|
||||
/// Whether or not this @-rule was declared with curly
|
||||
/// braces. A body may not necessarily have contents
|
||||
pub has_body: bool,
|
||||
}
|
||||
|
@ -21,6 +21,15 @@ struct ToplevelUnknownAtRule {
|
||||
name: String,
|
||||
params: String,
|
||||
body: Vec<Stmt>,
|
||||
has_body: bool,
|
||||
is_group_end: bool,
|
||||
inside_rule: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct BlockEntryUnknownAtRule {
|
||||
name: String,
|
||||
params: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -65,6 +74,7 @@ impl Toplevel {
|
||||
pub fn is_group_end(&self) -> bool {
|
||||
match self {
|
||||
Toplevel::RuleSet { is_group_end, .. } => *is_group_end,
|
||||
Toplevel::UnknownAtRule(t) => t.is_group_end && t.inside_rule,
|
||||
Toplevel::Media {
|
||||
inside_rule,
|
||||
is_group_end,
|
||||
@ -87,6 +97,7 @@ fn set_group_end(group: &mut [Toplevel]) {
|
||||
| Some(Toplevel::Media { is_group_end, .. }) => {
|
||||
*is_group_end = true;
|
||||
}
|
||||
Some(Toplevel::UnknownAtRule(t)) => t.is_group_end = true,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -95,6 +106,7 @@ fn set_group_end(group: &mut [Toplevel]) {
|
||||
enum BlockEntry {
|
||||
Style(Style),
|
||||
MultilineComment(String),
|
||||
UnknownAtRule(BlockEntryUnknownAtRule),
|
||||
}
|
||||
|
||||
impl BlockEntry {
|
||||
@ -102,6 +114,13 @@ impl BlockEntry {
|
||||
match self {
|
||||
BlockEntry::Style(s) => s.to_string(),
|
||||
BlockEntry::MultilineComment(s) => Ok(format!("/*{}*/", s)),
|
||||
BlockEntry::UnknownAtRule(BlockEntryUnknownAtRule { name, params }) => {
|
||||
Ok(if params.is_empty() {
|
||||
format!("@{};", name)
|
||||
} else {
|
||||
format!("@{} {};", name, params)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -138,6 +157,17 @@ impl Toplevel {
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
|
||||
fn push_unknown_at_rule(&mut self, at_rule: ToplevelUnknownAtRule) {
|
||||
if let Toplevel::RuleSet { body, .. } = self {
|
||||
body.push(BlockEntry::UnknownAtRule(BlockEntryUnknownAtRule {
|
||||
name: at_rule.name,
|
||||
params: at_rule.params,
|
||||
}));
|
||||
} else {
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -206,13 +236,27 @@ impl Css {
|
||||
}
|
||||
Stmt::UnknownAtRule(u) => {
|
||||
let UnknownAtRule {
|
||||
params, body, name, ..
|
||||
params,
|
||||
body,
|
||||
name,
|
||||
has_body,
|
||||
..
|
||||
} = *u;
|
||||
vals.push(Toplevel::UnknownAtRule(Box::new(ToplevelUnknownAtRule {
|
||||
|
||||
let at_rule = ToplevelUnknownAtRule {
|
||||
name,
|
||||
params,
|
||||
body,
|
||||
})));
|
||||
has_body,
|
||||
inside_rule: true,
|
||||
is_group_end: false,
|
||||
};
|
||||
|
||||
if has_body {
|
||||
vals.push(Toplevel::UnknownAtRule(Box::new(at_rule)));
|
||||
} else {
|
||||
vals.first_mut().unwrap().push_unknown_at_rule(at_rule);
|
||||
}
|
||||
}
|
||||
Stmt::Return(..) => unreachable!(),
|
||||
Stmt::AtRoot { body } => {
|
||||
@ -267,12 +311,19 @@ impl Css {
|
||||
}
|
||||
Stmt::UnknownAtRule(u) => {
|
||||
let UnknownAtRule {
|
||||
params, body, name, ..
|
||||
params,
|
||||
body,
|
||||
name,
|
||||
has_body,
|
||||
..
|
||||
} = *u;
|
||||
vec![Toplevel::UnknownAtRule(Box::new(ToplevelUnknownAtRule {
|
||||
name,
|
||||
params,
|
||||
body,
|
||||
has_body,
|
||||
inside_rule: false,
|
||||
is_group_end: false,
|
||||
}))]
|
||||
}
|
||||
Stmt::Return(..) => unreachable!("@return: {:?}", stmt),
|
||||
@ -393,7 +444,9 @@ impl Formatter for CompressedFormatter {
|
||||
write!(buf, "@import {};", s)?;
|
||||
}
|
||||
Toplevel::UnknownAtRule(u) => {
|
||||
let ToplevelUnknownAtRule { params, name, body } = *u;
|
||||
let ToplevelUnknownAtRule {
|
||||
params, name, body, ..
|
||||
} = *u;
|
||||
|
||||
if params.is_empty() {
|
||||
write!(buf, "@{}", name)?;
|
||||
@ -494,6 +547,7 @@ impl CompressedFormatter {
|
||||
break;
|
||||
}
|
||||
BlockEntry::MultilineComment(..) => continue,
|
||||
b @ BlockEntry::UnknownAtRule(_) => write!(buf, "{}", b.to_string()?)?,
|
||||
}
|
||||
}
|
||||
|
||||
@ -505,6 +559,7 @@ impl CompressedFormatter {
|
||||
write!(buf, ";{}:{}", s.property, value)?;
|
||||
}
|
||||
BlockEntry::MultilineComment(..) => continue,
|
||||
b @ BlockEntry::UnknownAtRule(_) => write!(buf, "{}", b.to_string()?)?,
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@ -593,7 +648,14 @@ impl Formatter for ExpandedFormatter {
|
||||
write!(buf, "{}@import {};", padding, s)?;
|
||||
}
|
||||
Toplevel::UnknownAtRule(u) => {
|
||||
let ToplevelUnknownAtRule { params, name, body } = *u;
|
||||
let ToplevelUnknownAtRule {
|
||||
params,
|
||||
name,
|
||||
body,
|
||||
has_body,
|
||||
inside_rule,
|
||||
..
|
||||
} = *u;
|
||||
|
||||
if params.is_empty() {
|
||||
write!(buf, "{}@{}", padding, name)?;
|
||||
@ -601,14 +663,29 @@ impl Formatter for ExpandedFormatter {
|
||||
write!(buf, "{}@{} {}", padding, name, params)?;
|
||||
}
|
||||
|
||||
if body.is_empty() {
|
||||
let css = Css::from_stmts(
|
||||
body,
|
||||
if inside_rule {
|
||||
AtRuleContext::Unknown
|
||||
} else {
|
||||
AtRuleContext::None
|
||||
},
|
||||
css.allows_charset,
|
||||
)?;
|
||||
|
||||
if !has_body {
|
||||
write!(buf, ";")?;
|
||||
prev = Some(Previous { is_group_end });
|
||||
continue;
|
||||
}
|
||||
|
||||
if css.blocks.iter().all(Toplevel::is_invisible) {
|
||||
write!(buf, " {{}}")?;
|
||||
prev = Some(Previous { is_group_end });
|
||||
continue;
|
||||
}
|
||||
|
||||
writeln!(buf, " {{")?;
|
||||
let css = Css::from_stmts(body, AtRuleContext::Unknown, css.allows_charset)?;
|
||||
self.write_css(buf, css, map)?;
|
||||
write!(buf, "\n{}}}", padding)?;
|
||||
}
|
||||
|
@ -218,7 +218,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||
if let Some(ref content_args) = content.content_args {
|
||||
call_args.max_args(content_args.len())?;
|
||||
|
||||
let scope = self.eval_args(&content_args, call_args)?;
|
||||
let scope = self.eval_args(content_args, call_args)?;
|
||||
scope_at_decl.enter_scope(scope);
|
||||
entered_scope = true;
|
||||
} else {
|
||||
|
@ -618,36 +618,47 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||
|
||||
let mut params = String::new();
|
||||
self.whitespace_or_comment();
|
||||
if let Some(Token { kind: ';', .. }) | None = self.toks.peek() {
|
||||
self.toks.next();
|
||||
return Ok(Stmt::UnknownAtRule(Box::new(UnknownAtRule {
|
||||
name,
|
||||
super_selector: Selector::new(self.span_before),
|
||||
params: String::new(),
|
||||
body: Vec::new(),
|
||||
})));
|
||||
}
|
||||
while let Some(tok) = self.toks.next() {
|
||||
match tok.kind {
|
||||
'{' => break,
|
||||
'#' => {
|
||||
|
||||
loop {
|
||||
match self.toks.peek() {
|
||||
Some(Token { kind: '{', .. }) => {
|
||||
self.toks.next();
|
||||
break;
|
||||
}
|
||||
Some(Token { kind: ';', .. }) | Some(Token { kind: '}', .. }) | None => {
|
||||
self.consume_char_if_exists(';');
|
||||
return Ok(Stmt::UnknownAtRule(Box::new(UnknownAtRule {
|
||||
name,
|
||||
super_selector: Selector::new(self.span_before),
|
||||
has_body: false,
|
||||
params: params.trim().to_owned(),
|
||||
body: Vec::new(),
|
||||
})));
|
||||
}
|
||||
Some(Token { kind: '#', .. }) => {
|
||||
self.toks.next();
|
||||
|
||||
if let Some(Token { kind: '{', pos }) = self.toks.peek() {
|
||||
self.span_before = self.span_before.merge(pos);
|
||||
self.toks.next();
|
||||
params.push_str(&self.parse_interpolation_as_string()?);
|
||||
} else {
|
||||
params.push(tok.kind);
|
||||
params.push('#');
|
||||
}
|
||||
continue;
|
||||
}
|
||||
'\n' | ' ' | '\t' => {
|
||||
Some(Token { kind: '\n', .. })
|
||||
| Some(Token { kind: ' ', .. })
|
||||
| Some(Token { kind: '\t', .. }) => {
|
||||
self.whitespace();
|
||||
params.push(' ');
|
||||
continue;
|
||||
}
|
||||
_ => {}
|
||||
Some(Token { kind, .. }) => {
|
||||
self.toks.next();
|
||||
params.push(kind);
|
||||
}
|
||||
}
|
||||
params.push(tok.kind);
|
||||
}
|
||||
|
||||
let raw_body = self.parse_stmt()?;
|
||||
@ -680,6 +691,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||
name,
|
||||
super_selector: Selector::new(self.span_before),
|
||||
params: params.trim().to_owned(),
|
||||
has_body: true,
|
||||
body,
|
||||
})))
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ test!(
|
||||
"@foo {\n color: red;\n}\n"
|
||||
);
|
||||
test!(unknown_at_rule_no_body, "@foo;\n", "@foo;\n");
|
||||
test!(unknown_at_rule_empty_body, "@foo {}\n", "@foo {}\n");
|
||||
test!(unknown_at_rule_no_body_eof, "@foo", "@foo;\n");
|
||||
test!(
|
||||
unknown_at_rule_interpolated_eof_no_body,
|
||||
@ -32,4 +33,56 @@ test!(
|
||||
}",
|
||||
"@foo (a: b) {\n a {\n color: red;\n }\n}\na {\n color: green;\n}\n"
|
||||
);
|
||||
test!(
|
||||
no_semicolon_no_params_no_body,
|
||||
"a {
|
||||
@b
|
||||
}
|
||||
|
||||
a {
|
||||
color: red;
|
||||
}",
|
||||
"a {\n @b;\n}\n\na {\n color: red;\n}\n"
|
||||
);
|
||||
test!(
|
||||
no_semicolon_has_params_no_body,
|
||||
"a {
|
||||
@foo bar
|
||||
}
|
||||
|
||||
a {
|
||||
color: red;
|
||||
}",
|
||||
"a {\n @foo bar;\n}\n\na {\n color: red;\n}\n"
|
||||
);
|
||||
test!(
|
||||
no_body_remains_inside_style_rule,
|
||||
"a {
|
||||
@box-shadow: $btn-focus-box-shadow, $btn-active-box-shadow;
|
||||
}
|
||||
|
||||
a {
|
||||
color: red;
|
||||
}",
|
||||
"a {\n @box-shadow : $btn-focus-box-shadow, $btn-active-box-shadow;\n}\n\na {\n color: red;\n}\n"
|
||||
);
|
||||
test!(
|
||||
empty_body_moves_outside_style_rule,
|
||||
"a {
|
||||
@b {}
|
||||
}
|
||||
|
||||
a {
|
||||
color: red;
|
||||
}",
|
||||
"@b {}\n\na {\n color: red;\n}\n"
|
||||
);
|
||||
test!(
|
||||
#[ignore = "not sure how dart-sass is parsing this to include the semicolon in the params"]
|
||||
params_contain_silent_comment_and_semicolon,
|
||||
"a {
|
||||
@box-shadow: $btn-focus-box-shadow, // $btn-active-box-shadow;
|
||||
}",
|
||||
"a {\n @box-shadow : $btn-focus-box-shadow, / $btn-active-box-shadow;\n}\n"
|
||||
);
|
||||
test!(contains_multiline_comment, "@foo /**/;\n", "@foo;\n");
|
||||
|
Loading…
x
Reference in New Issue
Block a user