handle special functions calc, element, expression, and progid
This commit is contained in:
parent
ce346077f9
commit
7e793392a7
77
src/value/css_function.rs
Normal file
77
src/value/css_function.rs
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
use std::iter::Peekable;
|
||||||
|
|
||||||
|
use crate::error::SassResult;
|
||||||
|
use crate::scope::Scope;
|
||||||
|
use crate::selector::Selector;
|
||||||
|
use crate::utils::{devour_whitespace, parse_interpolation};
|
||||||
|
use crate::Token;
|
||||||
|
|
||||||
|
pub(crate) fn eat_calc_args<I: Iterator<Item = Token>>(
|
||||||
|
toks: &mut Peekable<I>,
|
||||||
|
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 == '{' {
|
||||||
|
toks.next();
|
||||||
|
string.push_str(&parse_interpolation(toks, scope, super_selector)?.to_string());
|
||||||
|
} else {
|
||||||
|
string.push('#');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'(' => {
|
||||||
|
nesting += 1;
|
||||||
|
string.push('(');
|
||||||
|
}
|
||||||
|
')' => {
|
||||||
|
if nesting == 0 {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
nesting -= 1;
|
||||||
|
string.push(')');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c => string.push(c),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
string.push(')');
|
||||||
|
Ok(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
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>>(
|
||||||
|
toks: &mut Peekable<I>,
|
||||||
|
scope: &Scope,
|
||||||
|
super_selector: &Selector,
|
||||||
|
) -> SassResult<String> {
|
||||||
|
let mut string = String::new();
|
||||||
|
while let Some(tok) = toks.next() {
|
||||||
|
match tok.kind {
|
||||||
|
'a'..='z' | 'A'..='Z' | '.' => {
|
||||||
|
string.push(tok.kind);
|
||||||
|
}
|
||||||
|
'(' => {
|
||||||
|
string.push_str(&eat_calc_args(toks, scope, super_selector)?);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_ => return Err("expected \"(\".".into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(string)
|
||||||
|
}
|
@ -11,6 +11,7 @@ pub(crate) use map::SassMap;
|
|||||||
pub(crate) use number::Number;
|
pub(crate) use number::Number;
|
||||||
pub(crate) use sass_function::SassFunction;
|
pub(crate) use sass_function::SassFunction;
|
||||||
|
|
||||||
|
mod css_function;
|
||||||
mod map;
|
mod map;
|
||||||
mod number;
|
mod number;
|
||||||
mod ops;
|
mod ops;
|
||||||
|
@ -6,6 +6,8 @@ use num_bigint::BigInt;
|
|||||||
use num_rational::BigRational;
|
use num_rational::BigRational;
|
||||||
use num_traits::pow;
|
use num_traits::pow;
|
||||||
|
|
||||||
|
use super::css_function::{eat_calc_args, eat_progid};
|
||||||
|
|
||||||
use crate::args::eat_call_args;
|
use crate::args::eat_call_args;
|
||||||
use crate::builtin::GLOBAL_FUNCTIONS;
|
use crate::builtin::GLOBAL_FUNCTIONS;
|
||||||
use crate::color::Color;
|
use crate::color::Color;
|
||||||
@ -360,6 +362,12 @@ impl Value {
|
|||||||
super_selector: &Selector,
|
super_selector: &Selector,
|
||||||
) -> SassResult<IntermediateValue> {
|
) -> SassResult<IntermediateValue> {
|
||||||
let mut s = eat_ident(toks, scope, super_selector)?;
|
let mut s = eat_ident(toks, scope, super_selector)?;
|
||||||
|
if s == "progid" && toks.peek().is_some() && toks.peek().unwrap().kind == ':' {
|
||||||
|
toks.next();
|
||||||
|
s.push(':');
|
||||||
|
s.push_str(&eat_progid(toks, scope, super_selector)?);
|
||||||
|
return Ok(IntermediateValue::Value(Value::Ident(s, QuoteKind::None)));
|
||||||
|
}
|
||||||
match toks.peek() {
|
match toks.peek() {
|
||||||
Some(Token { kind: '(', .. }) => {
|
Some(Token { kind: '(', .. }) => {
|
||||||
toks.next();
|
toks.next();
|
||||||
@ -374,10 +382,18 @@ impl Value {
|
|||||||
)?))
|
)?))
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
s.push_str(
|
match s.as_str() {
|
||||||
|
"calc" | "element" | "expression" => {
|
||||||
|
s.push_str(&eat_calc_args(toks, scope, super_selector)?)
|
||||||
|
}
|
||||||
|
// "min" => {}
|
||||||
|
// "max" => {}
|
||||||
|
// "url" => {}
|
||||||
|
_ => s.push_str(
|
||||||
&eat_call_args(toks, scope, super_selector)?
|
&eat_call_args(toks, scope, super_selector)?
|
||||||
.to_css_string(scope, super_selector)?,
|
.to_css_string(scope, super_selector)?,
|
||||||
);
|
),
|
||||||
|
}
|
||||||
return Ok(IntermediateValue::Value(Value::Ident(s, QuoteKind::None)));
|
return Ok(IntermediateValue::Value(Value::Ident(s, QuoteKind::None)));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
134
tests/special-functions.rs
Normal file
134
tests/special-functions.rs
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
#![cfg(test)]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod macros;
|
||||||
|
|
||||||
|
test!(
|
||||||
|
calc_whitespace,
|
||||||
|
"a {\n color: calc( 1 );\n}\n",
|
||||||
|
"a {\n color: calc( 1 );\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
calc_multiple_args,
|
||||||
|
"a {\n color: calc(1, 2, a, b, c);\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
calc_does_not_evaluate_arithmetic,
|
||||||
|
"a {\n color: calc(1 + 2);\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
calc_evaluates_interpolated_arithmetic,
|
||||||
|
"a {\n color: calc(#{1 + 2});\n}\n",
|
||||||
|
"a {\n color: calc(3);\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
#[ignore]
|
||||||
|
calc_retains_silent_comment,
|
||||||
|
"a {\n color: calc(//);\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
calc_retains_multiline_comment,
|
||||||
|
"a {\n color: calc(/**/);\n}\n"
|
||||||
|
);
|
||||||
|
test!(calc_nested_parens, "a {\n color: calc((((()))));\n}\n");
|
||||||
|
test!(
|
||||||
|
element_whitespace,
|
||||||
|
"a {\n color: element( 1 );\n}\n",
|
||||||
|
"a {\n color: element( 1 );\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
element_multiple_args,
|
||||||
|
"a {\n color: element(1, 2, a, b, c);\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
element_does_not_evaluate_arithmetic,
|
||||||
|
"a {\n color: element(1 + 2);\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
element_evaluates_interpolated_arithmetic,
|
||||||
|
"a {\n color: element(#{1 + 2});\n}\n",
|
||||||
|
"a {\n color: element(3);\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
#[ignore]
|
||||||
|
element_retains_silent_comment,
|
||||||
|
"a {\n color: element(//);\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
element_retains_multiline_comment,
|
||||||
|
"a {\n color: element(/**/);\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
element_nested_parens,
|
||||||
|
"a {\n color: element((((()))));\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
expression_whitespace,
|
||||||
|
"a {\n color: expression( 1 );\n}\n",
|
||||||
|
"a {\n color: expression( 1 );\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
expression_multiple_args,
|
||||||
|
"a {\n color: expression(1, 2, a, b, c);\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
expression_does_not_evaluate_arithmetic,
|
||||||
|
"a {\n color: expression(1 + 2);\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
expression_evaluates_interpolated_arithmetic,
|
||||||
|
"a {\n color: expression(#{1 + 2});\n}\n",
|
||||||
|
"a {\n color: expression(3);\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
#[ignore]
|
||||||
|
expression_retains_silent_comment,
|
||||||
|
"a {\n color: expression(//);\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
expression_retains_multiline_comment,
|
||||||
|
"a {\n color: expression(/**/);\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
expression_nested_parens,
|
||||||
|
"a {\n color: expression((((()))));\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
progid_whitespace,
|
||||||
|
"a {\n color: progid:( 1 );\n}\n",
|
||||||
|
"a {\n color: progid:( 1 );\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
progid_multiple_args,
|
||||||
|
"a {\n color: progid:(1, 2, a, b, c);\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
progid_does_not_evaluate_arithmetic,
|
||||||
|
"a {\n color: progid:(1 + 2);\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
progid_evaluates_interpolated_arithmetic,
|
||||||
|
"a {\n color: progid:(#{1 + 2});\n}\n",
|
||||||
|
"a {\n color: progid:(3);\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
#[ignore]
|
||||||
|
progid_retains_silent_comment,
|
||||||
|
"a {\n color: progid:(//);\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
progid_retains_multiline_comment,
|
||||||
|
"a {\n color: progid:(/**/);\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
progid_nested_parens,
|
||||||
|
"a {\n color: progid:((((()))));\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
progid_values_after_colon,
|
||||||
|
"a {\n color: progid:apple.bottoM..jeans.boots();\n}\n"
|
||||||
|
);
|
||||||
|
error!(
|
||||||
|
progid_number_after_colon,
|
||||||
|
"a {\n color: progid:ap1ple.bottoM..jeans.boots();\n}\n", "Error: expected \"(\"."
|
||||||
|
);
|
Loading…
x
Reference in New Issue
Block a user