args and default values are lazily evaluated
This commit is contained in:
parent
ece0f5afde
commit
b7be1705a2
95
src/args.rs
95
src/args.rs
@ -18,7 +18,7 @@ pub(crate) struct FuncArgs(pub Vec<FuncArg>);
|
|||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub(crate) struct FuncArg {
|
pub(crate) struct FuncArg {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub default: Option<Value>,
|
pub default: Option<Vec<Token>>,
|
||||||
pub is_variadic: bool,
|
pub is_variadic: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ impl FuncArgs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct CallArgs(HashMap<CallArg, Value>);
|
pub(crate) struct CallArgs(HashMap<CallArg, Vec<Token>>);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||||
enum CallArg {
|
enum CallArg {
|
||||||
@ -58,25 +58,58 @@ impl CallArgs {
|
|||||||
CallArgs(HashMap::new())
|
CallArgs(HashMap::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
/// Get argument by name
|
||||||
pub fn get_named(&self, val: String) -> Option<&Value> {
|
///
|
||||||
self.0.get(&CallArg::Named(val))
|
/// Removes the argument
|
||||||
|
pub fn get_named(
|
||||||
|
&mut self,
|
||||||
|
val: String,
|
||||||
|
scope: &Scope,
|
||||||
|
super_selector: &Selector,
|
||||||
|
) -> Option<SassResult<Value>> {
|
||||||
|
match self.0.remove(&CallArg::Named(val)) {
|
||||||
|
Some(v) => Some(Value::from_tokens(
|
||||||
|
&mut v.into_iter().peekable(),
|
||||||
|
scope,
|
||||||
|
super_selector,
|
||||||
|
)),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_positional(&self, val: usize) -> Option<&Value> {
|
/// Get a positional argument by 0-indexed position
|
||||||
self.0.get(&CallArg::Positional(val))
|
///
|
||||||
|
/// Removes the argument
|
||||||
|
pub fn get_positional(
|
||||||
|
&mut self,
|
||||||
|
val: usize,
|
||||||
|
scope: &Scope,
|
||||||
|
super_selector: &Selector,
|
||||||
|
) -> Option<SassResult<Value>> {
|
||||||
|
match self.0.remove(&CallArg::Positional(val)) {
|
||||||
|
Some(v) => Some(Value::from_tokens(
|
||||||
|
&mut v.into_iter().peekable(),
|
||||||
|
scope,
|
||||||
|
super_selector,
|
||||||
|
)),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_variadic(self) -> SassResult<Vec<Value>> {
|
pub fn get_variadic(self, scope: &Scope, super_selector: &Selector) -> SassResult<Vec<Value>> {
|
||||||
let mut vals = Vec::new();
|
let mut vals = Vec::new();
|
||||||
let mut args = self
|
let mut args = self
|
||||||
.0
|
.0
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(a, v)| Ok((a.position()?, v)))
|
.map(|(a, v)| Ok((a.position()?, v)))
|
||||||
.collect::<SassResult<Vec<(usize, Value)>>>()?;
|
.collect::<SassResult<Vec<(usize, Vec<Token>)>>>()?;
|
||||||
args.sort_by(|(a1, _), (a2, _)| a1.cmp(a2));
|
args.sort_by(|(a1, _), (a2, _)| a1.cmp(a2));
|
||||||
for arg in args {
|
for arg in args {
|
||||||
vals.push(arg.1);
|
vals.push(Value::from_tokens(
|
||||||
|
&mut arg.1.into_iter().peekable(),
|
||||||
|
scope,
|
||||||
|
super_selector,
|
||||||
|
)?);
|
||||||
}
|
}
|
||||||
Ok(vals)
|
Ok(vals)
|
||||||
}
|
}
|
||||||
@ -97,14 +130,6 @@ impl CallArgs {
|
|||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.0.len() == 0
|
self.0.len() == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_named(&mut self, s: String) -> Option<Value> {
|
|
||||||
self.0.remove(&CallArg::Named(s))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_positional(&mut self, s: usize) -> Option<Value> {
|
|
||||||
self.0.remove(&CallArg::Positional(s))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn eat_func_args<I: Iterator<Item = Token>>(
|
pub(crate) fn eat_func_args<I: Iterator<Item = Token>>(
|
||||||
@ -137,11 +162,7 @@ pub(crate) fn eat_func_args<I: Iterator<Item = Token>>(
|
|||||||
toks.next();
|
toks.next();
|
||||||
args.push(FuncArg {
|
args.push(FuncArg {
|
||||||
name: name.replace('_', "-"),
|
name: name.replace('_', "-"),
|
||||||
default: Some(Value::from_tokens(
|
default: Some(default),
|
||||||
&mut default.into_iter().peekable(),
|
|
||||||
scope,
|
|
||||||
super_selector,
|
|
||||||
)?),
|
|
||||||
is_variadic,
|
is_variadic,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
@ -149,11 +170,7 @@ pub(crate) fn eat_func_args<I: Iterator<Item = Token>>(
|
|||||||
')' => {
|
')' => {
|
||||||
args.push(FuncArg {
|
args.push(FuncArg {
|
||||||
name: name.replace('_', "-"),
|
name: name.replace('_', "-"),
|
||||||
default: Some(Value::from_tokens(
|
default: Some(default),
|
||||||
&mut default.into_iter().peekable(),
|
|
||||||
scope,
|
|
||||||
super_selector,
|
|
||||||
)?),
|
|
||||||
is_variadic,
|
is_variadic,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
@ -181,11 +198,7 @@ pub(crate) fn eat_func_args<I: Iterator<Item = Token>>(
|
|||||||
|
|
||||||
args.push(FuncArg {
|
args.push(FuncArg {
|
||||||
name: name.replace('_', "-"),
|
name: name.replace('_', "-"),
|
||||||
default: Some(Value::from_tokens(
|
default: Some(default),
|
||||||
&mut default.into_iter().peekable(),
|
|
||||||
scope,
|
|
||||||
super_selector,
|
|
||||||
)?),
|
|
||||||
is_variadic,
|
is_variadic,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
@ -196,11 +209,7 @@ pub(crate) fn eat_func_args<I: Iterator<Item = Token>>(
|
|||||||
default: if default.is_empty() {
|
default: if default.is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(Value::from_tokens(
|
Some(default)
|
||||||
&mut default.into_iter().peekable(),
|
|
||||||
scope,
|
|
||||||
super_selector,
|
|
||||||
)?)
|
|
||||||
},
|
},
|
||||||
is_variadic,
|
is_variadic,
|
||||||
});
|
});
|
||||||
@ -228,7 +237,7 @@ pub(crate) fn eat_call_args<I: Iterator<Item = Token>>(
|
|||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
super_selector: &Selector,
|
super_selector: &Selector,
|
||||||
) -> SassResult<CallArgs> {
|
) -> SassResult<CallArgs> {
|
||||||
let mut args: HashMap<CallArg, Value> = HashMap::new();
|
let mut args: HashMap<CallArg, Vec<Token>> = HashMap::new();
|
||||||
devour_whitespace_or_comment(toks)?;
|
devour_whitespace_or_comment(toks)?;
|
||||||
let mut name = String::new();
|
let mut name = String::new();
|
||||||
let mut val: Vec<Token> = Vec::new();
|
let mut val: Vec<Token> = Vec::new();
|
||||||
@ -264,7 +273,7 @@ pub(crate) fn eat_call_args<I: Iterator<Item = Token>>(
|
|||||||
} else {
|
} else {
|
||||||
CallArg::Named(name.replace('_', "-"))
|
CallArg::Named(name.replace('_', "-"))
|
||||||
},
|
},
|
||||||
Value::from_tokens(&mut val.into_iter().peekable(), scope, super_selector)?,
|
val,
|
||||||
);
|
);
|
||||||
return Ok(CallArgs(args));
|
return Ok(CallArgs(args));
|
||||||
}
|
}
|
||||||
@ -291,11 +300,7 @@ pub(crate) fn eat_call_args<I: Iterator<Item = Token>>(
|
|||||||
} else {
|
} else {
|
||||||
CallArg::Named(name.replace('_', "-"))
|
CallArg::Named(name.replace('_', "-"))
|
||||||
},
|
},
|
||||||
Value::from_tokens(
|
val.clone(),
|
||||||
&mut val.clone().into_iter().peekable(),
|
|
||||||
scope,
|
|
||||||
super_selector,
|
|
||||||
)?,
|
|
||||||
);
|
);
|
||||||
val.clear();
|
val.clear();
|
||||||
devour_whitespace(toks);
|
devour_whitespace(toks);
|
||||||
|
@ -59,19 +59,30 @@ impl Function {
|
|||||||
Ok((name, Function::new(scope, args, body, pos)))
|
Ok((name, Function::new(scope, args, body, pos)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn args(mut self, mut args: CallArgs) -> SassResult<Function> {
|
pub fn args(
|
||||||
|
mut self,
|
||||||
|
mut args: CallArgs,
|
||||||
|
scope: &Scope,
|
||||||
|
super_selector: &Selector,
|
||||||
|
) -> SassResult<Function> {
|
||||||
for (idx, arg) in self.args.0.iter().enumerate() {
|
for (idx, arg) in self.args.0.iter().enumerate() {
|
||||||
if arg.is_variadic {
|
if arg.is_variadic {
|
||||||
self.scope
|
self.scope.insert_var(
|
||||||
.insert_var(&arg.name, Value::ArgList(args.get_variadic()?))?;
|
&arg.name,
|
||||||
|
Value::ArgList(args.get_variadic(scope, super_selector)?),
|
||||||
|
)?;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let val = match args.remove_positional(idx) {
|
let val = match args.get_positional(idx, scope, super_selector) {
|
||||||
Some(v) => v,
|
Some(v) => v?,
|
||||||
None => match args.remove_named(arg.name.clone()) {
|
None => match args.get_named(arg.name.clone(), scope, super_selector) {
|
||||||
Some(v) => v,
|
Some(v) => v?,
|
||||||
None => match &arg.default {
|
None => match &arg.default {
|
||||||
Some(v) => v.clone(),
|
Some(v) => Value::from_tokens(
|
||||||
|
&mut v.iter().cloned().peekable(),
|
||||||
|
scope,
|
||||||
|
super_selector,
|
||||||
|
)?,
|
||||||
None => return Err(format!("Missing argument ${}.", &arg.name).into()),
|
None => return Err(format!("Missing argument ${}.", &arg.name).into()),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -60,19 +60,30 @@ impl Mixin {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn args(mut self, mut args: CallArgs) -> SassResult<Mixin> {
|
pub fn args(
|
||||||
|
mut self,
|
||||||
|
mut args: CallArgs,
|
||||||
|
scope: &Scope,
|
||||||
|
super_selector: &Selector,
|
||||||
|
) -> SassResult<Mixin> {
|
||||||
for (idx, arg) in self.args.0.iter().enumerate() {
|
for (idx, arg) in self.args.0.iter().enumerate() {
|
||||||
if arg.is_variadic {
|
if arg.is_variadic {
|
||||||
self.scope
|
self.scope.insert_var(
|
||||||
.insert_var(&arg.name, Value::ArgList(args.get_variadic()?))?;
|
&arg.name,
|
||||||
|
Value::ArgList(args.get_variadic(scope, super_selector)?),
|
||||||
|
)?;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let val = match args.remove_positional(idx) {
|
let val = match args.get_positional(idx, scope, super_selector) {
|
||||||
Some(v) => v,
|
Some(v) => v?,
|
||||||
None => match args.remove_named(arg.name.clone()) {
|
None => match args.get_named(arg.name.clone(), scope, super_selector) {
|
||||||
Some(v) => v,
|
Some(v) => v?,
|
||||||
None => match &arg.default {
|
None => match &arg.default {
|
||||||
Some(v) => v.clone(),
|
Some(v) => Value::from_tokens(
|
||||||
|
&mut v.iter().cloned().peekable(),
|
||||||
|
scope,
|
||||||
|
super_selector,
|
||||||
|
)?,
|
||||||
None => return Err(format!("Missing argument ${}.", &arg.name).into()),
|
None => return Err(format!("Missing argument ${}.", &arg.name).into()),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -176,6 +187,9 @@ pub(crate) fn eat_include<I: Iterator<Item = Token>>(
|
|||||||
|
|
||||||
let mixin = scope.get_mixin(&name)?.clone();
|
let mixin = scope.get_mixin(&name)?.clone();
|
||||||
|
|
||||||
let rules = mixin.args(args)?.content(content).call(super_selector)?;
|
let rules = mixin
|
||||||
|
.args(args, scope, super_selector)?
|
||||||
|
.content(content)
|
||||||
|
.call(super_selector)?;
|
||||||
Ok(rules)
|
Ok(rules)
|
||||||
}
|
}
|
||||||
|
@ -11,13 +11,13 @@ use crate::value::{Number, Value};
|
|||||||
pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
||||||
f.insert(
|
f.insert(
|
||||||
"hsl".to_owned(),
|
"hsl".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
return Err("Missing argument $channels.".into());
|
return Err("Missing argument $channels.".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.len() == 1 {
|
if args.len() == 1 {
|
||||||
let mut channels = match arg!(args, 0, "channels") {
|
let mut channels = match arg!(args, scope, super_selector, 0, "channels") {
|
||||||
Value::List(v, ..) => v,
|
Value::List(v, ..) => v,
|
||||||
_ => return Err("Missing argument $channels.".into()),
|
_ => return Err("Missing argument $channels.".into()),
|
||||||
};
|
};
|
||||||
@ -55,20 +55,22 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
Number::one(),
|
Number::one(),
|
||||||
)))
|
)))
|
||||||
} else {
|
} else {
|
||||||
let hue = match arg!(args, 0, "hue") {
|
let hue = match arg!(args, scope, super_selector, 0, "hue") {
|
||||||
Value::Dimension(n, _) => n,
|
Value::Dimension(n, _) => n,
|
||||||
v => return Err(format!("$hue: {} is not a number.", v).into()),
|
v => return Err(format!("$hue: {} is not a number.", v).into()),
|
||||||
};
|
};
|
||||||
let saturation = match arg!(args, 1, "saturation") {
|
let saturation = match arg!(args, scope, super_selector, 1, "saturation") {
|
||||||
Value::Dimension(n, _) => n / Number::from(100),
|
Value::Dimension(n, _) => n / Number::from(100),
|
||||||
v => return Err(format!("$saturation: {} is not a number.", v).into()),
|
v => return Err(format!("$saturation: {} is not a number.", v).into()),
|
||||||
};
|
};
|
||||||
let lightness = match arg!(args, 2, "lightness") {
|
let lightness = match arg!(args, scope, super_selector, 2, "lightness") {
|
||||||
Value::Dimension(n, _) => n / Number::from(100),
|
Value::Dimension(n, _) => n / Number::from(100),
|
||||||
v => return Err(format!("$lightness: {} is not a number.", v).into()),
|
v => return Err(format!("$lightness: {} is not a number.", v).into()),
|
||||||
};
|
};
|
||||||
let alpha = match arg!(
|
let alpha = match arg!(
|
||||||
args,
|
args,
|
||||||
|
scope,
|
||||||
|
super_selector,
|
||||||
3,
|
3,
|
||||||
"alpha" = Value::Dimension(Number::one(), Unit::None)
|
"alpha" = Value::Dimension(Number::one(), Unit::None)
|
||||||
) {
|
) {
|
||||||
@ -89,13 +91,13 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"hsla".to_owned(),
|
"hsla".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
return Err("Missing argument $channels.".into());
|
return Err("Missing argument $channels.".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.len() == 1 {
|
if args.len() == 1 {
|
||||||
let mut channels = match arg!(args, 0, "channels") {
|
let mut channels = match arg!(args, scope, super_selector, 0, "channels") {
|
||||||
Value::List(v, ..) => v,
|
Value::List(v, ..) => v,
|
||||||
_ => return Err("Missing argument $channels.".into()),
|
_ => return Err("Missing argument $channels.".into()),
|
||||||
};
|
};
|
||||||
@ -133,20 +135,22 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
Number::one(),
|
Number::one(),
|
||||||
)))
|
)))
|
||||||
} else {
|
} else {
|
||||||
let hue = match arg!(args, 0, "hue") {
|
let hue = match arg!(args, scope, super_selector, 0, "hue") {
|
||||||
Value::Dimension(n, _) => n,
|
Value::Dimension(n, _) => n,
|
||||||
v => return Err(format!("$hue: {} is not a number.", v).into()),
|
v => return Err(format!("$hue: {} is not a number.", v).into()),
|
||||||
};
|
};
|
||||||
let saturation = match arg!(args, 1, "saturation") {
|
let saturation = match arg!(args, scope, super_selector, 1, "saturation") {
|
||||||
Value::Dimension(n, _) => n / Number::from(100),
|
Value::Dimension(n, _) => n / Number::from(100),
|
||||||
v => return Err(format!("$saturation: {} is not a number.", v).into()),
|
v => return Err(format!("$saturation: {} is not a number.", v).into()),
|
||||||
};
|
};
|
||||||
let lightness = match arg!(args, 2, "lightness") {
|
let lightness = match arg!(args, scope, super_selector, 2, "lightness") {
|
||||||
Value::Dimension(n, _) => n / Number::from(100),
|
Value::Dimension(n, _) => n / Number::from(100),
|
||||||
v => return Err(format!("$lightness: {} is not a number.", v).into()),
|
v => return Err(format!("$lightness: {} is not a number.", v).into()),
|
||||||
};
|
};
|
||||||
let alpha = match arg!(
|
let alpha = match arg!(
|
||||||
args,
|
args,
|
||||||
|
scope,
|
||||||
|
super_selector,
|
||||||
3,
|
3,
|
||||||
"alpha" = Value::Dimension(Number::one(), Unit::None)
|
"alpha" = Value::Dimension(Number::one(), Unit::None)
|
||||||
) {
|
) {
|
||||||
@ -167,9 +171,9 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"hue".to_owned(),
|
"hue".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
match arg!(args, 0, "color") {
|
match arg!(args, scope, super_selector, 0, "color") {
|
||||||
Value::Color(c) => Ok(Value::Dimension(c.hue(), Unit::Deg)),
|
Value::Color(c) => Ok(Value::Dimension(c.hue(), Unit::Deg)),
|
||||||
v => Err(format!("$color: {} is not a color.", v).into()),
|
v => Err(format!("$color: {} is not a color.", v).into()),
|
||||||
}
|
}
|
||||||
@ -177,9 +181,9 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"saturation".to_owned(),
|
"saturation".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
match arg!(args, 0, "color") {
|
match arg!(args, scope, super_selector, 0, "color") {
|
||||||
Value::Color(c) => Ok(Value::Dimension(c.saturation(), Unit::Percent)),
|
Value::Color(c) => Ok(Value::Dimension(c.saturation(), Unit::Percent)),
|
||||||
v => Err(format!("$color: {} is not a color.", v).into()),
|
v => Err(format!("$color: {} is not a color.", v).into()),
|
||||||
}
|
}
|
||||||
@ -187,9 +191,9 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"lightness".to_owned(),
|
"lightness".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
match arg!(args, 0, "color") {
|
match arg!(args, scope, super_selector, 0, "color") {
|
||||||
Value::Color(c) => Ok(Value::Dimension(c.lightness(), Unit::Percent)),
|
Value::Color(c) => Ok(Value::Dimension(c.lightness(), Unit::Percent)),
|
||||||
v => Err(format!("$color: {} is not a color.", v).into()),
|
v => Err(format!("$color: {} is not a color.", v).into()),
|
||||||
}
|
}
|
||||||
@ -197,13 +201,13 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"adjust-hue".to_owned(),
|
"adjust-hue".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 2);
|
max_args!(args, 2);
|
||||||
let color = match arg!(args, 0, "color") {
|
let color = match arg!(args, scope, super_selector, 0, "color") {
|
||||||
Value::Color(c) => c,
|
Value::Color(c) => c,
|
||||||
v => return Err(format!("$color: {} is not a color.", v).into()),
|
v => return Err(format!("$color: {} is not a color.", v).into()),
|
||||||
};
|
};
|
||||||
let degrees = match arg!(args, 1, "degrees") {
|
let degrees = match arg!(args, scope, super_selector, 1, "degrees") {
|
||||||
Value::Dimension(n, _) => n,
|
Value::Dimension(n, _) => n,
|
||||||
v => return Err(format!("$degrees: {} is not a number.", v).into()),
|
v => return Err(format!("$degrees: {} is not a number.", v).into()),
|
||||||
};
|
};
|
||||||
@ -212,13 +216,13 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"lighten".to_owned(),
|
"lighten".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 2);
|
max_args!(args, 2);
|
||||||
let color = match arg!(args, 0, "color") {
|
let color = match arg!(args, scope, super_selector, 0, "color") {
|
||||||
Value::Color(c) => c,
|
Value::Color(c) => c,
|
||||||
v => return Err(format!("$color: {} is not a color.", v).into()),
|
v => return Err(format!("$color: {} is not a color.", v).into()),
|
||||||
};
|
};
|
||||||
let amount = match arg!(args, 1, "amount") {
|
let amount = match arg!(args, scope, super_selector, 1, "amount") {
|
||||||
Value::Dimension(n, u) => bound!("amount", n, u, 0, 100) / Number::from(100),
|
Value::Dimension(n, u) => bound!("amount", n, u, 0, 100) / Number::from(100),
|
||||||
v => return Err(format!("$amount: {} is not a number.", v).into()),
|
v => return Err(format!("$amount: {} is not a number.", v).into()),
|
||||||
};
|
};
|
||||||
@ -227,13 +231,13 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"darken".to_owned(),
|
"darken".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 2);
|
max_args!(args, 2);
|
||||||
let color = match arg!(args, 0, "color") {
|
let color = match arg!(args, scope, super_selector, 0, "color") {
|
||||||
Value::Color(c) => c,
|
Value::Color(c) => c,
|
||||||
v => return Err(format!("$color: {} is not a color.", v).into()),
|
v => return Err(format!("$color: {} is not a color.", v).into()),
|
||||||
};
|
};
|
||||||
let amount = match arg!(args, 1, "amount") {
|
let amount = match arg!(args, scope, super_selector, 1, "amount") {
|
||||||
Value::Dimension(n, u) => bound!("amount", n, u, 0, 100) / Number::from(100),
|
Value::Dimension(n, u) => bound!("amount", n, u, 0, 100) / Number::from(100),
|
||||||
v => return Err(format!("$amount: {} is not a number.", v).into()),
|
v => return Err(format!("$amount: {} is not a number.", v).into()),
|
||||||
};
|
};
|
||||||
@ -242,20 +246,23 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"saturate".to_owned(),
|
"saturate".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 2);
|
max_args!(args, 2);
|
||||||
if args.len() == 1 {
|
if args.len() == 1 {
|
||||||
return Ok(Value::Ident(
|
return Ok(Value::Ident(
|
||||||
format!("saturate({})", arg!(args, 0, "amount")),
|
format!(
|
||||||
|
"saturate({})",
|
||||||
|
arg!(args, scope, super_selector, 0, "amount")
|
||||||
|
),
|
||||||
QuoteKind::None,
|
QuoteKind::None,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let amount = match arg!(args, 1, "amount") {
|
let amount = match arg!(args, scope, super_selector, 1, "amount") {
|
||||||
Value::Dimension(n, u) => bound!("amount", n, u, 0, 100) / Number::from(100),
|
Value::Dimension(n, u) => bound!("amount", n, u, 0, 100) / Number::from(100),
|
||||||
v => return Err(format!("$amount: {} is not a number.", v).into()),
|
v => return Err(format!("$amount: {} is not a number.", v).into()),
|
||||||
};
|
};
|
||||||
let color = match arg!(args, 0, "color") {
|
let color = match arg!(args, scope, super_selector, 0, "color") {
|
||||||
Value::Color(c) => c,
|
Value::Color(c) => c,
|
||||||
Value::Dimension(n, u) => {
|
Value::Dimension(n, u) => {
|
||||||
return Ok(Value::Ident(
|
return Ok(Value::Ident(
|
||||||
@ -270,13 +277,13 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"desaturate".to_owned(),
|
"desaturate".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 2);
|
max_args!(args, 2);
|
||||||
let color = match arg!(args, 0, "color") {
|
let color = match arg!(args, scope, super_selector, 0, "color") {
|
||||||
Value::Color(c) => c,
|
Value::Color(c) => c,
|
||||||
v => return Err(format!("$color: {} is not a color.", v).into()),
|
v => return Err(format!("$color: {} is not a color.", v).into()),
|
||||||
};
|
};
|
||||||
let amount = match arg!(args, 1, "amount") {
|
let amount = match arg!(args, scope, super_selector, 1, "amount") {
|
||||||
Value::Dimension(n, u) => bound!("amount", n, u, 0, 100) / Number::from(100),
|
Value::Dimension(n, u) => bound!("amount", n, u, 0, 100) / Number::from(100),
|
||||||
v => return Err(format!("$amount: {} is not a number.", v).into()),
|
v => return Err(format!("$amount: {} is not a number.", v).into()),
|
||||||
};
|
};
|
||||||
@ -285,9 +292,9 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"grayscale".to_owned(),
|
"grayscale".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
let color = match arg!(args, 0, "color") {
|
let color = match arg!(args, scope, super_selector, 0, "color") {
|
||||||
Value::Color(c) => c,
|
Value::Color(c) => c,
|
||||||
Value::Dimension(n, u) => {
|
Value::Dimension(n, u) => {
|
||||||
return Ok(Value::Ident(
|
return Ok(Value::Ident(
|
||||||
@ -302,9 +309,9 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"complement".to_owned(),
|
"complement".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
let color = match arg!(args, 0, "color") {
|
let color = match arg!(args, scope, super_selector, 0, "color") {
|
||||||
Value::Color(c) => c,
|
Value::Color(c) => c,
|
||||||
v => return Err(format!("$color: {} is not a color.", v).into()),
|
v => return Err(format!("$color: {} is not a color.", v).into()),
|
||||||
};
|
};
|
||||||
@ -313,17 +320,19 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"invert".to_owned(),
|
"invert".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 2);
|
max_args!(args, 2);
|
||||||
let weight = match arg!(
|
let weight = match arg!(
|
||||||
args,
|
args,
|
||||||
|
scope,
|
||||||
|
super_selector,
|
||||||
1,
|
1,
|
||||||
"weight" = Value::Dimension(Number::from(100), Unit::Percent)
|
"weight" = Value::Dimension(Number::from(100), Unit::Percent)
|
||||||
) {
|
) {
|
||||||
Value::Dimension(n, u) => bound!("weight", n, u, 0, 100) / Number::from(100),
|
Value::Dimension(n, u) => bound!("weight", n, u, 0, 100) / Number::from(100),
|
||||||
v => return Err(format!("$weight: {} is not a number.", v).into()),
|
v => return Err(format!("$weight: {} is not a number.", v).into()),
|
||||||
};
|
};
|
||||||
match arg!(args, 0, "color") {
|
match arg!(args, scope, super_selector, 0, "color") {
|
||||||
Value::Color(c) => Ok(Value::Color(c.invert(weight))),
|
Value::Color(c) => Ok(Value::Color(c.invert(weight))),
|
||||||
Value::Dimension(n, Unit::Percent) => {
|
Value::Dimension(n, Unit::Percent) => {
|
||||||
Ok(Value::Ident(format!("invert({}%)", n), QuoteKind::None))
|
Ok(Value::Ident(format!("invert({}%)", n), QuoteKind::None))
|
||||||
|
@ -9,9 +9,9 @@ use crate::value::Value;
|
|||||||
pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
||||||
f.insert(
|
f.insert(
|
||||||
"alpha".to_owned(),
|
"alpha".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
match arg!(args, 0, "color") {
|
match arg!(args, scope, super_selector, 0, "color") {
|
||||||
Value::Color(c) => Ok(Value::Dimension(c.alpha(), Unit::None)),
|
Value::Color(c) => Ok(Value::Dimension(c.alpha(), Unit::None)),
|
||||||
v => Err(format!("$color: {} is not a color.", v).into()),
|
v => Err(format!("$color: {} is not a color.", v).into()),
|
||||||
}
|
}
|
||||||
@ -19,9 +19,9 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"opacity".to_owned(),
|
"opacity".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
match arg!(args, 0, "color") {
|
match arg!(args, scope, super_selector, 0, "color") {
|
||||||
Value::Color(c) => Ok(Value::Dimension(c.alpha(), Unit::None)),
|
Value::Color(c) => Ok(Value::Dimension(c.alpha(), Unit::None)),
|
||||||
Value::Dimension(num, unit) => Ok(Value::Ident(
|
Value::Dimension(num, unit) => Ok(Value::Ident(
|
||||||
format!("opacity({}{})", num, unit),
|
format!("opacity({}{})", num, unit),
|
||||||
@ -33,13 +33,13 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"opacify".to_owned(),
|
"opacify".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 2);
|
max_args!(args, 2);
|
||||||
let color = match arg!(args, 0, "color") {
|
let color = match arg!(args, scope, super_selector, 0, "color") {
|
||||||
Value::Color(c) => c,
|
Value::Color(c) => c,
|
||||||
v => return Err(format!("$color: {} is not a color.", v).into()),
|
v => return Err(format!("$color: {} is not a color.", v).into()),
|
||||||
};
|
};
|
||||||
let amount = match arg!(args, 1, "amount") {
|
let amount = match arg!(args, scope, super_selector, 1, "amount") {
|
||||||
Value::Dimension(n, u) => bound!("amount", n, u, 0, 1),
|
Value::Dimension(n, u) => bound!("amount", n, u, 0, 1),
|
||||||
v => return Err(format!("$amount: {} is not a number.", v).into()),
|
v => return Err(format!("$amount: {} is not a number.", v).into()),
|
||||||
};
|
};
|
||||||
@ -48,13 +48,13 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"fade-in".to_owned(),
|
"fade-in".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 2);
|
max_args!(args, 2);
|
||||||
let color = match arg!(args, 0, "color") {
|
let color = match arg!(args, scope, super_selector, 0, "color") {
|
||||||
Value::Color(c) => c,
|
Value::Color(c) => c,
|
||||||
v => return Err(format!("$color: {} is not a color.", v).into()),
|
v => return Err(format!("$color: {} is not a color.", v).into()),
|
||||||
};
|
};
|
||||||
let amount = match arg!(args, 1, "amount") {
|
let amount = match arg!(args, scope, super_selector, 1, "amount") {
|
||||||
Value::Dimension(n, u) => bound!("amount", n, u, 0, 1),
|
Value::Dimension(n, u) => bound!("amount", n, u, 0, 1),
|
||||||
v => return Err(format!("$amount: {} is not a number.", v).into()),
|
v => return Err(format!("$amount: {} is not a number.", v).into()),
|
||||||
};
|
};
|
||||||
@ -63,13 +63,13 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"transparentize".to_owned(),
|
"transparentize".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 2);
|
max_args!(args, 2);
|
||||||
let color = match arg!(args, 0, "color") {
|
let color = match arg!(args, scope, super_selector, 0, "color") {
|
||||||
Value::Color(c) => c,
|
Value::Color(c) => c,
|
||||||
v => return Err(format!("$color: {} is not a color.", v).into()),
|
v => return Err(format!("$color: {} is not a color.", v).into()),
|
||||||
};
|
};
|
||||||
let amount = match arg!(args, 1, "amount") {
|
let amount = match arg!(args, scope, super_selector, 1, "amount") {
|
||||||
Value::Dimension(n, u) => bound!("amount", n, u, 0, 1),
|
Value::Dimension(n, u) => bound!("amount", n, u, 0, 1),
|
||||||
v => return Err(format!("$amount: {} is not a number.", v).into()),
|
v => return Err(format!("$amount: {} is not a number.", v).into()),
|
||||||
};
|
};
|
||||||
@ -78,13 +78,13 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"fade-out".to_owned(),
|
"fade-out".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 2);
|
max_args!(args, 2);
|
||||||
let color = match arg!(args, 0, "color") {
|
let color = match arg!(args, scope, super_selector, 0, "color") {
|
||||||
Value::Color(c) => c,
|
Value::Color(c) => c,
|
||||||
v => return Err(format!("$color: {} is not a color.", v).into()),
|
v => return Err(format!("$color: {} is not a color.", v).into()),
|
||||||
};
|
};
|
||||||
let amount = match arg!(args, 1, "amount") {
|
let amount = match arg!(args, scope, super_selector, 1, "amount") {
|
||||||
Value::Dimension(n, u) => bound!("amount", n, u, 0, 1),
|
Value::Dimension(n, u) => bound!("amount", n, u, 0, 1),
|
||||||
v => return Err(format!("$amount: {} is not a number.", v).into()),
|
v => return Err(format!("$amount: {} is not a number.", v).into()),
|
||||||
};
|
};
|
||||||
|
@ -9,9 +9,9 @@ use crate::unit::Unit;
|
|||||||
use crate::value::{Number, Value};
|
use crate::value::{Number, Value};
|
||||||
|
|
||||||
macro_rules! opt_rgba {
|
macro_rules! opt_rgba {
|
||||||
($args:ident, $name:ident, $arg:literal, $low:literal, $high:literal) => {
|
($args:ident, $name:ident, $arg:literal, $low:literal, $high:literal, $scope:ident, $super_selector:ident) => {
|
||||||
let x = $low;
|
let x = $low;
|
||||||
let $name = match named_arg!($args, $arg = Value::Null) {
|
let $name = match named_arg!($args, $scope, $super_selector, $arg = Value::Null) {
|
||||||
Value::Dimension(n, u) => Some(bound!($arg, n, u, x, $high)),
|
Value::Dimension(n, u) => Some(bound!($arg, n, u, x, $high)),
|
||||||
Value::Null => None,
|
Value::Null => None,
|
||||||
v => return Err(format!("${}: {} is not a number.", $arg, v).into()),
|
v => return Err(format!("${}: {} is not a number.", $arg, v).into()),
|
||||||
@ -20,9 +20,9 @@ macro_rules! opt_rgba {
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! opt_hsl {
|
macro_rules! opt_hsl {
|
||||||
($args:ident, $name:ident, $arg:literal, $low:literal, $high:literal) => {
|
($args:ident, $name:ident, $arg:literal, $low:literal, $high:literal, $scope:ident, $super_selector:ident) => {
|
||||||
let x = $low;
|
let x = $low;
|
||||||
let $name = match named_arg!($args, $arg = Value::Null) {
|
let $name = match named_arg!($args, $scope, $super_selector, $arg = Value::Null) {
|
||||||
Value::Dimension(n, u) => Some(bound!($arg, n, u, x, $high) / Number::from(100)),
|
Value::Dimension(n, u) => Some(bound!($arg, n, u, x, $high) / Number::from(100)),
|
||||||
Value::Null => None,
|
Value::Null => None,
|
||||||
v => return Err(format!("${}: {} is not a number.", $arg, v).into()),
|
v => return Err(format!("${}: {} is not a number.", $arg, v).into()),
|
||||||
@ -31,33 +31,33 @@ macro_rules! opt_hsl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
||||||
f.insert("change-color".to_owned(), Builtin::new(|mut args, _| {
|
f.insert("change-color".to_owned(), Builtin::new(|mut args, scope, super_selector| {
|
||||||
if args.get_positional(1).is_some() {
|
if args.get_positional(1, scope, super_selector).is_some() {
|
||||||
return Err("Only one positional argument is allowed. All other arguments must be passed by name.".into());
|
return Err("Only one positional argument is allowed. All other arguments must be passed by name.".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let color = match arg!(args, 0, "color") {
|
let color = match arg!(args, scope, super_selector, 0, "color") {
|
||||||
Value::Color(c) => c,
|
Value::Color(c) => c,
|
||||||
v => return Err(format!("$color: {} is not a color.", v).into()),
|
v => return Err(format!("$color: {} is not a color.", v).into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
opt_rgba!(args, alpha, "alpha", 0, 1);
|
opt_rgba!(args, alpha, "alpha", 0, 1, scope, super_selector);
|
||||||
opt_rgba!(args, red, "red", 0, 255);
|
opt_rgba!(args, red, "red", 0, 255, scope, super_selector);
|
||||||
opt_rgba!(args, green, "green", 0, 255);
|
opt_rgba!(args, green, "green", 0, 255, scope, super_selector);
|
||||||
opt_rgba!(args, blue, "blue", 0, 255);
|
opt_rgba!(args, blue, "blue", 0, 255, scope, super_selector);
|
||||||
|
|
||||||
if red.is_some() || green.is_some() || blue.is_some() {
|
if red.is_some() || green.is_some() || blue.is_some() {
|
||||||
return Ok(Value::Color(Color::from_rgba(red.unwrap_or(color.red()), green.unwrap_or(color.green()), blue.unwrap_or(color.blue()), alpha.unwrap_or(color.alpha()))))
|
return Ok(Value::Color(Color::from_rgba(red.unwrap_or(color.red()), green.unwrap_or(color.green()), blue.unwrap_or(color.blue()), alpha.unwrap_or(color.alpha()))))
|
||||||
}
|
}
|
||||||
|
|
||||||
let hue = match named_arg!(args, "hue"=Value::Null) {
|
let hue = match named_arg!(args, scope, super_selector, "hue"=Value::Null) {
|
||||||
Value::Dimension(n, _) => Some(n),
|
Value::Dimension(n, _) => Some(n),
|
||||||
Value::Null => None,
|
Value::Null => None,
|
||||||
v => return Err(format!("$hue: {} is not a number.", v).into()),
|
v => return Err(format!("$hue: {} is not a number.", v).into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
opt_hsl!(args, saturation, "saturation", 0, 100);
|
opt_hsl!(args, saturation, "saturation", 0, 100, scope, super_selector);
|
||||||
opt_hsl!(args, luminance, "lightness", 0, 100);
|
opt_hsl!(args, luminance, "lightness", 0, 100, scope, super_selector);
|
||||||
|
|
||||||
if hue.is_some() || saturation.is_some() || luminance.is_some() {
|
if hue.is_some() || saturation.is_some() || luminance.is_some() {
|
||||||
// Color::as_hsla() returns more exact values than Color::hue(), etc.
|
// Color::as_hsla() returns more exact values than Color::hue(), etc.
|
||||||
@ -73,16 +73,16 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
}));
|
}));
|
||||||
f.insert(
|
f.insert(
|
||||||
"adjust-color".to_owned(),
|
"adjust-color".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
let color = match arg!(args, 0, "color") {
|
let color = match arg!(args, scope, super_selector, 0, "color") {
|
||||||
Value::Color(c) => c,
|
Value::Color(c) => c,
|
||||||
v => return Err(format!("$color: {} is not a color.", v).into()),
|
v => return Err(format!("$color: {} is not a color.", v).into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
opt_rgba!(args, alpha, "alpha", -1, 1);
|
opt_rgba!(args, alpha, "alpha", -1, 1, scope, super_selector);
|
||||||
opt_rgba!(args, red, "red", -255, 255);
|
opt_rgba!(args, red, "red", -255, 255, scope, super_selector);
|
||||||
opt_rgba!(args, green, "green", -255, 255);
|
opt_rgba!(args, green, "green", -255, 255, scope, super_selector);
|
||||||
opt_rgba!(args, blue, "blue", -255, 255);
|
opt_rgba!(args, blue, "blue", -255, 255, scope, super_selector);
|
||||||
|
|
||||||
if red.is_some() || green.is_some() || blue.is_some() {
|
if red.is_some() || green.is_some() || blue.is_some() {
|
||||||
return Ok(Value::Color(Color::from_rgba(
|
return Ok(Value::Color(Color::from_rgba(
|
||||||
@ -93,14 +93,30 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let hue = match named_arg!(args, "hue" = Value::Null) {
|
let hue = match named_arg!(args, scope, super_selector, "hue" = Value::Null) {
|
||||||
Value::Dimension(n, _) => Some(n),
|
Value::Dimension(n, _) => Some(n),
|
||||||
Value::Null => None,
|
Value::Null => None,
|
||||||
v => return Err(format!("$hue: {} is not a number.", v).into()),
|
v => return Err(format!("$hue: {} is not a number.", v).into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
opt_hsl!(args, saturation, "saturation", -100, 100);
|
opt_hsl!(
|
||||||
opt_hsl!(args, luminance, "lightness", -100, 100);
|
args,
|
||||||
|
saturation,
|
||||||
|
"saturation",
|
||||||
|
-100,
|
||||||
|
100,
|
||||||
|
scope,
|
||||||
|
super_selector
|
||||||
|
);
|
||||||
|
opt_hsl!(
|
||||||
|
args,
|
||||||
|
luminance,
|
||||||
|
"lightness",
|
||||||
|
-100,
|
||||||
|
100,
|
||||||
|
scope,
|
||||||
|
super_selector
|
||||||
|
);
|
||||||
|
|
||||||
if hue.is_some() || saturation.is_some() || luminance.is_some() {
|
if hue.is_some() || saturation.is_some() || luminance.is_some() {
|
||||||
// Color::as_hsla() returns more exact values than Color::hue(), etc.
|
// Color::as_hsla() returns more exact values than Color::hue(), etc.
|
||||||
@ -123,16 +139,16 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"scale-color".to_owned(),
|
"scale-color".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
let color = match arg!(args, 0, "color") {
|
let color = match arg!(args, scope, super_selector, 0, "color") {
|
||||||
Value::Color(c) => c,
|
Value::Color(c) => c,
|
||||||
v => return Err(format!("$color: {} is not a color.", v).into()),
|
v => return Err(format!("$color: {} is not a color.", v).into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
macro_rules! opt_scale_arg {
|
macro_rules! opt_scale_arg {
|
||||||
($args:ident, $name:ident, $arg:literal, $low:literal, $high:literal) => {
|
($args:ident, $name:ident, $arg:literal, $low:literal, $high:literal, $scope:ident, $super_selector:ident) => {
|
||||||
let x = $low;
|
let x = $low;
|
||||||
let $name = match named_arg!($args, $arg = Value::Null) {
|
let $name = match named_arg!($args, $scope, $super_selector, $arg = Value::Null) {
|
||||||
Value::Dimension(n, Unit::Percent) => {
|
Value::Dimension(n, Unit::Percent) => {
|
||||||
Some(bound!($arg, n, Unit::Percent, x, $high) / Number::from(100))
|
Some(bound!($arg, n, Unit::Percent, x, $high) / Number::from(100))
|
||||||
}
|
}
|
||||||
@ -147,10 +163,10 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
opt_scale_arg!(args, alpha, "alpha", -100, 100);
|
opt_scale_arg!(args, alpha, "alpha", -100, 100, scope, super_selector);
|
||||||
opt_scale_arg!(args, red, "red", -100, 100);
|
opt_scale_arg!(args, red, "red", -100, 100, scope, super_selector);
|
||||||
opt_scale_arg!(args, green, "green", -100, 100);
|
opt_scale_arg!(args, green, "green", -100, 100, scope, super_selector);
|
||||||
opt_scale_arg!(args, blue, "blue", -100, 100);
|
opt_scale_arg!(args, blue, "blue", -100, 100, scope, super_selector);
|
||||||
|
|
||||||
if red.is_some() || green.is_some() || blue.is_some() {
|
if red.is_some() || green.is_some() || blue.is_some() {
|
||||||
return Ok(Value::Color(Color::from_rgba(
|
return Ok(Value::Color(Color::from_rgba(
|
||||||
@ -177,8 +193,8 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
opt_scale_arg!(args, saturation, "saturation", -100, 100);
|
opt_scale_arg!(args, saturation, "saturation", -100, 100, scope, super_selector);
|
||||||
opt_scale_arg!(args, luminance, "lightness", -100, 100);
|
opt_scale_arg!(args, luminance, "lightness", -100, 100, scope, super_selector);
|
||||||
|
|
||||||
if saturation.is_some() || luminance.is_some() {
|
if saturation.is_some() || luminance.is_some() {
|
||||||
// Color::as_hsla() returns more exact values than Color::hue(), etc.
|
// Color::as_hsla() returns more exact values than Color::hue(), etc.
|
||||||
@ -209,9 +225,9 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"ie-hex-str".to_owned(),
|
"ie-hex-str".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
let color = match arg!(args, 0, "color") {
|
let color = match arg!(args, scope, super_selector, 0, "color") {
|
||||||
Value::Color(c) => c,
|
Value::Color(c) => c,
|
||||||
v => return Err(format!("$color: {} is not a color.", v).into()),
|
v => return Err(format!("$color: {} is not a color.", v).into()),
|
||||||
};
|
};
|
||||||
|
@ -10,13 +10,13 @@ use crate::value::{Number, Value};
|
|||||||
pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
||||||
f.insert(
|
f.insert(
|
||||||
"rgb".to_owned(),
|
"rgb".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
return Err("Missing argument $channels.".into());
|
return Err("Missing argument $channels.".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.len() == 1 {
|
if args.len() == 1 {
|
||||||
let mut channels = match arg!(args, 0, "channels") {
|
let mut channels = match arg!(args, scope, super_selector, 0, "channels") {
|
||||||
Value::List(v, ..) => v,
|
Value::List(v, ..) => v,
|
||||||
_ => return Err("Missing argument $channels.".into()),
|
_ => return Err("Missing argument $channels.".into()),
|
||||||
};
|
};
|
||||||
@ -60,11 +60,11 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
|
|
||||||
Ok(Value::Color(color))
|
Ok(Value::Color(color))
|
||||||
} else if args.len() == 2 {
|
} else if args.len() == 2 {
|
||||||
let color = match arg!(args, 0, "color") {
|
let color = match arg!(args, scope, super_selector, 0, "color") {
|
||||||
Value::Color(c) => c,
|
Value::Color(c) => c,
|
||||||
v => return Err(format!("$color: {} is not a color.", v).into()),
|
v => return Err(format!("$color: {} is not a color.", v).into()),
|
||||||
};
|
};
|
||||||
let alpha = match arg!(args, 1, "alpha") {
|
let alpha = match arg!(args, scope, super_selector, 1, "alpha") {
|
||||||
Value::Dimension(n, Unit::None) => n,
|
Value::Dimension(n, Unit::None) => n,
|
||||||
Value::Dimension(n, Unit::Percent) => n / Number::from(100),
|
Value::Dimension(n, Unit::Percent) => n / Number::from(100),
|
||||||
v @ Value::Dimension(..) => {
|
v @ Value::Dimension(..) => {
|
||||||
@ -76,7 +76,7 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
};
|
};
|
||||||
Ok(Value::Color(color.with_alpha(alpha)))
|
Ok(Value::Color(color.with_alpha(alpha)))
|
||||||
} else {
|
} else {
|
||||||
let red = match arg!(args, 0, "red") {
|
let red = match arg!(args, scope, super_selector, 0, "red") {
|
||||||
Value::Dimension(n, Unit::None) => n,
|
Value::Dimension(n, Unit::None) => n,
|
||||||
Value::Dimension(n, Unit::Percent) => {
|
Value::Dimension(n, Unit::Percent) => {
|
||||||
(n / Number::from(100)) * Number::from(255)
|
(n / Number::from(100)) * Number::from(255)
|
||||||
@ -88,7 +88,7 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
}
|
}
|
||||||
v => return Err(format!("$red: {} is not a number.", v).into()),
|
v => return Err(format!("$red: {} is not a number.", v).into()),
|
||||||
};
|
};
|
||||||
let green = match arg!(args, 1, "green") {
|
let green = match arg!(args, scope, super_selector, 1, "green") {
|
||||||
Value::Dimension(n, Unit::None) => n,
|
Value::Dimension(n, Unit::None) => n,
|
||||||
Value::Dimension(n, Unit::Percent) => {
|
Value::Dimension(n, Unit::Percent) => {
|
||||||
(n / Number::from(100)) * Number::from(255)
|
(n / Number::from(100)) * Number::from(255)
|
||||||
@ -100,7 +100,7 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
}
|
}
|
||||||
v => return Err(format!("$green: {} is not a number.", v).into()),
|
v => return Err(format!("$green: {} is not a number.", v).into()),
|
||||||
};
|
};
|
||||||
let blue = match arg!(args, 2, "blue") {
|
let blue = match arg!(args, scope, super_selector, 2, "blue") {
|
||||||
Value::Dimension(n, Unit::None) => n,
|
Value::Dimension(n, Unit::None) => n,
|
||||||
Value::Dimension(n, Unit::Percent) => {
|
Value::Dimension(n, Unit::Percent) => {
|
||||||
(n / Number::from(100)) * Number::from(255)
|
(n / Number::from(100)) * Number::from(255)
|
||||||
@ -114,6 +114,8 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
};
|
};
|
||||||
let alpha = match arg!(
|
let alpha = match arg!(
|
||||||
args,
|
args,
|
||||||
|
scope,
|
||||||
|
super_selector,
|
||||||
3,
|
3,
|
||||||
"alpha" = Value::Dimension(Number::one(), Unit::None)
|
"alpha" = Value::Dimension(Number::one(), Unit::None)
|
||||||
) {
|
) {
|
||||||
@ -132,13 +134,13 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"rgba".to_owned(),
|
"rgba".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
return Err("Missing argument $channels.".into());
|
return Err("Missing argument $channels.".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.len() == 1 {
|
if args.len() == 1 {
|
||||||
let mut channels = match arg!(args, 0, "channels") {
|
let mut channels = match arg!(args, scope, super_selector, 0, "channels") {
|
||||||
Value::List(v, ..) => v,
|
Value::List(v, ..) => v,
|
||||||
_ => return Err("Missing argument $channels.".into()),
|
_ => return Err("Missing argument $channels.".into()),
|
||||||
};
|
};
|
||||||
@ -182,11 +184,11 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
|
|
||||||
Ok(Value::Color(color))
|
Ok(Value::Color(color))
|
||||||
} else if args.len() == 2 {
|
} else if args.len() == 2 {
|
||||||
let color = match arg!(args, 0, "color") {
|
let color = match arg!(args, scope, super_selector, 0, "color") {
|
||||||
Value::Color(c) => c,
|
Value::Color(c) => c,
|
||||||
v => return Err(format!("$color: {} is not a color.", v).into()),
|
v => return Err(format!("$color: {} is not a color.", v).into()),
|
||||||
};
|
};
|
||||||
let alpha = match arg!(args, 1, "alpha") {
|
let alpha = match arg!(args, scope, super_selector, 1, "alpha") {
|
||||||
Value::Dimension(n, Unit::None) => n,
|
Value::Dimension(n, Unit::None) => n,
|
||||||
Value::Dimension(n, Unit::Percent) => n / Number::from(100),
|
Value::Dimension(n, Unit::Percent) => n / Number::from(100),
|
||||||
v @ Value::Dimension(..) => {
|
v @ Value::Dimension(..) => {
|
||||||
@ -198,7 +200,7 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
};
|
};
|
||||||
Ok(Value::Color(color.with_alpha(alpha)))
|
Ok(Value::Color(color.with_alpha(alpha)))
|
||||||
} else {
|
} else {
|
||||||
let red = match arg!(args, 0, "red") {
|
let red = match arg!(args, scope, super_selector, 0, "red") {
|
||||||
Value::Dimension(n, Unit::None) => n,
|
Value::Dimension(n, Unit::None) => n,
|
||||||
Value::Dimension(n, Unit::Percent) => {
|
Value::Dimension(n, Unit::Percent) => {
|
||||||
(n / Number::from(100)) * Number::from(255)
|
(n / Number::from(100)) * Number::from(255)
|
||||||
@ -210,7 +212,7 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
}
|
}
|
||||||
v => return Err(format!("$red: {} is not a number.", v).into()),
|
v => return Err(format!("$red: {} is not a number.", v).into()),
|
||||||
};
|
};
|
||||||
let green = match arg!(args, 1, "green") {
|
let green = match arg!(args, scope, super_selector, 1, "green") {
|
||||||
Value::Dimension(n, Unit::None) => n,
|
Value::Dimension(n, Unit::None) => n,
|
||||||
Value::Dimension(n, Unit::Percent) => {
|
Value::Dimension(n, Unit::Percent) => {
|
||||||
(n / Number::from(100)) * Number::from(255)
|
(n / Number::from(100)) * Number::from(255)
|
||||||
@ -222,7 +224,7 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
}
|
}
|
||||||
v => return Err(format!("$green: {} is not a number.", v).into()),
|
v => return Err(format!("$green: {} is not a number.", v).into()),
|
||||||
};
|
};
|
||||||
let blue = match arg!(args, 2, "blue") {
|
let blue = match arg!(args, scope, super_selector, 2, "blue") {
|
||||||
Value::Dimension(n, Unit::None) => n,
|
Value::Dimension(n, Unit::None) => n,
|
||||||
Value::Dimension(n, Unit::Percent) => {
|
Value::Dimension(n, Unit::Percent) => {
|
||||||
(n / Number::from(100)) * Number::from(255)
|
(n / Number::from(100)) * Number::from(255)
|
||||||
@ -236,6 +238,8 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
};
|
};
|
||||||
let alpha = match arg!(
|
let alpha = match arg!(
|
||||||
args,
|
args,
|
||||||
|
scope,
|
||||||
|
super_selector,
|
||||||
3,
|
3,
|
||||||
"alpha" = Value::Dimension(Number::one(), Unit::None)
|
"alpha" = Value::Dimension(Number::one(), Unit::None)
|
||||||
) {
|
) {
|
||||||
@ -254,9 +258,9 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"red".to_owned(),
|
"red".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
match arg!(args, 0, "color") {
|
match arg!(args, scope, super_selector, 0, "color") {
|
||||||
Value::Color(c) => Ok(Value::Dimension(c.red(), Unit::None)),
|
Value::Color(c) => Ok(Value::Dimension(c.red(), Unit::None)),
|
||||||
v => Err(format!("$color: {} is not a color.", v).into()),
|
v => Err(format!("$color: {} is not a color.", v).into()),
|
||||||
}
|
}
|
||||||
@ -264,9 +268,9 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"green".to_owned(),
|
"green".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
match arg!(args, 0, "color") {
|
match arg!(args, scope, super_selector, 0, "color") {
|
||||||
Value::Color(c) => Ok(Value::Dimension(c.green(), Unit::None)),
|
Value::Color(c) => Ok(Value::Dimension(c.green(), Unit::None)),
|
||||||
v => Err(format!("$color: {} is not a color.", v).into()),
|
v => Err(format!("$color: {} is not a color.", v).into()),
|
||||||
}
|
}
|
||||||
@ -274,9 +278,9 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"blue".to_owned(),
|
"blue".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
match arg!(args, 0, "color") {
|
match arg!(args, scope, super_selector, 0, "color") {
|
||||||
Value::Color(c) => Ok(Value::Dimension(c.blue(), Unit::None)),
|
Value::Color(c) => Ok(Value::Dimension(c.blue(), Unit::None)),
|
||||||
v => Err(format!("$color: {} is not a color.", v).into()),
|
v => Err(format!("$color: {} is not a color.", v).into()),
|
||||||
}
|
}
|
||||||
@ -284,20 +288,22 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"mix".to_owned(),
|
"mix".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 3);
|
max_args!(args, 3);
|
||||||
let color1 = match arg!(args, 0, "color1") {
|
let color1 = match arg!(args, scope, super_selector, 0, "color1") {
|
||||||
Value::Color(c) => c,
|
Value::Color(c) => c,
|
||||||
v => return Err(format!("$color1: {} is not a color.", v).into()),
|
v => return Err(format!("$color1: {} is not a color.", v).into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let color2 = match arg!(args, 1, "color2") {
|
let color2 = match arg!(args, scope, super_selector, 1, "color2") {
|
||||||
Value::Color(c) => c,
|
Value::Color(c) => c,
|
||||||
v => return Err(format!("$color2: {} is not a color.", v).into()),
|
v => return Err(format!("$color2: {} is not a color.", v).into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let weight = match arg!(
|
let weight = match arg!(
|
||||||
args,
|
args,
|
||||||
|
scope,
|
||||||
|
super_selector,
|
||||||
2,
|
2,
|
||||||
"weight" = Value::Dimension(Number::from(50), Unit::None)
|
"weight" = Value::Dimension(Number::from(50), Unit::None)
|
||||||
) {
|
) {
|
||||||
|
@ -10,9 +10,9 @@ use crate::value::{Number, Value};
|
|||||||
pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
||||||
f.insert(
|
f.insert(
|
||||||
"length".to_owned(),
|
"length".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
let len = match arg!(args, 0, "list") {
|
let len = match arg!(args, scope, super_selector, 0, "list") {
|
||||||
Value::List(v, ..) => Number::from(v.len()),
|
Value::List(v, ..) => Number::from(v.len()),
|
||||||
Value::Map(m) => Number::from(m.len()),
|
Value::Map(m) => Number::from(m.len()),
|
||||||
_ => Number::one(),
|
_ => Number::one(),
|
||||||
@ -22,14 +22,14 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"nth".to_owned(),
|
"nth".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 2);
|
max_args!(args, 2);
|
||||||
let list = match arg!(args, 0, "list") {
|
let list = match arg!(args, scope, super_selector, 0, "list") {
|
||||||
Value::List(v, ..) => v,
|
Value::List(v, ..) => v,
|
||||||
Value::Map(m) => m.entries(),
|
Value::Map(m) => m.entries(),
|
||||||
v => vec![v],
|
v => vec![v],
|
||||||
};
|
};
|
||||||
let n = match arg!(args, 1, "n") {
|
let n = match arg!(args, scope, super_selector, 1, "n") {
|
||||||
Value::Dimension(num, _) => num,
|
Value::Dimension(num, _) => num,
|
||||||
v => return Err(format!("$n: {} is not a number.", v).into()),
|
v => return Err(format!("$n: {} is not a number.", v).into()),
|
||||||
};
|
};
|
||||||
@ -60,10 +60,10 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"list-separator".to_owned(),
|
"list-separator".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
Ok(Value::Ident(
|
Ok(Value::Ident(
|
||||||
match arg!(args, 0, "list") {
|
match arg!(args, scope, super_selector, 0, "list") {
|
||||||
Value::List(_, sep, ..) => sep.name(),
|
Value::List(_, sep, ..) => sep.name(),
|
||||||
_ => ListSeparator::Space.name(),
|
_ => ListSeparator::Space.name(),
|
||||||
}
|
}
|
||||||
@ -74,14 +74,14 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"set-nth".to_owned(),
|
"set-nth".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 3);
|
max_args!(args, 3);
|
||||||
let (mut list, sep, brackets) = match arg!(args, 0, "list") {
|
let (mut list, sep, brackets) = match arg!(args, scope, super_selector, 0, "list") {
|
||||||
Value::List(v, sep, b) => (v, sep, b),
|
Value::List(v, sep, b) => (v, sep, b),
|
||||||
Value::Map(m) => (m.entries(), ListSeparator::Comma, Brackets::None),
|
Value::Map(m) => (m.entries(), ListSeparator::Comma, Brackets::None),
|
||||||
v => (vec![v], ListSeparator::Space, Brackets::None),
|
v => (vec![v], ListSeparator::Space, Brackets::None),
|
||||||
};
|
};
|
||||||
let n = match arg!(args, 1, "n") {
|
let n = match arg!(args, scope, super_selector, 1, "n") {
|
||||||
Value::Dimension(num, _) => num,
|
Value::Dimension(num, _) => num,
|
||||||
v => return Err(format!("$n: {} is not a number.", v).into()),
|
v => return Err(format!("$n: {} is not a number.", v).into()),
|
||||||
};
|
};
|
||||||
@ -102,7 +102,7 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
return Err(format!("$n: {} is not an int.", n).into());
|
return Err(format!("$n: {} is not an int.", n).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let val = arg!(args, 2, "value");
|
let val = arg!(args, scope, super_selector, 2, "value");
|
||||||
|
|
||||||
if n.is_positive() {
|
if n.is_positive() {
|
||||||
list[n.to_integer().to_usize().unwrap() - 1] = val;
|
list[n.to_integer().to_usize().unwrap() - 1] = val;
|
||||||
@ -115,15 +115,17 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"append".to_owned(),
|
"append".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 3);
|
max_args!(args, 3);
|
||||||
let (mut list, sep, brackets) = match arg!(args, 0, "list") {
|
let (mut list, sep, brackets) = match arg!(args, scope, super_selector, 0, "list") {
|
||||||
Value::List(v, sep, b) => (v, sep, b),
|
Value::List(v, sep, b) => (v, sep, b),
|
||||||
v => (vec![v], ListSeparator::Space, Brackets::None),
|
v => (vec![v], ListSeparator::Space, Brackets::None),
|
||||||
};
|
};
|
||||||
let val = arg!(args, 1, "val");
|
let val = arg!(args, scope, super_selector, 1, "val");
|
||||||
let sep = match arg!(
|
let sep = match arg!(
|
||||||
args,
|
args,
|
||||||
|
scope,
|
||||||
|
super_selector,
|
||||||
2,
|
2,
|
||||||
"separator" = Value::Ident("auto".to_owned(), QuoteKind::None)
|
"separator" = Value::Ident("auto".to_owned(), QuoteKind::None)
|
||||||
) {
|
) {
|
||||||
@ -145,20 +147,22 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"join".to_owned(),
|
"join".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 4);
|
max_args!(args, 4);
|
||||||
let (mut list1, sep1, brackets) = match arg!(args, 0, "list1") {
|
let (mut list1, sep1, brackets) = match arg!(args, scope, super_selector, 0, "list1") {
|
||||||
Value::List(v, sep, brackets) => (v, sep, brackets),
|
Value::List(v, sep, brackets) => (v, sep, brackets),
|
||||||
Value::Map(m) => (m.entries(), ListSeparator::Comma, Brackets::None),
|
Value::Map(m) => (m.entries(), ListSeparator::Comma, Brackets::None),
|
||||||
v => (vec![v], ListSeparator::Space, Brackets::None),
|
v => (vec![v], ListSeparator::Space, Brackets::None),
|
||||||
};
|
};
|
||||||
let (list2, sep2) = match arg!(args, 1, "list2") {
|
let (list2, sep2) = match arg!(args, scope, super_selector, 1, "list2") {
|
||||||
Value::List(v, sep, ..) => (v, sep),
|
Value::List(v, sep, ..) => (v, sep),
|
||||||
Value::Map(m) => (m.entries(), ListSeparator::Comma),
|
Value::Map(m) => (m.entries(), ListSeparator::Comma),
|
||||||
v => (vec![v], ListSeparator::Space),
|
v => (vec![v], ListSeparator::Space),
|
||||||
};
|
};
|
||||||
let sep = match arg!(
|
let sep = match arg!(
|
||||||
args,
|
args,
|
||||||
|
scope,
|
||||||
|
super_selector,
|
||||||
2,
|
2,
|
||||||
"separator" = Value::Ident("auto".to_owned(), QuoteKind::None)
|
"separator" = Value::Ident("auto".to_owned(), QuoteKind::None)
|
||||||
) {
|
) {
|
||||||
@ -181,6 +185,8 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
|
|
||||||
let brackets = match arg!(
|
let brackets = match arg!(
|
||||||
args,
|
args,
|
||||||
|
scope,
|
||||||
|
super_selector,
|
||||||
3,
|
3,
|
||||||
"bracketed" = Value::Ident("auto".to_owned(), QuoteKind::None)
|
"bracketed" = Value::Ident("auto".to_owned(), QuoteKind::None)
|
||||||
) {
|
) {
|
||||||
@ -204,27 +210,29 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"is-bracketed".to_owned(),
|
"is-bracketed".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
Ok(Value::bool(match arg!(args, 0, "list") {
|
Ok(Value::bool(
|
||||||
Value::List(.., brackets) => match brackets {
|
match arg!(args, scope, super_selector, 0, "list") {
|
||||||
Brackets::Bracketed => true,
|
Value::List(.., brackets) => match brackets {
|
||||||
Brackets::None => false,
|
Brackets::Bracketed => true,
|
||||||
|
Brackets::None => false,
|
||||||
|
},
|
||||||
|
_ => false,
|
||||||
},
|
},
|
||||||
_ => false,
|
))
|
||||||
}))
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"index".to_owned(),
|
"index".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 2);
|
max_args!(args, 2);
|
||||||
let list = match arg!(args, 0, "list") {
|
let list = match arg!(args, scope, super_selector, 0, "list") {
|
||||||
Value::List(v, ..) => v,
|
Value::List(v, ..) => v,
|
||||||
Value::Map(m) => m.entries(),
|
Value::Map(m) => m.entries(),
|
||||||
v => vec![v],
|
v => vec![v],
|
||||||
};
|
};
|
||||||
let value = arg!(args, 1, "value");
|
let value = arg!(args, scope, super_selector, 1, "value");
|
||||||
// TODO: find a way around this unwrap.
|
// TODO: find a way around this unwrap.
|
||||||
// It should be impossible to hit as the arg is
|
// It should be impossible to hit as the arg is
|
||||||
// evaluated prior to checking equality, but
|
// evaluated prior to checking equality, but
|
||||||
@ -242,9 +250,9 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"zip".to_owned(),
|
"zip".to_owned(),
|
||||||
Builtin::new(|args, _| {
|
Builtin::new(|args, scope, super_selector| {
|
||||||
let lists = args
|
let lists = args
|
||||||
.get_variadic()?
|
.get_variadic(scope, super_selector)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|x| match x {
|
.map(|x| match x {
|
||||||
Value::List(v, ..) => v,
|
Value::List(v, ..) => v,
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
macro_rules! arg {
|
macro_rules! arg {
|
||||||
($args:ident, $idx:literal, $name:literal) => {
|
($args:ident, $scope:ident, $super_selector:ident, $idx:literal, $name:literal) => {
|
||||||
match $args.remove_positional($idx) {
|
match $args.get_positional($idx, $scope, $super_selector) {
|
||||||
Some(v) => v.eval()?,
|
Some(v) => v?.eval()?,
|
||||||
None => match $args.remove_named($name.to_owned()) {
|
None => match $args.get_named($name.to_owned(), $scope, $super_selector) {
|
||||||
Some(v) => v.eval()?,
|
Some(v) => v?.eval()?,
|
||||||
None => return Err(concat!("Missing argument $", $name, ".").into()),
|
None => return Err(concat!("Missing argument $", $name, ".").into()),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
($args:ident, $idx:literal, $name:literal=$default:expr) => {
|
($args:ident, $scope:ident, $super_selector:ident, $idx:literal, $name:literal=$default:expr) => {
|
||||||
match $args.remove_positional($idx) {
|
match $args.get_positional($idx, $scope, $super_selector) {
|
||||||
Some(v) => v.eval()?,
|
Some(v) => v?.eval()?,
|
||||||
None => match $args.remove_named($name.to_owned()) {
|
None => match $args.get_named($name.to_owned(), $scope, $super_selector) {
|
||||||
Some(v) => v.eval()?,
|
Some(v) => v?.eval()?,
|
||||||
None => $default,
|
None => $default,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -20,15 +20,15 @@ macro_rules! arg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! named_arg {
|
macro_rules! named_arg {
|
||||||
($args:ident, $name:literal) => {
|
($args:ident, $scope:ident, $super_selector:ident, $name:literal) => {
|
||||||
match $args.remove_named($name.to_owned()) {
|
match $args.get_named($name.to_owned(), $scope, $super_selector) {
|
||||||
Some(v) => v.eval()?,
|
Some(v) => v?.eval()?,
|
||||||
None => return Err(concat!("Missing argument $", $name, ".").into()),
|
None => return Err(concat!("Missing argument $", $name, ".").into()),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
($args:ident, $name:literal=$default:expr) => {
|
($args:ident, $scope:ident, $super_selector:ident, $name:literal=$default:expr) => {
|
||||||
match $args.remove_named($name.to_owned()) {
|
match $args.get_named($name.to_owned(), $scope, $super_selector) {
|
||||||
Some(v) => v.eval()?,
|
Some(v) => v?.eval()?,
|
||||||
None => $default,
|
None => $default,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -7,10 +7,10 @@ use crate::value::{SassMap, Value};
|
|||||||
pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
||||||
f.insert(
|
f.insert(
|
||||||
"map-get".to_owned(),
|
"map-get".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 2);
|
max_args!(args, 2);
|
||||||
let key = arg!(args, 1, "key");
|
let key = arg!(args, scope, super_selector, 1, "key");
|
||||||
let map = match arg!(args, 0, "map") {
|
let map = match arg!(args, scope, super_selector, 0, "map") {
|
||||||
Value::Map(m) => m,
|
Value::Map(m) => m,
|
||||||
Value::List(v, ..) if v.is_empty() => SassMap::new(),
|
Value::List(v, ..) if v.is_empty() => SassMap::new(),
|
||||||
v => return Err(format!("$map: {} is not a map.", v).into()),
|
v => return Err(format!("$map: {} is not a map.", v).into()),
|
||||||
@ -20,10 +20,10 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"map-has-key".to_owned(),
|
"map-has-key".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 2);
|
max_args!(args, 2);
|
||||||
let key = arg!(args, 1, "key");
|
let key = arg!(args, scope, super_selector, 1, "key");
|
||||||
let map = match arg!(args, 0, "map") {
|
let map = match arg!(args, scope, super_selector, 0, "map") {
|
||||||
Value::Map(m) => m,
|
Value::Map(m) => m,
|
||||||
Value::List(v, ..) if v.is_empty() => SassMap::new(),
|
Value::List(v, ..) if v.is_empty() => SassMap::new(),
|
||||||
v => return Err(format!("$map: {} is not a map.", v).into()),
|
v => return Err(format!("$map: {} is not a map.", v).into()),
|
||||||
@ -33,9 +33,9 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"map-keys".to_owned(),
|
"map-keys".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
let map = match arg!(args, 0, "map") {
|
let map = match arg!(args, scope, super_selector, 0, "map") {
|
||||||
Value::Map(m) => m,
|
Value::Map(m) => m,
|
||||||
Value::List(v, ..) if v.is_empty() => SassMap::new(),
|
Value::List(v, ..) if v.is_empty() => SassMap::new(),
|
||||||
v => return Err(format!("$map: {} is not a map.", v).into()),
|
v => return Err(format!("$map: {} is not a map.", v).into()),
|
||||||
@ -49,9 +49,9 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"map-values".to_owned(),
|
"map-values".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
let map = match arg!(args, 0, "map") {
|
let map = match arg!(args, scope, super_selector, 0, "map") {
|
||||||
Value::Map(m) => m,
|
Value::Map(m) => m,
|
||||||
Value::List(v, ..) if v.is_empty() => SassMap::new(),
|
Value::List(v, ..) if v.is_empty() => SassMap::new(),
|
||||||
v => return Err(format!("$map: {} is not a map.", v).into()),
|
v => return Err(format!("$map: {} is not a map.", v).into()),
|
||||||
@ -65,14 +65,14 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"map-merge".to_owned(),
|
"map-merge".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 2);
|
max_args!(args, 2);
|
||||||
let mut map1 = match arg!(args, 0, "map1") {
|
let mut map1 = match arg!(args, scope, super_selector, 0, "map1") {
|
||||||
Value::Map(m) => m,
|
Value::Map(m) => m,
|
||||||
Value::List(v, ..) if v.is_empty() => SassMap::new(),
|
Value::List(v, ..) if v.is_empty() => SassMap::new(),
|
||||||
v => return Err(format!("$map1: {} is not a map.", v).into()),
|
v => return Err(format!("$map1: {} is not a map.", v).into()),
|
||||||
};
|
};
|
||||||
let map2 = match arg!(args, 1, "map2") {
|
let map2 = match arg!(args, scope, super_selector, 1, "map2") {
|
||||||
Value::Map(m) => m,
|
Value::Map(m) => m,
|
||||||
Value::List(v, ..) if v.is_empty() => SassMap::new(),
|
Value::List(v, ..) if v.is_empty() => SassMap::new(),
|
||||||
v => return Err(format!("$map2: {} is not a map.", v).into()),
|
v => return Err(format!("$map2: {} is not a map.", v).into()),
|
||||||
@ -83,13 +83,13 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"map-remove".to_owned(),
|
"map-remove".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
let mut map = match arg!(args, 0, "map") {
|
let mut map = match arg!(args, scope, super_selector, 0, "map") {
|
||||||
Value::Map(m) => m,
|
Value::Map(m) => m,
|
||||||
Value::List(v, ..) if v.is_empty() => SassMap::new(),
|
Value::List(v, ..) if v.is_empty() => SassMap::new(),
|
||||||
v => return Err(format!("$map: {} is not a map.", v).into()),
|
v => return Err(format!("$map: {} is not a map.", v).into()),
|
||||||
};
|
};
|
||||||
let keys = args.get_variadic()?;
|
let keys = args.get_variadic(scope, super_selector)?;
|
||||||
for key in keys {
|
for key in keys {
|
||||||
map.remove(&key);
|
map.remove(&key);
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,9 @@ use crate::value::{Number, Value};
|
|||||||
pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
||||||
f.insert(
|
f.insert(
|
||||||
"percentage".to_owned(),
|
"percentage".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
let num = match arg!(args, 0, "number") {
|
let num = match arg!(args, scope, super_selector, 0, "number") {
|
||||||
Value::Dimension(n, Unit::None) => n * Number::from(100),
|
Value::Dimension(n, Unit::None) => n * Number::from(100),
|
||||||
v @ Value::Dimension(..) => {
|
v @ Value::Dimension(..) => {
|
||||||
return Err(format!("$number: Expected {} to have no units.", v).into())
|
return Err(format!("$number: Expected {} to have no units.", v).into())
|
||||||
@ -21,9 +21,9 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"round".to_owned(),
|
"round".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
match arg!(args, 0, "number") {
|
match arg!(args, scope, super_selector, 0, "number") {
|
||||||
Value::Dimension(n, u) => Ok(Value::Dimension(n.round(), u)),
|
Value::Dimension(n, u) => Ok(Value::Dimension(n.round(), u)),
|
||||||
v => Err(format!("$number: {} is not a number.", v).into()),
|
v => Err(format!("$number: {} is not a number.", v).into()),
|
||||||
}
|
}
|
||||||
@ -31,9 +31,9 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"ceil".to_owned(),
|
"ceil".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
match arg!(args, 0, "number") {
|
match arg!(args, scope, super_selector, 0, "number") {
|
||||||
Value::Dimension(n, u) => Ok(Value::Dimension(n.ceil(), u)),
|
Value::Dimension(n, u) => Ok(Value::Dimension(n.ceil(), u)),
|
||||||
v => Err(format!("$number: {} is not a number.", v).into()),
|
v => Err(format!("$number: {} is not a number.", v).into()),
|
||||||
}
|
}
|
||||||
@ -41,9 +41,9 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"floor".to_owned(),
|
"floor".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
match arg!(args, 0, "number") {
|
match arg!(args, scope, super_selector, 0, "number") {
|
||||||
Value::Dimension(n, u) => Ok(Value::Dimension(n.floor(), u)),
|
Value::Dimension(n, u) => Ok(Value::Dimension(n.floor(), u)),
|
||||||
v => Err(format!("$number: {} is not a number.", v).into()),
|
v => Err(format!("$number: {} is not a number.", v).into()),
|
||||||
}
|
}
|
||||||
@ -51,9 +51,9 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"abs".to_owned(),
|
"abs".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
match arg!(args, 0, "number") {
|
match arg!(args, scope, super_selector, 0, "number") {
|
||||||
Value::Dimension(n, u) => Ok(Value::Dimension(n.abs(), u)),
|
Value::Dimension(n, u) => Ok(Value::Dimension(n.abs(), u)),
|
||||||
v => Err(format!("$number: {} is not a number.", v).into()),
|
v => Err(format!("$number: {} is not a number.", v).into()),
|
||||||
}
|
}
|
||||||
@ -61,13 +61,13 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"comparable".to_owned(),
|
"comparable".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 2);
|
max_args!(args, 2);
|
||||||
let unit1 = match arg!(args, 0, "number1") {
|
let unit1 = match arg!(args, scope, super_selector, 0, "number1") {
|
||||||
Value::Dimension(_, u) => u,
|
Value::Dimension(_, u) => u,
|
||||||
v => return Err(format!("$number1: {} is not a number.", v).into()),
|
v => return Err(format!("$number1: {} is not a number.", v).into()),
|
||||||
};
|
};
|
||||||
let unit2 = match arg!(args, 1, "number2") {
|
let unit2 = match arg!(args, scope, super_selector, 1, "number2") {
|
||||||
Value::Dimension(_, u) => u,
|
Value::Dimension(_, u) => u,
|
||||||
v => return Err(format!("$number2: {} is not a number.", v).into()),
|
v => return Err(format!("$number2: {} is not a number.", v).into()),
|
||||||
};
|
};
|
||||||
|
@ -9,20 +9,20 @@ use crate::value::{SassFunction, Value};
|
|||||||
pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
||||||
f.insert(
|
f.insert(
|
||||||
"if".to_owned(),
|
"if".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 3);
|
max_args!(args, 3);
|
||||||
if arg!(args, 0, "condition").is_true()? {
|
if arg!(args, scope, super_selector, 0, "condition").is_true()? {
|
||||||
Ok(arg!(args, 1, "if-true"))
|
Ok(arg!(args, scope, super_selector, 1, "if-true"))
|
||||||
} else {
|
} else {
|
||||||
Ok(arg!(args, 2, "if-false"))
|
Ok(arg!(args, scope, super_selector, 2, "if-false"))
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"feature-exists".to_owned(),
|
"feature-exists".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
match arg!(args, 0, "feature") {
|
match arg!(args, scope, super_selector, 0, "feature") {
|
||||||
Value::Ident(s, _) => match s.as_str() {
|
Value::Ident(s, _) => match s.as_str() {
|
||||||
// A local variable will shadow a global variable unless
|
// A local variable will shadow a global variable unless
|
||||||
// `!global` is used.
|
// `!global` is used.
|
||||||
@ -47,9 +47,9 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"unit".to_owned(),
|
"unit".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
let unit = match arg!(args, 0, "number") {
|
let unit = match arg!(args, scope, super_selector, 0, "number") {
|
||||||
Value::Dimension(_, u) => u.to_string(),
|
Value::Dimension(_, u) => u.to_string(),
|
||||||
v => return Err(format!("$number: {} is not a number.", v).into()),
|
v => return Err(format!("$number: {} is not a number.", v).into()),
|
||||||
};
|
};
|
||||||
@ -58,17 +58,17 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"type-of".to_owned(),
|
"type-of".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
let value = arg!(args, 0, "value");
|
let value = arg!(args, scope, super_selector, 0, "value");
|
||||||
Ok(Value::Ident(value.kind()?.to_owned(), QuoteKind::None))
|
Ok(Value::Ident(value.kind()?.to_owned(), QuoteKind::None))
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"unitless".to_owned(),
|
"unitless".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
match arg!(args, 0, "number") {
|
match arg!(args, scope, super_selector, 0, "number") {
|
||||||
Value::Dimension(_, Unit::None) => Ok(Value::True),
|
Value::Dimension(_, Unit::None) => Ok(Value::True),
|
||||||
Value::Dimension(_, _) => Ok(Value::False),
|
Value::Dimension(_, _) => Ok(Value::False),
|
||||||
_ => Ok(Value::True),
|
_ => Ok(Value::True),
|
||||||
@ -77,10 +77,10 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"inspect".to_owned(),
|
"inspect".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
Ok(Value::Ident(
|
Ok(Value::Ident(
|
||||||
match arg!(args, 0, "value") {
|
match arg!(args, scope, super_selector, 0, "value") {
|
||||||
Value::List(v, _, brackets) if v.is_empty() => match brackets {
|
Value::List(v, _, brackets) if v.is_empty() => match brackets {
|
||||||
Brackets::None => "()".to_string(),
|
Brackets::None => "()".to_string(),
|
||||||
Brackets::Bracketed => "[]".to_string(),
|
Brackets::Bracketed => "[]".to_string(),
|
||||||
@ -94,9 +94,9 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"variable-exists".to_owned(),
|
"variable-exists".to_owned(),
|
||||||
Builtin::new(|mut args, scope| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
match arg!(args, 0, "name") {
|
match arg!(args, scope, super_selector, 0, "name") {
|
||||||
Value::Ident(s, _) => Ok(Value::bool(scope.var_exists(&s))),
|
Value::Ident(s, _) => Ok(Value::bool(scope.var_exists(&s))),
|
||||||
v => Err(format!("$name: {} is not a string.", v).into()),
|
v => Err(format!("$name: {} is not a string.", v).into()),
|
||||||
}
|
}
|
||||||
@ -104,9 +104,9 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"global-variable-exists".to_owned(),
|
"global-variable-exists".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
match arg!(args, 0, "name") {
|
match arg!(args, scope, super_selector, 0, "name") {
|
||||||
Value::Ident(s, _) => Ok(Value::bool(global_var_exists(&s))),
|
Value::Ident(s, _) => Ok(Value::bool(global_var_exists(&s))),
|
||||||
v => Err(format!("$name: {} is not a string.", v).into()),
|
v => Err(format!("$name: {} is not a string.", v).into()),
|
||||||
}
|
}
|
||||||
@ -114,9 +114,9 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"mixin-exists".to_owned(),
|
"mixin-exists".to_owned(),
|
||||||
Builtin::new(|mut args, scope| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 2);
|
max_args!(args, 2);
|
||||||
match arg!(args, 0, "name") {
|
match arg!(args, scope, super_selector, 0, "name") {
|
||||||
Value::Ident(s, _) => Ok(Value::bool(scope.mixin_exists(&s))),
|
Value::Ident(s, _) => Ok(Value::bool(scope.mixin_exists(&s))),
|
||||||
v => Err(format!("$name: {} is not a string.", v).into()),
|
v => Err(format!("$name: {} is not a string.", v).into()),
|
||||||
}
|
}
|
||||||
@ -124,9 +124,9 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"function-exists".to_owned(),
|
"function-exists".to_owned(),
|
||||||
Builtin::new(|mut args, scope| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 2);
|
max_args!(args, 2);
|
||||||
match arg!(args, 0, "name") {
|
match arg!(args, scope, super_selector, 0, "name") {
|
||||||
Value::Ident(s, _) => Ok(Value::bool(
|
Value::Ident(s, _) => Ok(Value::bool(
|
||||||
scope.fn_exists(&s) || GLOBAL_FUNCTIONS.contains_key(&s),
|
scope.fn_exists(&s) || GLOBAL_FUNCTIONS.contains_key(&s),
|
||||||
)),
|
)),
|
||||||
@ -136,14 +136,14 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"get-function".to_owned(),
|
"get-function".to_owned(),
|
||||||
Builtin::new(|mut args, scope| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 3);
|
max_args!(args, 3);
|
||||||
let name = match arg!(args, 0, "name") {
|
let name = match arg!(args, scope, super_selector, 0, "name") {
|
||||||
Value::Ident(s, _) => s,
|
Value::Ident(s, _) => s,
|
||||||
v => return Err(format!("$name: {} is not a string.", v).into()),
|
v => return Err(format!("$name: {} is not a string.", v).into()),
|
||||||
};
|
};
|
||||||
let css = arg!(args, 1, "css" = Value::False).is_true()?;
|
let css = arg!(args, scope, super_selector, 1, "css" = Value::False).is_true()?;
|
||||||
let module = match arg!(args, 2, "module" = Value::Null) {
|
let module = match arg!(args, scope, super_selector, 2, "module" = Value::Null) {
|
||||||
Value::Ident(s, ..) => Some(s),
|
Value::Ident(s, ..) => Some(s),
|
||||||
Value::Null => None,
|
Value::Null => None,
|
||||||
v => return Err(format!("$module: {} is not a string.", v).into()),
|
v => return Err(format!("$module: {} is not a string.", v).into()),
|
||||||
@ -166,12 +166,12 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"call".to_owned(),
|
"call".to_owned(),
|
||||||
Builtin::new(|mut args, scope| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
let func = match arg!(args, 0, "function") {
|
let func = match arg!(args, scope, super_selector, 0, "function") {
|
||||||
Value::Function(f) => f,
|
Value::Function(f) => f,
|
||||||
v => return Err(format!("$function: {} is not a function reference.", v).into()),
|
v => return Err(format!("$function: {} is not a function reference.", v).into()),
|
||||||
};
|
};
|
||||||
func.call(args.decrement(), scope)
|
func.call(args.decrement(), scope, super_selector)
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
|
|||||||
use crate::args::CallArgs;
|
use crate::args::CallArgs;
|
||||||
use crate::error::SassResult;
|
use crate::error::SassResult;
|
||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
|
use crate::selector::Selector;
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
@ -22,9 +23,12 @@ static FUNCTION_COUNT: AtomicUsize = AtomicUsize::new(0);
|
|||||||
|
|
||||||
// TODO: impl Fn
|
// TODO: impl Fn
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct Builtin(pub fn(CallArgs, &Scope) -> SassResult<Value>, usize);
|
pub(crate) struct Builtin(
|
||||||
|
pub fn(CallArgs, &Scope, &Selector) -> SassResult<Value>,
|
||||||
|
usize,
|
||||||
|
);
|
||||||
impl Builtin {
|
impl Builtin {
|
||||||
pub fn new(body: fn(CallArgs, &Scope) -> SassResult<Value>) -> Builtin {
|
pub fn new(body: fn(CallArgs, &Scope, &Selector) -> SassResult<Value>) -> Builtin {
|
||||||
let count = FUNCTION_COUNT.fetch_add(1, Ordering::Relaxed);
|
let count = FUNCTION_COUNT.fetch_add(1, Ordering::Relaxed);
|
||||||
Self(body, count)
|
Self(body, count)
|
||||||
}
|
}
|
||||||
|
@ -11,9 +11,9 @@ use crate::value::{Number, Value};
|
|||||||
pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
||||||
f.insert(
|
f.insert(
|
||||||
"to-upper-case".to_owned(),
|
"to-upper-case".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
match arg!(args, 0, "string") {
|
match arg!(args, scope, super_selector, 0, "string") {
|
||||||
Value::Ident(i, q) => Ok(Value::Ident(i.to_ascii_uppercase(), q)),
|
Value::Ident(i, q) => Ok(Value::Ident(i.to_ascii_uppercase(), q)),
|
||||||
v => Err(format!("$string: {} is not a string.", v).into()),
|
v => Err(format!("$string: {} is not a string.", v).into()),
|
||||||
}
|
}
|
||||||
@ -21,9 +21,9 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"to-lower-case".to_owned(),
|
"to-lower-case".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
match arg!(args, 0, "string") {
|
match arg!(args, scope, super_selector, 0, "string") {
|
||||||
Value::Ident(i, q) => Ok(Value::Ident(i.to_ascii_lowercase(), q)),
|
Value::Ident(i, q) => Ok(Value::Ident(i.to_ascii_lowercase(), q)),
|
||||||
v => Err(format!("$string: {} is not a string.", v).into()),
|
v => Err(format!("$string: {} is not a string.", v).into()),
|
||||||
}
|
}
|
||||||
@ -31,9 +31,9 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"str-length".to_owned(),
|
"str-length".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
match arg!(args, 0, "string") {
|
match arg!(args, scope, super_selector, 0, "string") {
|
||||||
Value::Ident(i, _) => Ok(Value::Dimension(
|
Value::Ident(i, _) => Ok(Value::Dimension(
|
||||||
Number::from(i.chars().count()),
|
Number::from(i.chars().count()),
|
||||||
Unit::None,
|
Unit::None,
|
||||||
@ -44,9 +44,9 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"quote".to_owned(),
|
"quote".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
match arg!(args, 0, "string") {
|
match arg!(args, scope, super_selector, 0, "string") {
|
||||||
Value::Ident(i, _) => Ok(Value::Ident(i, QuoteKind::Double)),
|
Value::Ident(i, _) => Ok(Value::Ident(i, QuoteKind::Double)),
|
||||||
v => Err(format!("$string: {} is not a string.", v).into()),
|
v => Err(format!("$string: {} is not a string.", v).into()),
|
||||||
}
|
}
|
||||||
@ -54,9 +54,9 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"unquote".to_owned(),
|
"unquote".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 1);
|
max_args!(args, 1);
|
||||||
match arg!(args, 0, "string") {
|
match arg!(args, scope, super_selector, 0, "string") {
|
||||||
i @ Value::Ident(..) => Ok(i.unquote()),
|
i @ Value::Ident(..) => Ok(i.unquote()),
|
||||||
v => Err(format!("$string: {} is not a string.", v).into()),
|
v => Err(format!("$string: {} is not a string.", v).into()),
|
||||||
}
|
}
|
||||||
@ -64,14 +64,14 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"str-slice".to_owned(),
|
"str-slice".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 3);
|
max_args!(args, 3);
|
||||||
let (string, quotes) = match arg!(args, 0, "string") {
|
let (string, quotes) = match arg!(args, scope, super_selector, 0, "string") {
|
||||||
Value::Ident(s, q) => (s, q),
|
Value::Ident(s, q) => (s, q),
|
||||||
v => return Err(format!("$string: {} is not a string.", v).into()),
|
v => return Err(format!("$string: {} is not a string.", v).into()),
|
||||||
};
|
};
|
||||||
let str_len = string.chars().count();
|
let str_len = string.chars().count();
|
||||||
let start = match arg!(args, 1, "start-at") {
|
let start = match arg!(args, scope, super_selector, 1, "start-at") {
|
||||||
Value::Dimension(n, Unit::None) if n.is_decimal() => {
|
Value::Dimension(n, Unit::None) if n.is_decimal() => {
|
||||||
return Err(format!("{} is not an int.", n).into())
|
return Err(format!("{} is not an int.", n).into())
|
||||||
}
|
}
|
||||||
@ -88,7 +88,7 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
}
|
}
|
||||||
v => return Err(format!("$start-at: {} is not a number.", v).into()),
|
v => return Err(format!("$start-at: {} is not a number.", v).into()),
|
||||||
};
|
};
|
||||||
let mut end = match arg!(args, 2, "end-at" = Value::Null) {
|
let mut end = match arg!(args, scope, super_selector, 2, "end-at" = Value::Null) {
|
||||||
Value::Dimension(n, Unit::None) if n.is_decimal() => {
|
Value::Dimension(n, Unit::None) if n.is_decimal() => {
|
||||||
return Err(format!("{} is not an int.", n).into())
|
return Err(format!("{} is not an int.", n).into())
|
||||||
}
|
}
|
||||||
@ -127,14 +127,14 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"str-index".to_owned(),
|
"str-index".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 2);
|
max_args!(args, 2);
|
||||||
let s1 = match arg!(args, 0, "string") {
|
let s1 = match arg!(args, scope, super_selector, 0, "string") {
|
||||||
Value::Ident(i, _) => i,
|
Value::Ident(i, _) => i,
|
||||||
v => return Err(format!("$string: {} is not a string.", v).into()),
|
v => return Err(format!("$string: {} is not a string.", v).into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let substr = match arg!(args, 1, "substring") {
|
let substr = match arg!(args, scope, super_selector, 1, "substring") {
|
||||||
Value::Ident(i, _) => i,
|
Value::Ident(i, _) => i,
|
||||||
v => return Err(format!("$substring: {} is not a string.", v).into()),
|
v => return Err(format!("$substring: {} is not a string.", v).into()),
|
||||||
};
|
};
|
||||||
@ -147,19 +147,19 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
|||||||
);
|
);
|
||||||
f.insert(
|
f.insert(
|
||||||
"str-insert".to_owned(),
|
"str-insert".to_owned(),
|
||||||
Builtin::new(|mut args, _| {
|
Builtin::new(|mut args, scope, super_selector| {
|
||||||
max_args!(args, 3);
|
max_args!(args, 3);
|
||||||
let (s1, quotes) = match arg!(args, 0, "string") {
|
let (s1, quotes) = match arg!(args, scope, super_selector, 0, "string") {
|
||||||
Value::Ident(i, q) => (i, q.normalize()),
|
Value::Ident(i, q) => (i, q.normalize()),
|
||||||
v => return Err(format!("$string: {} is not a string.", v).into()),
|
v => return Err(format!("$string: {} is not a string.", v).into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let substr = match arg!(args, 1, "insert") {
|
let substr = match arg!(args, scope, super_selector, 1, "insert") {
|
||||||
Value::Ident(i, _) => i,
|
Value::Ident(i, _) => i,
|
||||||
v => return Err(format!("$insert: {} is not a string.", v).into()),
|
v => return Err(format!("$insert: {} is not a string.", v).into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let index = match arg!(args, 2, "index") {
|
let index = match arg!(args, scope, super_selector, 2, "index") {
|
||||||
Value::Dimension(n, Unit::None) if n.is_decimal() => {
|
Value::Dimension(n, Unit::None) if n.is_decimal() => {
|
||||||
return Err(format!("$index: {} is not an int.", n).into())
|
return Err(format!("$index: {} is not an int.", n).into())
|
||||||
}
|
}
|
||||||
|
@ -29,11 +29,19 @@ impl SassFunction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call(self, args: CallArgs, scope: &Scope) -> SassResult<Value> {
|
pub fn call(
|
||||||
|
self,
|
||||||
|
args: CallArgs,
|
||||||
|
scope: &Scope,
|
||||||
|
super_selector: &Selector,
|
||||||
|
) -> SassResult<Value> {
|
||||||
match self {
|
match self {
|
||||||
Self::Builtin(f, ..) => f.0(args, scope),
|
Self::Builtin(f, ..) => f.0(args, scope, super_selector),
|
||||||
// todo: superselector
|
// todo: superselector
|
||||||
Self::UserDefined(f, ..) => f.clone().args(args)?.call(&Selector::new(), f.body()),
|
Self::UserDefined(f, ..) => f
|
||||||
|
.clone()
|
||||||
|
.args(args, scope, super_selector)?
|
||||||
|
.call(&Selector::new(), f.body()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -371,6 +371,7 @@ impl Value {
|
|||||||
return Ok(IntermediateValue::Value(f.0(
|
return Ok(IntermediateValue::Value(f.0(
|
||||||
eat_call_args(toks, scope, super_selector)?,
|
eat_call_args(toks, scope, super_selector)?,
|
||||||
scope,
|
scope,
|
||||||
|
super_selector,
|
||||||
)?))
|
)?))
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
@ -408,7 +409,11 @@ impl Value {
|
|||||||
};
|
};
|
||||||
Ok(IntermediateValue::Value(
|
Ok(IntermediateValue::Value(
|
||||||
func.clone()
|
func.clone()
|
||||||
.args(eat_call_args(toks, scope, super_selector)?)?
|
.args(
|
||||||
|
eat_call_args(toks, scope, super_selector)?,
|
||||||
|
scope,
|
||||||
|
super_selector,
|
||||||
|
)?
|
||||||
.call(super_selector, func.body())?,
|
.call(super_selector, func.body())?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -25,3 +25,8 @@ test!(
|
|||||||
"@function foo($a...) {\n @return $a;\n}\n\na {\n color: foo(1, 2, 3, 4, 5);\n}\n",
|
"@function foo($a...) {\n @return $a;\n}\n\na {\n color: foo(1, 2, 3, 4, 5);\n}\n",
|
||||||
"a {\n color: 1, 2, 3, 4, 5;\n}\n"
|
"a {\n color: 1, 2, 3, 4, 5;\n}\n"
|
||||||
);
|
);
|
||||||
|
test!(
|
||||||
|
default_args_are_lazily_evaluated,
|
||||||
|
"$da: a;\n\n@mixin foo($x: $da) {\n color: $x;\n}\n$da: b;\n\na {\n @include foo();\n}\n",
|
||||||
|
"a {\n color: b;\n}\n"
|
||||||
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user