2020-05-18 11:50:40 -04:00
|
|
|
use super::{Builtin, GlobalFunctionMap, GLOBAL_FUNCTIONS};
|
2020-02-02 21:09:29 -05:00
|
|
|
|
2020-04-12 19:37:12 -04:00
|
|
|
use codemap::Spanned;
|
|
|
|
|
2020-06-16 20:00:11 -04:00
|
|
|
use crate::{
|
|
|
|
args::CallArgs,
|
|
|
|
common::QuoteKind,
|
|
|
|
error::SassResult,
|
|
|
|
parse::Parser,
|
|
|
|
unit::Unit,
|
|
|
|
value::{SassFunction, Value},
|
|
|
|
};
|
2020-02-02 22:33:04 -05:00
|
|
|
|
2020-06-16 19:38:30 -04:00
|
|
|
fn if_(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
2020-04-30 19:36:34 -04:00
|
|
|
args.max_args(3)?;
|
2020-06-16 19:38:30 -04:00
|
|
|
if parser
|
|
|
|
.arg(&mut args, 0, "condition")?
|
|
|
|
.is_true(args.span())?
|
|
|
|
{
|
|
|
|
Ok(parser.arg(&mut args, 1, "if-true")?)
|
2020-04-30 19:36:34 -04:00
|
|
|
} else {
|
2020-06-16 19:38:30 -04:00
|
|
|
Ok(parser.arg(&mut args, 2, "if-false")?)
|
2020-04-30 19:36:34 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-16 19:38:30 -04:00
|
|
|
fn feature_exists(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
2020-04-30 19:36:34 -04:00
|
|
|
args.max_args(1)?;
|
2020-06-16 19:38:30 -04:00
|
|
|
match parser.arg(&mut args, 0, "feature")? {
|
2020-05-24 17:41:24 -04:00
|
|
|
#[allow(clippy::match_same_arms)]
|
2020-05-22 14:35:41 -04:00
|
|
|
Value::String(s, _) => Ok(match s.as_str() {
|
2020-04-30 19:36:34 -04:00
|
|
|
// A local variable will shadow a global variable unless
|
|
|
|
// `!global` is used.
|
|
|
|
"global-variable-shadowing" => Value::True,
|
|
|
|
// the @extend rule will affect selectors nested in pseudo-classes
|
|
|
|
// like :not()
|
2020-06-23 02:43:28 -04:00
|
|
|
"extend-selector-pseudoclass" => Value::True,
|
2020-04-30 19:36:34 -04:00
|
|
|
// Full support for unit arithmetic using units defined in the
|
|
|
|
// [Values and Units Level 3][] spec.
|
|
|
|
"units-level-3" => Value::True,
|
|
|
|
// The Sass `@error` directive is supported.
|
|
|
|
"at-error" => Value::True,
|
|
|
|
// The "Custom Properties Level 1" spec is supported. This means
|
|
|
|
// that custom properties are parsed statically, with only
|
|
|
|
// interpolation treated as SassScript.
|
|
|
|
"custom-property" => Value::False,
|
|
|
|
_ => Value::False,
|
|
|
|
}),
|
|
|
|
v => Err((
|
|
|
|
format!(
|
|
|
|
"$feature: {} is not a string.",
|
|
|
|
v.to_css_string(args.span())?
|
|
|
|
),
|
|
|
|
args.span(),
|
|
|
|
)
|
|
|
|
.into()),
|
2020-04-30 18:31:55 -04:00
|
|
|
}
|
2020-04-30 19:36:34 -04:00
|
|
|
}
|
2020-04-30 18:31:55 -04:00
|
|
|
|
2020-06-16 19:38:30 -04:00
|
|
|
fn unit(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
2020-04-30 19:36:34 -04:00
|
|
|
args.max_args(1)?;
|
2020-06-16 19:38:30 -04:00
|
|
|
let unit = match parser.arg(&mut args, 0, "number")? {
|
2020-04-30 19:36:34 -04:00
|
|
|
Value::Dimension(_, u) => u.to_string(),
|
|
|
|
v => {
|
|
|
|
return Err((
|
2020-04-30 18:31:55 -04:00
|
|
|
format!(
|
2020-04-30 19:36:34 -04:00
|
|
|
"$number: {} is not a number.",
|
2020-04-30 18:31:55 -04:00
|
|
|
v.to_css_string(args.span())?
|
|
|
|
),
|
|
|
|
args.span(),
|
|
|
|
)
|
2020-04-30 19:36:34 -04:00
|
|
|
.into())
|
2020-04-30 18:31:55 -04:00
|
|
|
}
|
2020-04-30 19:36:34 -04:00
|
|
|
};
|
2020-05-22 14:35:41 -04:00
|
|
|
Ok(Value::String(unit, QuoteKind::Quoted))
|
2020-04-30 19:36:34 -04:00
|
|
|
}
|
2020-04-30 18:31:55 -04:00
|
|
|
|
2020-06-16 19:38:30 -04:00
|
|
|
fn type_of(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
2020-04-30 19:36:34 -04:00
|
|
|
args.max_args(1)?;
|
2020-06-16 19:38:30 -04:00
|
|
|
let value = parser.arg(&mut args, 0, "value")?;
|
2020-05-22 14:35:41 -04:00
|
|
|
Ok(Value::String(
|
2020-05-22 14:20:31 -04:00
|
|
|
value.kind(args.span())?.to_owned(),
|
2020-04-30 19:36:34 -04:00
|
|
|
QuoteKind::None,
|
|
|
|
))
|
|
|
|
}
|
2020-04-30 18:31:55 -04:00
|
|
|
|
2020-06-16 19:38:30 -04:00
|
|
|
fn unitless(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
2020-04-30 19:36:34 -04:00
|
|
|
args.max_args(1)?;
|
2020-05-31 05:32:19 -04:00
|
|
|
#[allow(clippy::match_same_arms)]
|
2020-06-16 19:38:30 -04:00
|
|
|
Ok(match parser.arg(&mut args, 0, "number")? {
|
2020-05-24 17:41:24 -04:00
|
|
|
Value::Dimension(_, Unit::None) => Value::True,
|
|
|
|
Value::Dimension(_, _) => Value::False,
|
|
|
|
_ => Value::True,
|
|
|
|
})
|
2020-04-30 19:36:34 -04:00
|
|
|
}
|
|
|
|
|
2020-06-16 19:38:30 -04:00
|
|
|
fn inspect(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
2020-04-30 19:36:34 -04:00
|
|
|
args.max_args(1)?;
|
2020-05-22 14:35:41 -04:00
|
|
|
Ok(Value::String(
|
2020-06-16 19:38:30 -04:00
|
|
|
parser
|
|
|
|
.arg(&mut args, 0, "value")?
|
2020-05-22 14:20:31 -04:00
|
|
|
.inspect(args.span())?
|
2020-05-25 13:09:20 -04:00
|
|
|
.into_owned(),
|
2020-04-30 19:36:34 -04:00
|
|
|
QuoteKind::None,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2020-06-16 19:38:30 -04:00
|
|
|
fn variable_exists(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
2020-04-30 19:36:34 -04:00
|
|
|
args.max_args(1)?;
|
2020-06-16 19:38:30 -04:00
|
|
|
match parser.arg(&mut args, 0, "name")? {
|
|
|
|
Value::String(s, _) => Ok(Value::bool(
|
|
|
|
parser
|
|
|
|
.scopes
|
|
|
|
.last()
|
|
|
|
.var_exists(&s.into(), parser.global_scope),
|
|
|
|
)),
|
2020-04-30 19:36:34 -04:00
|
|
|
v => Err((
|
|
|
|
format!("$name: {} is not a string.", v.to_css_string(args.span())?),
|
|
|
|
args.span(),
|
|
|
|
)
|
|
|
|
.into()),
|
2020-04-30 18:31:55 -04:00
|
|
|
}
|
2020-04-30 19:36:34 -04:00
|
|
|
}
|
2020-04-30 18:31:55 -04:00
|
|
|
|
2020-06-16 19:38:30 -04:00
|
|
|
fn global_variable_exists(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
2020-04-30 19:36:34 -04:00
|
|
|
args.max_args(1)?;
|
2020-06-16 19:38:30 -04:00
|
|
|
match parser.arg(&mut args, 0, "name")? {
|
|
|
|
Value::String(s, _) => Ok(Value::bool(
|
|
|
|
parser.global_scope.var_exists_no_global(&s.into()),
|
|
|
|
)),
|
2020-04-30 19:36:34 -04:00
|
|
|
v => Err((
|
|
|
|
format!("$name: {} is not a string.", v.to_css_string(args.span())?),
|
|
|
|
args.span(),
|
|
|
|
)
|
|
|
|
.into()),
|
2020-04-30 18:31:55 -04:00
|
|
|
}
|
2020-04-30 19:36:34 -04:00
|
|
|
}
|
2020-04-30 18:31:55 -04:00
|
|
|
|
2020-06-16 19:38:30 -04:00
|
|
|
fn mixin_exists(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
2020-04-30 19:36:34 -04:00
|
|
|
args.max_args(2)?;
|
2020-06-16 19:38:30 -04:00
|
|
|
match parser.arg(&mut args, 0, "name")? {
|
|
|
|
Value::String(s, _) => Ok(Value::bool(
|
|
|
|
parser.scopes.last().mixin_exists(&s, parser.global_scope),
|
|
|
|
)),
|
2020-04-30 19:36:34 -04:00
|
|
|
v => Err((
|
|
|
|
format!("$name: {} is not a string.", v.to_css_string(args.span())?),
|
|
|
|
args.span(),
|
|
|
|
)
|
|
|
|
.into()),
|
2020-04-30 18:31:55 -04:00
|
|
|
}
|
2020-04-30 19:36:34 -04:00
|
|
|
}
|
2020-04-30 18:31:55 -04:00
|
|
|
|
2020-06-16 19:38:30 -04:00
|
|
|
fn function_exists(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
2020-04-30 19:36:34 -04:00
|
|
|
args.max_args(2)?;
|
2020-06-16 19:38:30 -04:00
|
|
|
match parser.arg(&mut args, 0, "name")? {
|
2020-05-22 14:35:41 -04:00
|
|
|
Value::String(s, _) => Ok(Value::bool(
|
2020-06-16 19:38:30 -04:00
|
|
|
parser.scopes.last().fn_exists(&s, parser.global_scope),
|
2020-05-22 14:20:31 -04:00
|
|
|
)),
|
2020-04-30 19:36:34 -04:00
|
|
|
v => Err((
|
|
|
|
format!("$name: {} is not a string.", v.to_css_string(args.span())?),
|
|
|
|
args.span(),
|
|
|
|
)
|
|
|
|
.into()),
|
2020-04-30 18:31:55 -04:00
|
|
|
}
|
2020-04-30 19:36:34 -04:00
|
|
|
}
|
2020-04-30 18:31:55 -04:00
|
|
|
|
2020-06-16 19:38:30 -04:00
|
|
|
fn get_function(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
2020-04-30 19:36:34 -04:00
|
|
|
args.max_args(3)?;
|
2020-06-16 19:38:30 -04:00
|
|
|
let name = match parser.arg(&mut args, 0, "name")? {
|
2020-05-22 14:35:41 -04:00
|
|
|
Value::String(s, _) => s,
|
2020-04-30 19:36:34 -04:00
|
|
|
v => {
|
|
|
|
return Err((
|
2020-04-30 18:31:55 -04:00
|
|
|
format!("$name: {} is not a string.", v.to_css_string(args.span())?),
|
|
|
|
args.span(),
|
|
|
|
)
|
2020-04-30 19:36:34 -04:00
|
|
|
.into())
|
2020-04-30 18:31:55 -04:00
|
|
|
}
|
2020-04-30 19:36:34 -04:00
|
|
|
};
|
2020-06-16 19:38:30 -04:00
|
|
|
let css = parser
|
|
|
|
.default_arg(&mut args, 1, "css", Value::False)?
|
|
|
|
.is_true(args.span())?;
|
|
|
|
let module = match parser.default_arg(&mut args, 2, "module", Value::Null)? {
|
2020-05-22 14:35:41 -04:00
|
|
|
Value::String(s, ..) => Some(s),
|
2020-04-30 19:36:34 -04:00
|
|
|
Value::Null => None,
|
|
|
|
v => {
|
|
|
|
return Err((
|
|
|
|
format!(
|
|
|
|
"$module: {} is not a string.",
|
|
|
|
v.to_css_string(args.span())?
|
|
|
|
),
|
2020-04-30 18:31:55 -04:00
|
|
|
args.span(),
|
|
|
|
)
|
2020-04-30 19:36:34 -04:00
|
|
|
.into())
|
2020-04-30 18:31:55 -04:00
|
|
|
}
|
2020-04-30 19:36:34 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
if module.is_some() && css {
|
|
|
|
return Err((
|
|
|
|
"$css and $module may not both be passed at once.",
|
|
|
|
args.span(),
|
|
|
|
)
|
|
|
|
.into());
|
2020-04-30 18:31:55 -04:00
|
|
|
}
|
|
|
|
|
2020-06-16 19:38:30 -04:00
|
|
|
let func = match parser.scopes.last().get_fn(
|
|
|
|
Spanned {
|
|
|
|
node: &name,
|
|
|
|
span: args.span(),
|
|
|
|
},
|
|
|
|
parser.global_scope,
|
|
|
|
) {
|
2020-05-22 20:13:39 -04:00
|
|
|
Ok(f) => SassFunction::UserDefined(Box::new(f), name.into()),
|
2020-05-22 14:20:31 -04:00
|
|
|
Err(..) => match GLOBAL_FUNCTIONS.get(name.as_str()) {
|
2020-05-22 20:13:39 -04:00
|
|
|
Some(f) => SassFunction::Builtin(f.clone(), name.into()),
|
2020-04-30 19:36:34 -04:00
|
|
|
None => return Err((format!("Function not found: {}", name), args.span()).into()),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(Value::Function(func))
|
|
|
|
}
|
2020-04-30 18:31:55 -04:00
|
|
|
|
2020-06-16 19:38:30 -04:00
|
|
|
fn call(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
|
|
|
let func = match parser.arg(&mut args, 0, "function")? {
|
2020-04-30 19:36:34 -04:00
|
|
|
Value::Function(f) => f,
|
|
|
|
v => {
|
2020-04-30 18:31:55 -04:00
|
|
|
return Err((
|
2020-04-30 19:36:34 -04:00
|
|
|
format!(
|
|
|
|
"$function: {} is not a function reference.",
|
|
|
|
v.to_css_string(args.span())?
|
|
|
|
),
|
2020-04-30 18:31:55 -04:00
|
|
|
args.span(),
|
|
|
|
)
|
2020-04-30 19:36:34 -04:00
|
|
|
.into())
|
2020-04-30 18:31:55 -04:00
|
|
|
}
|
2020-04-30 19:36:34 -04:00
|
|
|
};
|
2020-06-16 19:38:30 -04:00
|
|
|
func.call(args.decrement(), parser)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(clippy::needless_pass_by_value)]
|
|
|
|
fn content_exists(args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
|
|
|
|
args.max_args(0)?;
|
|
|
|
if !parser.in_mixin {
|
|
|
|
return Err((
|
|
|
|
"content-exists() may only be called within a mixin.",
|
|
|
|
parser.span_before,
|
|
|
|
)
|
|
|
|
.into());
|
|
|
|
}
|
2020-07-02 15:22:15 -04:00
|
|
|
Ok(Value::bool(
|
|
|
|
parser
|
|
|
|
.content
|
|
|
|
.last()
|
2020-07-02 15:57:14 -04:00
|
|
|
.map_or(false, |c| c.content.is_some())
|
2020-07-02 15:22:15 -04:00
|
|
|
))
|
2020-04-30 19:36:34 -04:00
|
|
|
}
|
2020-04-30 18:31:55 -04:00
|
|
|
|
2020-05-16 18:01:06 -04:00
|
|
|
pub(crate) fn declare(f: &mut GlobalFunctionMap) {
|
2020-04-30 18:31:55 -04:00
|
|
|
f.insert("if", Builtin::new(if_));
|
|
|
|
f.insert("feature-exists", Builtin::new(feature_exists));
|
|
|
|
f.insert("unit", Builtin::new(unit));
|
|
|
|
f.insert("type-of", Builtin::new(type_of));
|
|
|
|
f.insert("unitless", Builtin::new(unitless));
|
|
|
|
f.insert("inspect", Builtin::new(inspect));
|
|
|
|
f.insert("variable-exists", Builtin::new(variable_exists));
|
2020-04-04 12:31:43 -04:00
|
|
|
f.insert(
|
2020-04-30 18:31:55 -04:00
|
|
|
"global-variable-exists",
|
|
|
|
Builtin::new(global_variable_exists),
|
2020-04-04 12:31:43 -04:00
|
|
|
);
|
2020-04-30 18:31:55 -04:00
|
|
|
f.insert("mixin-exists", Builtin::new(mixin_exists));
|
|
|
|
f.insert("function-exists", Builtin::new(function_exists));
|
|
|
|
f.insert("get-function", Builtin::new(get_function));
|
|
|
|
f.insert("call", Builtin::new(call));
|
2020-06-16 19:38:30 -04:00
|
|
|
f.insert("content-exists", Builtin::new(content_exists));
|
2020-02-07 00:10:43 -05:00
|
|
|
}
|