2020-04-20 14:40:51 -04:00
|
|
|
use std::iter::Iterator;
|
|
|
|
|
|
|
|
use codemap::Spanned;
|
|
|
|
|
|
|
|
use peekmore::PeekMoreIterator;
|
|
|
|
|
|
|
|
use crate::error::SassResult;
|
|
|
|
use crate::selector::Selector;
|
|
|
|
use crate::{Scope, Token};
|
|
|
|
|
2020-04-20 14:53:52 -04:00
|
|
|
use super::parse_interpolation;
|
2020-04-20 14:40:51 -04:00
|
|
|
|
|
|
|
pub(crate) trait IsWhitespace {
|
|
|
|
fn is_whitespace(&self) -> bool;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl IsWhitespace for char {
|
|
|
|
fn is_whitespace(&self) -> bool {
|
|
|
|
self.is_ascii_whitespace()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn devour_whitespace<I: Iterator<Item = W>, W: IsWhitespace>(
|
|
|
|
s: &mut PeekMoreIterator<I>,
|
|
|
|
) -> bool {
|
|
|
|
let mut found_whitespace = false;
|
|
|
|
while let Some(w) = s.peek() {
|
|
|
|
if !w.is_whitespace() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
found_whitespace = true;
|
|
|
|
s.next();
|
|
|
|
}
|
|
|
|
found_whitespace
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn devour_whitespace_or_comment<I: Iterator<Item = Token>>(
|
|
|
|
toks: &mut PeekMoreIterator<I>,
|
|
|
|
) -> SassResult<bool> {
|
|
|
|
let mut found_whitespace = false;
|
|
|
|
while let Some(tok) = toks.peek() {
|
|
|
|
if tok.kind == '/' {
|
2020-04-23 19:32:32 -04:00
|
|
|
let next = match toks.peek_forward(1) {
|
|
|
|
Some(v) => v,
|
|
|
|
None => return Ok(found_whitespace),
|
|
|
|
};
|
|
|
|
match next.kind {
|
2020-04-20 14:40:51 -04:00
|
|
|
'*' => {
|
2020-04-23 19:32:32 -04:00
|
|
|
toks.next();
|
2020-04-20 14:40:51 -04:00
|
|
|
eat_comment(toks, &Scope::new(), &Selector::new())?;
|
|
|
|
}
|
|
|
|
'/' => read_until_newline(toks),
|
2020-04-23 19:32:32 -04:00
|
|
|
_ => {
|
|
|
|
toks.reset_view();
|
2020-04-23 19:44:20 -04:00
|
|
|
return Ok(found_whitespace);
|
|
|
|
}
|
2020-04-20 14:40:51 -04:00
|
|
|
};
|
|
|
|
found_whitespace = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if !tok.is_whitespace() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
found_whitespace = true;
|
|
|
|
toks.next();
|
|
|
|
}
|
|
|
|
Ok(found_whitespace)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Eat and return the contents of a comment.
|
|
|
|
///
|
|
|
|
/// This function assumes that the starting "/*" has already been consumed
|
|
|
|
/// The entirety of the comment, including the ending "*/" is consumed.
|
|
|
|
/// Note that the ending "*/" is not included in the output.
|
|
|
|
pub(crate) fn eat_comment<I: Iterator<Item = Token>>(
|
|
|
|
toks: &mut PeekMoreIterator<I>,
|
|
|
|
scope: &Scope,
|
|
|
|
super_selector: &Selector,
|
|
|
|
) -> SassResult<Spanned<String>> {
|
|
|
|
let mut comment = String::new();
|
|
|
|
let mut span = if let Some(tok) = toks.peek() {
|
|
|
|
tok.pos()
|
|
|
|
} else {
|
|
|
|
todo!()
|
|
|
|
};
|
|
|
|
while let Some(tok) = toks.next() {
|
|
|
|
span = span.merge(tok.pos());
|
|
|
|
if tok.kind == '*' && toks.peek().unwrap().kind == '/' {
|
|
|
|
toks.next();
|
|
|
|
break;
|
|
|
|
} else if tok.kind == '#' && toks.peek().unwrap().kind == '{' {
|
|
|
|
toks.next();
|
|
|
|
comment
|
|
|
|
.push_str(&parse_interpolation(toks, scope, super_selector)?.to_css_string(span)?);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
comment.push(tok.kind);
|
|
|
|
}
|
|
|
|
devour_whitespace(toks);
|
|
|
|
Ok(Spanned {
|
|
|
|
node: comment,
|
|
|
|
span,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Eat tokens until a newline
|
|
|
|
///
|
|
|
|
/// This exists largely to eat silent comments, "//"
|
|
|
|
/// We only have to check for \n as the lexing step normalizes all newline characters
|
|
|
|
///
|
|
|
|
/// The newline is consumed
|
|
|
|
pub(crate) fn read_until_newline<I: Iterator<Item = Token>>(toks: &mut PeekMoreIterator<I>) {
|
|
|
|
for tok in toks {
|
|
|
|
if tok.kind == '\n' {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|