grass/src/format.rs

172 lines
4.9 KiB
Rust
Raw Normal View History

use std::io::Write;
use crate::{RuleSet, SassResult, Stmt, StyleSheet};
pub(crate) struct PrettyPrinter<W: Write> {
buf: W,
scope: usize,
}
impl<W: Write> PrettyPrinter<W> {
pub(crate) fn new(buf: W) -> Self {
PrettyPrinter { buf, scope: 0 }
}
2020-01-05 12:35:37 -05:00
/// Pretty print `crate::Stmt`
2020-01-05 19:09:46 -05:00
/// Throws away super selectors and variables
fn pretty_print_stmt(&mut self, stmt: &Stmt) -> SassResult<()> {
let padding = vec![' '; self.scope * 2].iter().collect::<String>();
match stmt {
Stmt::MultilineComment(s) => writeln!(self.buf, "{}/*{}*/", padding, s)?,
Stmt::RuleSet(RuleSet {
selector, rules, ..
}) => {
writeln!(self.buf, "{}{} {{", padding, selector)?;
self.scope += 1;
for rule in rules {
self.pretty_print_stmt(rule)?;
}
writeln!(self.buf, "{}}}", padding)?;
self.scope -= 1;
}
Stmt::Style(s) => {
writeln!(self.buf, "{}{}", padding, s)?;
}
2020-01-26 15:27:38 -05:00
Stmt::AtRule(r) => {
writeln!(self.buf, "{}{}", padding, r)?;
}
}
Ok(())
}
2020-01-05 12:35:37 -05:00
/// Pretty print SCSS
///
/// The result should be an exact copy of the SCSS input
/// Empty rules are included
pub fn pretty_print(&mut self, s: &StyleSheet) -> SassResult<()> {
2020-01-18 14:57:56 -05:00
for rule in &s.0 {
self.pretty_print_stmt(rule)?;
}
Ok(())
}
2020-01-05 12:35:37 -05:00
/// Pretty print `crate::Stmt`
/// Keeps super selectors
fn pretty_print_stmt_preserve_super_selectors(&mut self, stmt: &Stmt) -> SassResult<()> {
let padding = vec![' '; self.scope * 2].iter().collect::<String>();
match stmt {
Stmt::MultilineComment(s) => writeln!(self.buf, "/*{}*/", s)?,
Stmt::RuleSet(RuleSet {
selector,
rules,
super_selector,
}) => {
2020-01-18 21:05:26 -05:00
writeln!(self.buf, "{}{} {{", padding, super_selector.zip(selector))?;
self.scope += 1;
for rule in rules {
self.pretty_print_stmt(rule)?;
}
writeln!(self.buf, "{}}}", padding)?;
self.scope -= 1;
}
Stmt::Style(s) => {
writeln!(self.buf, "{}{}", padding, s)?;
}
2020-01-26 15:27:38 -05:00
Stmt::AtRule(r) => {
writeln!(self.buf, "{}{}", padding, r)?;
}
}
Ok(())
}
2020-01-05 12:35:37 -05:00
/// Pretty print a special form of SCSS that shows what the full selectors are for children
/// Meant for debugging
/// Empty rules are included
2020-01-05 12:52:50 -05:00
pub(crate) fn pretty_print_preserve_super_selectors(
&mut self,
s: &StyleSheet,
) -> SassResult<()> {
2020-01-18 14:57:56 -05:00
for rule in &s.0 {
self.pretty_print_stmt_preserve_super_selectors(rule)?;
}
writeln!(self.buf)?;
Ok(())
}
}
#[cfg(test)]
2020-01-05 20:23:35 -05:00
mod test_scss {
use super::StyleSheet;
macro_rules! test {
($func:ident, $input:literal) => {
#[test]
fn $func() {
let mut buf = Vec::new();
2020-01-06 19:23:52 -05:00
StyleSheet::new($input)
.expect(concat!("failed to parse on ", $input))
.pretty_print(&mut buf)
.expect(concat!("failed to pretty print on ", $input));
assert_eq!(
String::from($input),
String::from_utf8(buf).expect("produced invalid utf8")
);
}
};
($func:ident, $input:literal, $output:literal) => {
#[test]
fn $func() {
let mut buf = Vec::new();
2020-01-06 19:23:52 -05:00
StyleSheet::new($input)
.expect(concat!("failed to parse on ", $input))
.pretty_print(&mut buf)
.expect(concat!("failed to pretty print on ", $input));
assert_eq!(
String::from($output),
String::from_utf8(buf).expect("produced invalid utf8")
);
}
};
}
test!(empty, "");
test!(basic_nesting, "a {\n b {\n }\n}\n");
2020-01-05 20:23:35 -05:00
test!(mul_nesting, "a, b {\n a, b {\n }\n}\n");
test!(basic_style, "a {\n color: red;\n}\n");
test!(two_styles, "a {\n color: red;\n color: blue;\n}\n");
test!(
nested_style_in_parent,
"a {\n color: red;\n b {\n }\n}\n"
);
test!(
nested_style_in_child,
"a {\n b {\n color: red;\n }\n}\n"
);
test!(
nested_style_in_both,
"a {\n color: red;\n b {\n color: red;\n }\n}\n"
);
test!(
deeply_nested_selector,
"\
a {
b {
c {
d {
e {
f {
g {
h {
i {
}
}
}
}
}
}
}
}
}
"
);
}