diff --git a/src/output.rs b/src/output.rs index 14e1628..2541521 100644 --- a/src/output.rs +++ b/src/output.rs @@ -30,8 +30,15 @@ enum Toplevel { UnknownAtRule(Box), Keyframes(Box), KeyframesRuleSet(Vec, Vec), - Media { query: String, body: Vec }, - Supports { params: String, body: Vec }, + Media { + query: String, + body: Vec, + inside_rule: bool, + }, + Supports { + params: String, + body: Vec, + }, Newline, // todo: do we actually need a toplevel style variant? Style(Style), @@ -114,11 +121,15 @@ impl Css { if body.is_empty() { return Ok(Vec::new()); } + let selector = selector.into_selector().remove_placeholders(); + if selector.is_empty() { return Ok(Vec::new()); } + let mut vals = vec![Toplevel::new_rule(selector)]; + for rule in body { match rule { Stmt::RuleSet { .. } => vals.extend(self.parse_stmt(rule)?), @@ -126,7 +137,11 @@ impl Css { Stmt::Comment(s) => vals.first_mut().unwrap().push_comment(s), Stmt::Media(m) => { let MediaRule { query, body, .. } = *m; - vals.push(Toplevel::Media { query, body }); + vals.push(Toplevel::Media { + query, + body, + inside_rule: true, + }); } Stmt::Supports(s) => { let SupportsRule { params, body } = *s; @@ -173,7 +188,11 @@ impl Css { Stmt::Style(s) => vec![Toplevel::Style(s)], Stmt::Media(m) => { let MediaRule { query, body, .. } = *m; - vec![Toplevel::Media { query, body }] + vec![Toplevel::Media { + query, + body, + inside_rule: false, + }] } Stmt::Supports(s) => { let SupportsRule { params, body } = *s; @@ -265,8 +284,8 @@ trait Formatter { struct CompressedFormatter {} impl Formatter for CompressedFormatter { - fn write_css(&mut self, buf: &mut Vec, mut css: Css, map: &CodeMap) -> SassResult<()> { - for block in mem::take(&mut css.blocks) { + fn write_css(&mut self, buf: &mut Vec, css: Css, map: &CodeMap) -> SassResult<()> { + for block in css.blocks { match block { Toplevel::RuleSet(selector, styles) => { if styles.is_empty() { @@ -362,7 +381,7 @@ impl Formatter for CompressedFormatter { self.write_css(buf, css, map)?; write!(buf, "}}")?; } - Toplevel::Media { query, body } => { + Toplevel::Media { query, body, .. } => { if body.is_empty() { continue; } @@ -433,32 +452,38 @@ struct ExpandedFormatter { } impl Formatter for ExpandedFormatter { - fn write_css(&mut self, buf: &mut Vec, mut css: Css, map: &CodeMap) -> SassResult<()> { + fn write_css(&mut self, buf: &mut Vec, css: Css, map: &CodeMap) -> SassResult<()> { let mut has_written = false; let padding = " ".repeat(self.nesting); let mut should_emit_newline = false; self.nesting += 1; - for block in mem::take(&mut css.blocks) { + + for block in css.blocks { match block { Toplevel::RuleSet(selector, styles) => { if styles.is_empty() { continue; } + has_written = true; if should_emit_newline && !css.in_at_rule { should_emit_newline = false; writeln!(buf)?; } + writeln!(buf, "{}{} {{", padding, selector)?; + for style in styles { writeln!(buf, "{} {}", padding, style.to_string()?)?; } + writeln!(buf, "{}}}", padding)?; } Toplevel::KeyframesRuleSet(selector, body) => { if body.is_empty() { continue; } + has_written = true; writeln!( @@ -552,13 +577,22 @@ impl Formatter for ExpandedFormatter { self.write_css(buf, css, map)?; writeln!(buf, "{}}}", padding)?; } - Toplevel::Media { query, body } => { + Toplevel::Media { + query, + body, + inside_rule, + } => { if body.is_empty() { continue; } + if should_emit_newline { + should_emit_newline = false; + writeln!(buf)?; + } + writeln!(buf, "{}@media {} {{", padding, query)?; - let css = Css::from_stmts(body, true, css.allows_charset)?; + let css = Css::from_stmts(body, inside_rule, css.allows_charset)?; self.write_css(buf, css, map)?; writeln!(buf, "{}}}", padding)?; } @@ -573,7 +607,9 @@ impl Formatter for ExpandedFormatter { } } } + self.nesting -= 1; + Ok(()) } } diff --git a/src/token.rs b/src/token.rs index 6328446..ba9c979 100644 --- a/src/token.rs +++ b/src/token.rs @@ -23,6 +23,7 @@ impl IsWhitespace for Token { if self.kind.is_whitespace() { return true; } + false } } diff --git a/tests/media.rs b/tests/media.rs index 784aa87..8e3a939 100644 --- a/tests/media.rs +++ b/tests/media.rs @@ -109,6 +109,59 @@ test!( ", "@media (false) {\n a {\n b: c;\n }\n}\n" ); +test!( + newline_emitted_for_different_toplevel_rulesets, + "@media print { + a { + color: red; + } + + b { + color: green; + } + }", + "@media print {\n a {\n color: red;\n }\n\n b {\n color: green;\n }\n}\n" +); +test!( + newline_emitted_before_media_when_following_ruleset, + "a { + color: red; + } + @media print { + a { + color: red; + } + }", + "a {\n color: red;\n}\n\n@media print {\n a {\n color: red;\n }\n}\n" +); +test!( + no_newline_emitted_between_two_media_rules, + "@media print { + a { + color: red; + } + } + @media print { + a { + color: red; + } + }", + "@media print {\n a {\n color: red;\n }\n}\n@media print {\n a {\n color: red;\n }\n}\n" +); +test!( + no_newline_emitted_between_two_media_rules_when_in_same_ruleset, + "a { + @media foo { + color: red; + } + + @media bar { + color: green; + } + }", + "@media foo {\n a {\n color: red;\n }\n}\n@media bar {\n a {\n color: green;\n }\n}\n" +); + error!( media_feature_missing_closing_paren, "@media foo and (bar:a", "Error: expected \")\"."