refactor parsing and eval of @ each

This commit is contained in:
ConnorSkees 2020-04-24 22:57:39 -04:00
parent 092cbe75bb
commit f60089f4f9
12 changed files with 261 additions and 180 deletions

129
src/atrule/each_rule.rs Normal file
View File

@ -0,0 +1,129 @@
use codemap::{Span, Spanned};
use peekmore::{PeekMore, PeekMoreIterator};
use crate::common::{Brackets, ListSeparator};
use crate::error::SassResult;
use crate::scope::Scope;
use crate::selector::Selector;
use crate::utils::{
devour_whitespace, eat_ident, read_until_closing_curly_brace, read_until_open_curly_brace,
};
use crate::value::Value;
use crate::{Stmt, Token};
use super::{ruleset_eval, AtRule};
#[derive(Debug, Clone)]
pub(crate) struct Each {
vars: Vec<Spanned<String>>,
iter: Vec<Value>,
body: Vec<Token>,
}
impl Each {
pub fn ruleset_eval(
self,
scope: &mut Scope,
super_selector: &Selector,
) -> SassResult<Vec<Spanned<Stmt>>> {
let mut stmts = Vec::new();
for row in self.iter {
let this_iterator = match row {
Value::List(v, ..) => v,
Value::Map(m) => m
.into_iter()
.map(|(k, v)| Value::List(vec![k, v], ListSeparator::Space, Brackets::None))
.collect(),
v => vec![v],
};
if self.vars.len() == 1 {
scope.insert_var(
&self.vars[0],
Spanned {
node: Value::List(this_iterator, ListSeparator::Space, Brackets::None),
span: self.vars[0].span,
},
)?;
} else {
for (var, val) in self.vars.clone().into_iter().zip(
this_iterator
.into_iter()
.chain(std::iter::once(Value::Null).cycle()),
) {
scope.insert_var(
&var.node,
Spanned {
node: val,
span: var.span,
},
)?;
}
}
ruleset_eval(
&mut self.body.clone().into_iter().peekmore(),
scope,
super_selector,
false,
&mut stmts,
)?;
}
Ok(stmts)
}
}
pub(crate) fn parse_each<I: Iterator<Item = Token>>(
toks: &mut PeekMoreIterator<I>,
scope: &mut Scope,
super_selector: &Selector,
mut span: Span,
) -> SassResult<AtRule> {
devour_whitespace(toks);
let mut vars = Vec::new();
loop {
let next = toks.next().ok_or(("expected \"$\".", span))?;
span = next.pos();
match next.kind {
'$' => vars.push(eat_ident(toks, scope, super_selector)?),
_ => return Err(("expected \"$\".", next.pos()).into()),
}
devour_whitespace(toks);
if toks
.peek()
.ok_or(("expected \"$\".", vars[vars.len() - 1].span))?
.kind
== ','
{
toks.next();
devour_whitespace(toks);
} else {
break;
}
}
if toks.peek().is_none() {
todo!()
}
let i = eat_ident(toks, scope, super_selector)?;
if i.node.to_ascii_lowercase() != "in" {
return Err(("Expected \"in\".", i.span).into());
}
devour_whitespace(toks);
let iter_val = Value::from_vec(read_until_open_curly_brace(toks), scope, super_selector)?;
let iter = match iter_val.node.eval(iter_val.span)?.node {
Value::List(v, ..) => v,
Value::Map(m) => m
.into_iter()
.map(|(k, v)| Value::List(vec![k, v], ListSeparator::Space, Brackets::None))
.collect(),
v => vec![v],
};
toks.next();
devour_whitespace(toks);
let mut body = read_until_closing_curly_brace(toks);
body.push(toks.next().unwrap());
devour_whitespace(toks);
Ok(AtRule::Each(Each { vars, iter, body }))
}

View File

@ -6,7 +6,7 @@ use peekmore::{PeekMore, PeekMoreIterator};
use num_traits::cast::ToPrimitive; use num_traits::cast::ToPrimitive;
use super::parse::eat_stmts; use super::parse::ruleset_eval;
use super::AtRule; use super::AtRule;
use crate::error::SassResult; use crate::error::SassResult;
@ -42,27 +42,13 @@ impl For {
span: self.var.span, span: self.var.span,
}, },
)?; )?;
for stmt in eat_stmts( ruleset_eval(
&mut self.body.clone().into_iter().peekmore(), &mut self.body.clone().into_iter().peekmore(),
scope, scope,
super_selector, super_selector,
false, false,
)? { &mut stmts,
match stmt.node { )?;
Stmt::AtRule(AtRule::For(f)) => {
stmts.extend(f.ruleset_eval(scope, super_selector)?)
}
Stmt::AtRule(AtRule::While(w)) => {
// TODO: should at_root be false? scoping
stmts.extend(w.ruleset_eval(scope, super_selector, false)?)
}
Stmt::AtRule(AtRule::Include(s)) | Stmt::AtRule(AtRule::Each(s)) => {
stmts.extend(s)
}
Stmt::AtRule(AtRule::If(i)) => stmts.extend(i.eval(scope, super_selector)?),
_ => stmts.push(stmt),
}
}
} }
Ok(stmts) Ok(stmts)
} }

View File

@ -178,6 +178,7 @@ impl Function {
val = Value::from_vec(w.cond.clone(), scope, super_selector)?; val = Value::from_vec(w.cond.clone(), scope, super_selector)?;
} }
} }
Stmt::AtRule(AtRule::Each(..)) => todo!("@each in @function"),
_ => return Err(("This at-rule is not allowed here.", stmt.span).into()), _ => return Err(("This at-rule is not allowed here.", stmt.span).into()),
} }
} }

View File

@ -2,7 +2,7 @@ use codemap::Spanned;
use peekmore::{PeekMore, PeekMoreIterator}; use peekmore::{PeekMore, PeekMoreIterator};
use super::{eat_stmts, AtRule}; use super::ruleset_eval;
use crate::error::SassResult; use crate::error::SassResult;
use crate::scope::Scope; use crate::scope::Scope;
@ -120,18 +120,13 @@ impl If {
if !found_true { if !found_true {
toks = self.else_; toks = self.else_;
} }
for stmt in eat_stmts( ruleset_eval(
&mut toks.into_iter().peekmore(), &mut toks.into_iter().peekmore(),
scope, scope,
super_selector, super_selector,
false, false,
)? { &mut stmts,
match stmt.node { )?;
Stmt::AtRule(AtRule::If(i)) => stmts.extend(i.eval(scope, super_selector)?),
Stmt::RuleSet(r) if r.selector.is_empty() => stmts.extend(r.rules),
_ => stmts.push(stmt),
}
}
Ok(stmts) Ok(stmts)
} }
} }

View File

@ -126,10 +126,13 @@ impl Mixin {
AtRule::For(f) => { AtRule::For(f) => {
stmts.extend(f.ruleset_eval(&mut self.scope, super_selector)?) stmts.extend(f.ruleset_eval(&mut self.scope, super_selector)?)
} }
AtRule::Each(e) => {
stmts.extend(e.ruleset_eval(&mut self.scope, super_selector)?)
}
AtRule::While(w) => { AtRule::While(w) => {
stmts.extend(w.ruleset_eval(&mut self.scope, super_selector, false)?) stmts.extend(w.ruleset_eval(&mut self.scope, super_selector, false)?)
} }
AtRule::Include(s) | AtRule::Each(s) => stmts.extend(s), AtRule::Include(s) => stmts.extend(s),
AtRule::If(i) => stmts.extend(i.eval(&mut self.scope.clone(), super_selector)?), AtRule::If(i) => stmts.extend(i.eval(&mut self.scope.clone(), super_selector)?),
AtRule::Content => stmts.extend(self.content.clone()), AtRule::Content => stmts.extend(self.content.clone()),
AtRule::Return(..) => { AtRule::Return(..) => {

View File

@ -2,26 +2,27 @@ use codemap::{Span, Spanned};
use peekmore::{PeekMore, PeekMoreIterator}; use peekmore::{PeekMore, PeekMoreIterator};
use crate::common::{Brackets, ListSeparator};
use crate::error::SassResult; use crate::error::SassResult;
use crate::scope::Scope; use crate::scope::Scope;
use crate::selector::Selector; use crate::selector::Selector;
use crate::utils::{ use crate::utils::{
devour_whitespace, eat_ident, read_until_closing_curly_brace, read_until_open_curly_brace, devour_whitespace, read_until_closing_curly_brace, read_until_open_curly_brace,
read_until_semicolon_or_closing_curly_brace, read_until_semicolon_or_closing_curly_brace,
}; };
use crate::value::Value; use crate::value::Value;
use crate::{RuleSet, Stmt, Token}; use crate::{RuleSet, Stmt, Token};
use each_rule::{parse_each, Each};
use for_rule::For; 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;
pub(crate) use mixin::{eat_include, Mixin}; pub(crate) use mixin::{eat_include, Mixin};
use parse::{eat_stmts, eat_stmts_at_root}; use parse::{eat_stmts, eat_stmts_at_root, ruleset_eval};
use unknown::UnknownAtRule; use unknown::UnknownAtRule;
use while_rule::{parse_while, While}; use while_rule::{parse_while, While};
mod each_rule;
mod for_rule; mod for_rule;
mod function; mod function;
mod if_rule; mod if_rule;
@ -33,8 +34,8 @@ mod while_rule;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) enum AtRule { pub(crate) enum AtRule {
Warn(String), Warn(Spanned<String>),
Debug(String), Debug(Spanned<String>),
Mixin(String, Box<Mixin>), Mixin(String, Box<Mixin>),
Function(String, Box<Function>), Function(String, Box<Function>),
Return(Vec<Token>), Return(Vec<Token>),
@ -42,7 +43,7 @@ pub(crate) enum AtRule {
Content, Content,
Unknown(UnknownAtRule), Unknown(UnknownAtRule),
For(For), For(For),
Each(Vec<Spanned<Stmt>>), Each(Each),
While(While), While(While),
Include(Vec<Spanned<Stmt>>), Include(Vec<Spanned<Stmt>>),
If(If), If(If),
@ -86,7 +87,10 @@ impl AtRule {
} }
devour_whitespace(toks); devour_whitespace(toks);
Spanned { Spanned {
node: AtRule::Warn(message.to_css_string(span)?), node: AtRule::Warn(Spanned {
node: message.to_css_string(span)?,
span,
}),
span, span,
} }
} }
@ -105,7 +109,10 @@ impl AtRule {
} }
devour_whitespace(toks); devour_whitespace(toks);
Spanned { Spanned {
node: AtRule::Debug(message.inspect(span)?), node: AtRule::Debug(Spanned {
node: message.inspect(span)?,
span,
}),
span, span,
} }
} }
@ -199,101 +206,10 @@ impl AtRule {
span: kind_span, span: kind_span,
} }
} }
AtRuleKind::Each => { AtRuleKind::Each => Spanned {
let mut stmts = Vec::new(); node: parse_each(toks, scope, super_selector, kind_span)?,
devour_whitespace(toks);
let mut vars = Vec::new();
let mut span = kind_span;
loop {
let next = toks.next().ok_or(("expected \"$\".", span))?;
span = next.pos();
match next.kind {
'$' => vars.push(eat_ident(toks, scope, super_selector)?),
_ => return Err(("expected \"$\".", next.pos()).into()),
}
devour_whitespace(toks);
if toks
.peek()
.ok_or(("expected \"$\".", vars[vars.len() - 1].span))?
.kind
== ','
{
toks.next();
devour_whitespace(toks);
} else {
break;
}
}
if toks.peek().is_none() {
todo!()
}
let i = eat_ident(toks, scope, super_selector)?;
if i.node.to_ascii_lowercase() != "in" {
return Err(("Expected \"in\".", i.span).into());
}
devour_whitespace(toks);
let iter_val =
Value::from_vec(read_until_open_curly_brace(toks), scope, super_selector)?;
let iterator = match iter_val.node.eval(iter_val.span)?.node {
Value::List(v, ..) => v,
Value::Map(m) => m
.into_iter()
.map(|(k, v)| Value::List(vec![k, v], ListSeparator::Space, Brackets::None))
.collect(),
v => vec![v],
};
toks.next();
devour_whitespace(toks);
let mut body = read_until_closing_curly_brace(toks);
body.push(toks.next().unwrap());
devour_whitespace(toks);
for row in iterator {
let this_iterator = match row {
Value::List(v, ..) => v,
Value::Map(m) => m
.into_iter()
.map(|(k, v)| {
Value::List(vec![k, v], ListSeparator::Space, Brackets::None)
})
.collect(),
v => vec![v],
};
if vars.len() == 1 {
scope.insert_var(
&vars[0],
Spanned {
node: Value::List(
this_iterator,
ListSeparator::Space,
Brackets::None,
),
span: vars[0].span,
},
)?;
} else {
for (var, val) in vars.clone().into_iter().zip(
this_iterator
.into_iter()
.chain(std::iter::once(Value::Null).cycle()),
) {
scope.insert_var(&var, Spanned { node: val, span })?;
}
}
stmts.extend(eat_stmts(
&mut body.clone().into_iter().peekmore(),
scope,
super_selector,
false,
)?);
}
Spanned {
node: AtRule::Each(stmts),
span: kind_span, span: kind_span,
} },
}
AtRuleKind::Extend => todo!("@extend not yet implemented"), AtRuleKind::Extend => todo!("@extend not yet implemented"),
AtRuleKind::If => Spanned { AtRuleKind::If => Spanned {
node: AtRule::If(If::from_tokens(toks)?), node: AtRule::If(If::from_tokens(toks)?),

View File

@ -2,6 +2,8 @@ use codemap::Spanned;
use peekmore::PeekMoreIterator; use peekmore::PeekMoreIterator;
use super::AtRule;
use crate::error::SassResult; use crate::error::SassResult;
use crate::scope::{global_var_exists, insert_global_var, Scope}; use crate::scope::{global_var_exists, insert_global_var, Scope};
use crate::selector::Selector; use crate::selector::Selector;
@ -100,3 +102,26 @@ pub(crate) fn eat_stmts_at_root<I: Iterator<Item = Token>>(
} }
Ok(stmts) Ok(stmts)
} }
pub(crate) fn ruleset_eval<I: Iterator<Item = Token>>(
toks: &mut PeekMoreIterator<I>,
scope: &mut Scope,
super_selector: &Selector,
at_root: bool,
stmts: &mut Vec<Spanned<Stmt>>,
) -> SassResult<()> {
for stmt in eat_stmts(toks, scope, super_selector, at_root)? {
match stmt.node {
Stmt::AtRule(AtRule::For(f)) => stmts.extend(f.ruleset_eval(scope, super_selector)?),
Stmt::AtRule(AtRule::Each(e)) => stmts.extend(e.ruleset_eval(scope, super_selector)?),
Stmt::AtRule(AtRule::While(w)) => {
// TODO: should at_root be false? scoping
stmts.extend(w.ruleset_eval(scope, super_selector, at_root)?)
}
Stmt::AtRule(AtRule::Include(s)) => stmts.extend(s),
Stmt::AtRule(AtRule::If(i)) => stmts.extend(i.eval(scope, super_selector)?),
_ => stmts.push(stmt),
}
}
Ok(())
}

View File

@ -2,7 +2,7 @@ use codemap::{Span, Spanned};
use peekmore::{PeekMore, PeekMoreIterator}; use peekmore::{PeekMore, PeekMoreIterator};
use super::{eat_stmts, AtRule}; use super::{ruleset_eval, AtRule};
use crate::error::SassResult; use crate::error::SassResult;
use crate::scope::Scope; use crate::scope::Scope;
@ -30,26 +30,13 @@ impl While {
let mut val = Value::from_vec(self.cond.clone(), scope, super_selector)?; let mut val = Value::from_vec(self.cond.clone(), scope, super_selector)?;
let scope = &mut scope.clone(); let scope = &mut scope.clone();
while val.node.is_true(val.span)? { while val.node.is_true(val.span)? {
for stmt in eat_stmts( ruleset_eval(
&mut self.body.clone().into_iter().peekmore(), &mut self.body.clone().into_iter().peekmore(),
scope, scope,
super_selector, super_selector,
at_root, at_root,
)? { &mut stmts,
match stmt.node { )?;
Stmt::AtRule(AtRule::For(f)) => {
stmts.extend(f.ruleset_eval(scope, super_selector)?)
}
Stmt::AtRule(AtRule::While(w)) => {
stmts.extend(w.ruleset_eval(scope, super_selector, at_root)?)
}
Stmt::AtRule(AtRule::Include(s)) | Stmt::AtRule(AtRule::Each(s)) => {
stmts.extend(s)
}
Stmt::AtRule(AtRule::If(i)) => stmts.extend(i.eval(scope, super_selector)?),
_ => stmts.push(stmt),
}
}
val = Value::from_vec(self.cond.clone(), scope, super_selector)?; val = Value::from_vec(self.cond.clone(), scope, super_selector)?;
} }
Ok(stmts) Ok(stmts)

View File

@ -16,7 +16,7 @@ impl SassError {
pub(crate) fn raw(self) -> (String, Span) { pub(crate) fn raw(self) -> (String, Span) {
match self.kind { match self.kind {
SassErrorKind::Raw(string, span) => (string, span), SassErrorKind::Raw(string, span) => (string, span),
_ => todo!(), e => todo!("unable to get raw of {:?}", e),
} }
} }

View File

@ -1,13 +1,17 @@
use std::ffi::OsStr; use std::ffi::OsStr;
use std::path::Path; use std::path::Path;
use codemap::Spanned; use codemap::{CodeMap, Spanned};
use crate::error::SassResult; use crate::error::SassResult;
use crate::scope::Scope; use crate::scope::Scope;
use crate::{Stmt, StyleSheet}; use crate::{Stmt, StyleSheet};
pub(crate) fn import(ctx: &Path, path: &Path) -> SassResult<(Vec<Spanned<Stmt>>, Scope)> { pub(crate) fn import(
ctx: &Path,
path: &Path,
map: &mut CodeMap,
) -> SassResult<(Vec<Spanned<Stmt>>, Scope)> {
let mut rules = Vec::new(); let mut rules = Vec::new();
let mut scope = Scope::new(); let mut scope = Scope::new();
if path.is_absolute() { if path.is_absolute() {
@ -39,7 +43,7 @@ pub(crate) fn import(ctx: &Path, path: &Path) -> SassResult<(Vec<Spanned<Stmt>>,
for name in &paths { for name in &paths {
if name.is_file() { if name.is_file() {
let (rules2, scope2) = let (rules2, scope2) =
StyleSheet::export_from_path(&name.to_str().expect("path should be UTF-8"))?; StyleSheet::export_from_path(&name.to_str().expect("path should be UTF-8"), map)?;
rules.extend(rules2); rules.extend(rules2);
scope.extend(scope2); scope.extend(scope2);
} }

View File

@ -237,7 +237,7 @@ impl StyleSheet {
StyleSheetParser { StyleSheetParser {
lexer: Lexer::new(&file).peekmore(), lexer: Lexer::new(&file).peekmore(),
nesting: 0, nesting: 0,
map: &map, map: &mut map,
path: Path::new(""), path: Path::new(""),
} }
.parse_toplevel() .parse_toplevel()
@ -245,7 +245,7 @@ impl StyleSheet {
.0, .0,
)) ))
.map_err(|e| raw_to_parse_error(&map, e))? .map_err(|e| raw_to_parse_error(&map, e))?
.pretty_print() .pretty_print(&map)
.map_err(|e| raw_to_parse_error(&map, e)) .map_err(|e| raw_to_parse_error(&map, e))
} }
@ -268,7 +268,7 @@ impl StyleSheet {
StyleSheetParser { StyleSheetParser {
lexer: Lexer::new(&file).peekmore(), lexer: Lexer::new(&file).peekmore(),
nesting: 0, nesting: 0,
map: &map, map: &mut map,
path: p.as_ref(), path: p.as_ref(),
} }
.parse_toplevel() .parse_toplevel()
@ -276,19 +276,19 @@ impl StyleSheet {
.0, .0,
)) ))
.map_err(|e| raw_to_parse_error(&map, e))? .map_err(|e| raw_to_parse_error(&map, e))?
.pretty_print() .pretty_print(&map)
.map_err(|e| raw_to_parse_error(&map, e)) .map_err(|e| raw_to_parse_error(&map, e))
} }
pub(crate) fn export_from_path<P: AsRef<Path> + Into<String> + Clone>( pub(crate) fn export_from_path<P: AsRef<Path> + Into<String> + Clone>(
p: &P, p: &P,
map: &mut CodeMap,
) -> SassResult<(Vec<Spanned<Stmt>>, Scope)> { ) -> SassResult<(Vec<Spanned<Stmt>>, Scope)> {
let mut map = CodeMap::new();
let file = map.add_file(p.clone().into(), String::from_utf8(fs::read(p)?)?); let file = map.add_file(p.clone().into(), String::from_utf8(fs::read(p)?)?);
Ok(StyleSheetParser { Ok(StyleSheetParser {
lexer: Lexer::new(&file).peekmore(), lexer: Lexer::new(&file).peekmore(),
nesting: 0, nesting: 0,
map: &map, map,
path: p.as_ref(), path: p.as_ref(),
} }
.parse_toplevel()?) .parse_toplevel()?)
@ -302,7 +302,7 @@ impl StyleSheet {
struct StyleSheetParser<'a> { struct StyleSheetParser<'a> {
lexer: PeekMoreIterator<Lexer<'a>>, lexer: PeekMoreIterator<Lexer<'a>>,
nesting: u32, nesting: u32,
map: &'a CodeMap, map: &'a mut CodeMap,
path: &'a Path, path: &'a Path,
} }
@ -387,7 +387,7 @@ impl<'a> StyleSheetParser<'a> {
devour_whitespace(&mut self.lexer); devour_whitespace(&mut self.lexer);
let (new_rules, new_scope) = import(self.path, file_name.as_ref())?; let (new_rules, new_scope) = import(self.path, file_name.as_ref(), &mut self.map)?;
rules.extend(new_rules); rules.extend(new_rules);
GLOBAL_SCOPE.with(|s| { GLOBAL_SCOPE.with(|s| {
s.borrow_mut().extend(new_scope); s.borrow_mut().extend(new_scope);
@ -412,8 +412,10 @@ impl<'a> StyleSheetParser<'a> {
} }
AtRule::For(f) => rules.extend(f.ruleset_eval(&mut Scope::new(), &Selector::new())?), AtRule::For(f) => rules.extend(f.ruleset_eval(&mut Scope::new(), &Selector::new())?),
AtRule::While(w) => rules.extend(w.ruleset_eval(&mut Scope::new(), &Selector::new(), true)?), AtRule::While(w) => rules.extend(w.ruleset_eval(&mut Scope::new(), &Selector::new(), true)?),
AtRule::Include(s) AtRule::Each(e) => {
| AtRule::Each(s) => rules.extend(s), rules.extend(e.ruleset_eval(&mut Scope::new(), &Selector::new())?)
}
AtRule::Include(s) => rules.extend(s),
AtRule::Content => return Err( AtRule::Content => return Err(
("@content is only allowed within mixin declarations.", rule.span ("@content is only allowed within mixin declarations.", rule.span
).into()), ).into()),
@ -461,7 +463,8 @@ impl<'a> StyleSheetParser<'a> {
AtRule::While(w) => { AtRule::While(w) => {
stmts.extend(w.ruleset_eval(scope, super_selector, false)?) stmts.extend(w.ruleset_eval(scope, super_selector, false)?)
} }
AtRule::Include(s) | AtRule::Each(s) => stmts.extend(s), AtRule::Each(e) => stmts.extend(e.ruleset_eval(scope, super_selector)?),
AtRule::Include(s) => stmts.extend(s),
AtRule::If(i) => stmts.extend(i.eval(scope, super_selector)?), AtRule::If(i) => stmts.extend(i.eval(scope, super_selector)?),
AtRule::Content => { AtRule::Content => {
return Err(( return Err((

View File

@ -1,6 +1,8 @@
//! # Convert from SCSS AST to CSS //! # Convert from SCSS AST to CSS
use std::io::Write; use std::io::Write;
use codemap::{CodeMap, Span};
use crate::atrule::AtRule; use crate::atrule::AtRule;
use crate::error::SassResult; use crate::error::SassResult;
use crate::{RuleSet, Selector, Stmt, Style, StyleSheet}; use crate::{RuleSet, Selector, Stmt, Style, StyleSheet};
@ -123,9 +125,9 @@ impl Css {
Ok(self) Ok(self)
} }
pub fn pretty_print(self) -> SassResult<String> { pub fn pretty_print(self, map: &CodeMap) -> SassResult<String> {
let mut string = Vec::new(); let mut string = Vec::new();
self._inner_pretty_print(&mut string, 0)?; self._inner_pretty_print(&mut string, map, 0)?;
if string.iter().any(|s| !s.is_ascii()) { if string.iter().any(|s| !s.is_ascii()) {
return Ok(format!( return Ok(format!(
"@charset \"UTF-8\";\n{}", "@charset \"UTF-8\";\n{}",
@ -135,7 +137,33 @@ impl Css {
Ok(String::from_utf8(string)?) Ok(String::from_utf8(string)?)
} }
fn _inner_pretty_print(self, buf: &mut Vec<u8>, nesting: usize) -> SassResult<()> { fn debug(map: &CodeMap, span: Span, message: &str) {
let loc = map.look_up_span(span);
eprintln!(
"{}:{} Debug: {}",
loc.file.name(),
loc.begin.line + 1,
message
);
}
fn warn(map: &CodeMap, span: Span, message: &str) {
let loc = map.look_up_span(span);
eprintln!(
"Warning: {}\n {} {}:{} root stylesheet",
message,
loc.file.name(),
loc.begin.line + 1,
loc.begin.column + 1
);
}
fn _inner_pretty_print(
self,
buf: &mut Vec<u8>,
map: &CodeMap,
nesting: usize,
) -> SassResult<()> {
let mut has_written = false; let mut has_written = false;
let padding = vec![' '; nesting * 2].iter().collect::<String>(); let padding = vec![' '; nesting * 2].iter().collect::<String>();
for block in self.blocks { for block in self.blocks {
@ -155,7 +183,8 @@ impl Css {
has_written = true; has_written = true;
writeln!(buf, "{}/*{}*/", padding, s)?; writeln!(buf, "{}/*{}*/", padding, s)?;
} }
Toplevel::AtRule(r) => match r { Toplevel::AtRule(r) => {
match r {
AtRule::Unknown(u) => { AtRule::Unknown(u) => {
if u.body.is_empty() { if u.body.is_empty() {
continue; continue;
@ -166,11 +195,14 @@ impl Css {
writeln!(buf, "{}@{} {} {{", padding, u.name, u.params)?; writeln!(buf, "{}@{} {} {{", padding, u.name, u.params)?;
} }
Css::from_stylesheet(StyleSheet::from_stmts(u.body))? Css::from_stylesheet(StyleSheet::from_stmts(u.body))?
._inner_pretty_print(buf, nesting + 1)?; ._inner_pretty_print(buf, map, nesting + 1)?;
writeln!(buf, "{}}}", padding)?; writeln!(buf, "{}}}", padding)?;
} }
AtRule::Debug(e) => Self::debug(map, e.span, &e.node),
AtRule::Warn(e) => Self::warn(map, e.span, &e.node),
_ => todo!("at-rule other than unknown at toplevel: {:?}", r), _ => todo!("at-rule other than unknown at toplevel: {:?}", r),
}, }
}
Toplevel::Style(s) => { Toplevel::Style(s) => {
writeln!(buf, "{}{}", padding, s.to_string()?)?; writeln!(buf, "{}{}", padding, s.to_string()?)?;
} }