2020-04-20 03:45:28 -04:00
|
|
|
use peekmore::PeekMoreIterator;
|
2020-04-06 19:26:47 -04:00
|
|
|
|
|
|
|
use crate::error::SassResult;
|
|
|
|
use crate::scope::Scope;
|
|
|
|
use crate::selector::Selector;
|
2020-04-23 21:30:25 -04:00
|
|
|
use crate::utils::{
|
|
|
|
devour_whitespace, parse_interpolation, peek_escape, peek_until_closing_curly_brace,
|
2020-04-24 19:00:06 -04:00
|
|
|
peek_whitespace,
|
2020-04-23 21:30:25 -04:00
|
|
|
};
|
2020-04-06 19:26:47 -04:00
|
|
|
use crate::Token;
|
|
|
|
|
|
|
|
pub(crate) fn eat_calc_args<I: Iterator<Item = Token>>(
|
2020-04-20 03:45:28 -04:00
|
|
|
toks: &mut PeekMoreIterator<I>,
|
2020-04-06 19:26:47 -04:00
|
|
|
scope: &Scope,
|
|
|
|
super_selector: &Selector,
|
|
|
|
) -> SassResult<String> {
|
|
|
|
let mut string = String::from("(");
|
|
|
|
let mut nesting = 0;
|
|
|
|
while let Some(tok) = toks.next() {
|
|
|
|
match tok.kind {
|
|
|
|
' ' | '\t' | '\n' => {
|
|
|
|
devour_whitespace(toks);
|
|
|
|
string.push(' ');
|
|
|
|
}
|
|
|
|
'#' => {
|
|
|
|
if toks.peek().is_some() && toks.peek().unwrap().kind == '{' {
|
2020-04-12 19:37:12 -04:00
|
|
|
let span = toks.next().unwrap().pos();
|
|
|
|
string.push_str(
|
|
|
|
&parse_interpolation(toks, scope, super_selector)?.to_css_string(span)?,
|
|
|
|
);
|
2020-04-06 19:26:47 -04:00
|
|
|
} else {
|
|
|
|
string.push('#');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
'(' => {
|
|
|
|
nesting += 1;
|
|
|
|
string.push('(');
|
|
|
|
}
|
|
|
|
')' => {
|
|
|
|
if nesting == 0 {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
nesting -= 1;
|
|
|
|
string.push(')');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
c => string.push(c),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
string.push(')');
|
|
|
|
Ok(string)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn is_special_function(s: &str) -> bool {
|
|
|
|
s.starts_with("calc(")
|
|
|
|
|| s.starts_with("var(")
|
|
|
|
|| s.starts_with("env(")
|
|
|
|
|| s.starts_with("min(")
|
|
|
|
|| s.starts_with("max(")
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn eat_progid<I: Iterator<Item = Token>>(
|
2020-04-20 03:45:28 -04:00
|
|
|
toks: &mut PeekMoreIterator<I>,
|
2020-04-06 19:26:47 -04:00
|
|
|
scope: &Scope,
|
|
|
|
super_selector: &Selector,
|
|
|
|
) -> SassResult<String> {
|
|
|
|
let mut string = String::new();
|
2020-04-12 19:37:12 -04:00
|
|
|
let mut span = toks.peek().unwrap().pos();
|
2020-04-06 19:26:47 -04:00
|
|
|
while let Some(tok) = toks.next() {
|
2020-04-12 19:37:12 -04:00
|
|
|
span = span.merge(tok.pos());
|
2020-04-06 19:26:47 -04:00
|
|
|
match tok.kind {
|
|
|
|
'a'..='z' | 'A'..='Z' | '.' => {
|
|
|
|
string.push(tok.kind);
|
|
|
|
}
|
|
|
|
'(' => {
|
|
|
|
string.push_str(&eat_calc_args(toks, scope, super_selector)?);
|
|
|
|
break;
|
|
|
|
}
|
2020-04-12 19:37:12 -04:00
|
|
|
_ => return Err(("expected \"(\".", span).into()),
|
2020-04-06 19:26:47 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(string)
|
|
|
|
}
|
2020-04-21 04:20:35 -04:00
|
|
|
|
|
|
|
pub(crate) fn try_eat_url<I: Iterator<Item = Token>>(
|
|
|
|
toks: &mut PeekMoreIterator<I>,
|
|
|
|
scope: &Scope,
|
|
|
|
super_selector: &Selector,
|
|
|
|
) -> SassResult<Option<String>> {
|
|
|
|
let mut buf = String::from("url(");
|
|
|
|
let mut peek_counter = 0;
|
2020-04-24 19:00:06 -04:00
|
|
|
peek_counter += peek_whitespace(toks);
|
2020-04-21 04:20:35 -04:00
|
|
|
while let Some(tok) = toks.peek() {
|
|
|
|
let kind = tok.kind;
|
|
|
|
toks.move_forward(1);
|
|
|
|
peek_counter += 1;
|
|
|
|
if kind == '!'
|
|
|
|
|| kind == '%'
|
|
|
|
|| kind == '&'
|
|
|
|
|| (kind >= '*' && kind <= '~')
|
|
|
|
|| kind as u32 >= 0x0080
|
|
|
|
{
|
|
|
|
buf.push(kind);
|
|
|
|
} else if kind == '\\' {
|
|
|
|
buf.push_str(&peek_escape(toks)?);
|
|
|
|
} else if kind == '#' {
|
|
|
|
let next = toks.peek();
|
|
|
|
if next.is_some() && next.unwrap().kind == '{' {
|
|
|
|
toks.move_forward(1);
|
|
|
|
peek_counter += 1;
|
|
|
|
let (interpolation, count) = peek_interpolation(toks, scope, super_selector)?;
|
|
|
|
peek_counter += count;
|
|
|
|
buf.push_str(&match interpolation.node {
|
|
|
|
Value::Ident(s, ..) => s,
|
|
|
|
v => v.to_css_string(interpolation.span)?,
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
buf.push('#');
|
|
|
|
}
|
|
|
|
} else if kind == ')' {
|
|
|
|
buf.push(')');
|
2020-04-24 19:00:06 -04:00
|
|
|
toks.take(peek_counter).for_each(drop);
|
2020-04-21 04:20:35 -04:00
|
|
|
return Ok(Some(buf));
|
2020-04-24 19:00:06 -04:00
|
|
|
} else if kind.is_whitespace() {
|
|
|
|
peek_counter += peek_whitespace(toks);
|
|
|
|
let next = match toks.peek() {
|
|
|
|
Some(v) => v,
|
|
|
|
None => break,
|
|
|
|
};
|
|
|
|
if next.kind == ')' {
|
|
|
|
buf.push(')');
|
|
|
|
toks.take(peek_counter + 1).for_each(drop);
|
|
|
|
return Ok(Some(buf));
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
2020-04-21 04:20:35 -04:00
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
toks.reset_view();
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
|
|
|
|
use crate::value::Value;
|
|
|
|
use codemap::Spanned;
|
|
|
|
|
|
|
|
fn peek_interpolation<I: Iterator<Item = Token>>(
|
|
|
|
toks: &mut PeekMoreIterator<I>,
|
|
|
|
scope: &Scope,
|
|
|
|
super_selector: &Selector,
|
|
|
|
) -> SassResult<(Spanned<Value>, usize)> {
|
|
|
|
let vec = peek_until_closing_curly_brace(toks);
|
|
|
|
let peek_counter = vec.len();
|
|
|
|
toks.move_forward(1);
|
|
|
|
let val = Value::from_vec(vec, scope, super_selector)?;
|
|
|
|
Ok((
|
|
|
|
Spanned {
|
|
|
|
node: val.node.eval(val.span)?.node.unquote(),
|
|
|
|
span: val.span,
|
|
|
|
},
|
|
|
|
peek_counter,
|
|
|
|
))
|
|
|
|
}
|