Skip to content

Commit

Permalink
Support slash-separated lists.
Browse files Browse the repository at this point in the history
  • Loading branch information
kaj committed May 30, 2021
1 parent b470f13 commit 0291193
Show file tree
Hide file tree
Showing 28 changed files with 108 additions and 135 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ project adheres to

### Breaking changes

* `value::Unit` has a new alternative.
* `value::Unit` and `value::ListSeparator` has new alternatives.
* The `List` alternative in `sass::Value` and `css::Value` is modified.
* The `Use` alternative in `sass::Item` is modified, and `Forward` added.

Expand All @@ -28,6 +28,7 @@ project adheres to
* Implement `math.div` function.
* Improved parameter checking for `hwb`, `alpha`, and `invert`
functions in `sass:color` module.
* Support slash-separated lists. PR #111.
* The `if` function evaluates its arguments lazily. Issue #107.
* The `--include-path` cli argument is now named `--load-path`.
* Update sass-spec test suite to 2021-05-24. Also include "other"
Expand Down
20 changes: 5 additions & 15 deletions src/css/valueformat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ impl<'a> Display for Formatted<'a, Value> {
}
}
Value::List(ref v, sep, brackets) => {
let sep = sep.unwrap_or_default();
let introspect = self.format.is_introspection();
if introspect && v.is_empty() && !brackets {
return out.write_str("()");
Expand All @@ -54,9 +55,7 @@ impl<'a> Display for Formatted<'a, Value> {
let needs_paren = match *v {
Value::List(ref v, inner, false) => {
((brackets || introspect)
&& (sep != Some(ListSeparator::Comma)
|| inner
== Some(ListSeparator::Comma)))
&& (sep <= inner.unwrap_or_default()))
&& !(introspect && v.len() < 2)
}
_ => false,
Expand All @@ -70,24 +69,15 @@ impl<'a> Display for Formatted<'a, Value> {
.collect::<Vec<_>>();
let t = if self.format.is_introspection()
&& t.len() == 1
&& sep == Some(ListSeparator::Comma)
&& sep > ListSeparator::Space
{
if self.format.is_introspection() && !brackets {
format!("({},)", t[0])
format!("({}{})", t[0], sep.sep(true))
} else {
format!("{},", t[0])
}
} else {
t.join(match sep {
Some(ListSeparator::Comma) => {
if self.format.is_compressed() {
","
} else {
", "
}
}
_ => " ",
})
t.join(sep.sep(self.format.is_compressed()))
};
if brackets {
out.write_str("[")?;
Expand Down
7 changes: 6 additions & 1 deletion src/sass/functions/color/hsl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ fn do_hsla(fn_name: &str, s: &Scope) -> Result<Value, Error> {
} else {
hue.clone()
} {
if let Some((h, s, v, a)) = values_from_list(&vec) {
if bracketed {
return Err(Error::BadValue(
"Error: $channels must be an unbracketed list.".into(),
));
}
if let Some((h, s, v, a)) = values_from_list(&vec, sep) {
Ok(hsla_from_values(&h, &s, &v, &a)?
.unwrap_or_else(|| make_call(fn_name, vec![h, s, v, a])))
} else {
Expand Down
37 changes: 22 additions & 15 deletions src/sass/functions/color/rgb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ fn do_rgba(fn_name: &str, s: &Scope) -> Result<Value, Error> {
} else {
red.clone()
} {
if let Some((r, g, b, a)) = values_from_list(&vec) {
if bracketed {
return Err(Error::BadValue(
"Error: $channels must be an unbracketed list.".into(),
));
}
if let Some((r, g, b, a)) = values_from_list(&vec, sep) {
Ok(rgba_from_values(&r, &g, &b, &a)?
.unwrap_or_else(|| make_call(fn_name, vec![r, g, b, a])))
} else {
Expand All @@ -54,26 +59,28 @@ fn do_rgba(fn_name: &str, s: &Scope) -> Result<Value, Error> {

pub fn values_from_list(
vec: &[Value],
sep: Option<ListSeparator>,
) -> Option<(Value, Value, Value, Value)> {
use crate::value::Operator::Div;

if vec.len() == 3 {
if let Value::BinOp(a, _, Div, _, b) = &vec[2] {
if let (Value::Numeric(..), Value::Numeric(..)) = (&**a, &**b) {
Some((vec[0].clone(), vec[1].clone(), *a.clone(), *b.clone()))
use Value::{BinOp, Null, Numeric};
match vec {
[Value::List(rgb, _, _), a] if sep == Some(ListSeparator::Slash) => {
match &rgb[..] {
[r, g, b] => {
Some((r.clone(), g.clone(), b.clone(), a.clone()))
}
_ => None,
}
}
[r, g, BinOp(b, _, Div, _, a)] => {
if let (b @ Numeric(..), a @ Numeric(..)) = (&**b, &**a) {
Some((r.clone(), g.clone(), b.clone(), a.clone()))
} else {
None
}
} else {
Some((
vec[0].clone(),
vec[1].clone(),
vec[2].clone(),
Value::Null,
))
}
} else {
None
[r, g, b] => Some((r.clone(), g.clone(), b.clone(), Null)),
_ => None,
}
}

Expand Down
15 changes: 14 additions & 1 deletion src/sass/functions/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,23 @@ pub fn create_module() -> Scope {
def!(f, separator(list), |s| Ok(Value::Literal(
match s.get("list")? {
Value::List(_, Some(ListSeparator::Comma), _) => "comma",
Value::List(_, Some(ListSeparator::Slash), _) => "slash",
Value::Map(ref map) if map.len() == 0 => "space",
Value::Map(_) => "comma",
_ => "space",
}
.into(),
Quotes::None
)));
def_va!(f, slash(elements), |s| {
let (list, _, _) = get_list(s.get("elements")?);
if list.len() < 2 {
return Err(Error::BadValue(
"Error: At least two elements are required.".into(),
));
}
Ok(Value::List(list, Some(ListSeparator::Slash), false))
});
def!(f, nth(list, n), |s| {
let n = get_integer(s, name!(n))?;
match s.get("list")? {
Expand Down Expand Up @@ -154,9 +164,12 @@ pub fn expose(m: &Scope, global: &mut FunctionMap) {
fn check_separator(v: Value) -> Result<Option<ListSeparator>, String> {
match check::string(v)?.0.as_ref() {
"comma" => Ok(Some(ListSeparator::Comma)),
"slash" => Ok(Some(ListSeparator::Slash)),
"space" => Ok(Some(ListSeparator::Space)),
"auto" => Ok(None),
_ => Err("Must be \"space\", \"comma\", or \"auto\"".into()),
_ => {
Err("Must be \"space\", \"comma\", \"slash\", or \"auto\"".into())
}
}
}

Expand Down
31 changes: 29 additions & 2 deletions src/value/list_separator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,35 @@
/// whitespace-separated list.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum ListSeparator {
/// The list is comma-separated.
Comma,
/// The list is space-separated.
Space,
/// The list is slash-separated.
Slash,
/// The list is comma-separated.
Comma,
}

impl ListSeparator {
/// Get the actutual separator string.
pub fn sep(&self, compressed: bool) -> &'static str {
match self {
ListSeparator::Comma if compressed => ",",
ListSeparator::Comma => ", ",
ListSeparator::Slash if compressed => "/",
ListSeparator::Slash => " / ",
ListSeparator::Space => " ",
}
}
}

impl Default for ListSeparator {
fn default() -> Self {
ListSeparator::Space
}
}

#[test]
fn check_sep_order() {
assert!(ListSeparator::Comma > ListSeparator::Space);
assert!(ListSeparator::Slash > ListSeparator::Space);
}
15 changes: 7 additions & 8 deletions tests/spec/core_functions/color/hsl/error/one_arg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ mod list {
use super::runner;

#[test]
#[ignore] // missing error
fn bracketed() {
assert_eq!(
runner().err(
Expand Down Expand Up @@ -137,7 +136,7 @@ mod slash_list {
use super::runner;

#[test]
#[ignore] // wrong error
#[ignore] // missing error
fn bracketed() {
assert_eq!(
runner().err(
Expand All @@ -153,7 +152,7 @@ mod slash_list {
);
}
#[test]
#[ignore] // wrong error
#[ignore] // missing error
fn comma_separated() {
assert_eq!(
runner().err(
Expand All @@ -169,7 +168,7 @@ mod slash_list {
);
}
#[test]
#[ignore] // wrong error
#[ignore] // missing error
fn empty() {
assert_eq!(
runner().err(
Expand All @@ -185,7 +184,7 @@ mod slash_list {
);
}
#[test]
#[ignore] // wrong error
#[ignore] // missing error
fn four_elements() {
assert_eq!(
runner().err(
Expand All @@ -201,7 +200,7 @@ mod slash_list {
);
}
#[test]
#[ignore] // wrong error
#[ignore] // missing error
fn one_element() {
assert_eq!(
runner().err(
Expand All @@ -217,7 +216,7 @@ mod slash_list {
);
}
#[test]
#[ignore] // wrong error
#[ignore] // missing error
fn two_elements() {
assert_eq!(
runner().err(
Expand All @@ -234,7 +233,7 @@ mod slash_list {
}
}
#[test]
#[ignore] // wrong error
#[ignore] // missing error
fn too_few_elements() {
assert_eq!(
runner().err(
Expand Down
1 change: 0 additions & 1 deletion tests/spec/core_functions/color/hsl/one_arg/alpha.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ mod in_gamut {
}
}
#[test]
#[ignore] // unexepected error
fn slash_list() {
assert_eq!(
runner().ok("@use \"sass:list\";\
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,6 @@ mod slash_list {
use super::runner;

#[test]
#[ignore] // unexepected error
fn alpha() {
assert_eq!(
runner().ok("@use \"sass:list\";\
Expand All @@ -569,7 +568,6 @@ mod slash_list {
);
}
#[test]
#[ignore] // unexepected error
fn channels() {
assert_eq!(
runner().ok("@use \"sass:list\";\
Expand All @@ -580,7 +578,6 @@ mod slash_list {
);
}
#[test]
#[ignore] // unexepected error
fn some_channels() {
assert_eq!(
runner().ok("@use \"sass:list\";\
Expand Down
15 changes: 7 additions & 8 deletions tests/spec/core_functions/color/hsla/error/one_arg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ mod list {
use super::runner;

#[test]
#[ignore] // missing error
fn bracketed() {
assert_eq!(
runner().err(
Expand Down Expand Up @@ -137,7 +136,7 @@ mod slash_list {
use super::runner;

#[test]
#[ignore] // wrong error
#[ignore] // missing error
fn bracketed() {
assert_eq!(
runner().err(
Expand All @@ -153,7 +152,7 @@ mod slash_list {
);
}
#[test]
#[ignore] // wrong error
#[ignore] // missing error
fn comma_separated() {
assert_eq!(
runner().err(
Expand All @@ -169,7 +168,7 @@ mod slash_list {
);
}
#[test]
#[ignore] // wrong error
#[ignore] // missing error
fn empty() {
assert_eq!(
runner().err(
Expand All @@ -185,7 +184,7 @@ mod slash_list {
);
}
#[test]
#[ignore] // wrong error
#[ignore] // missing error
fn four_elements() {
assert_eq!(
runner().err(
Expand All @@ -201,7 +200,7 @@ mod slash_list {
);
}
#[test]
#[ignore] // wrong error
#[ignore] // missing error
fn one_element() {
assert_eq!(
runner().err(
Expand All @@ -217,7 +216,7 @@ mod slash_list {
);
}
#[test]
#[ignore] // wrong error
#[ignore] // missing error
fn two_elements() {
assert_eq!(
runner().err(
Expand All @@ -234,7 +233,7 @@ mod slash_list {
}
}
#[test]
#[ignore] // wrong error
#[ignore] // missing error
fn too_few_elements() {
assert_eq!(
runner().err(
Expand Down
1 change: 0 additions & 1 deletion tests/spec/core_functions/color/hsla/one_arg/alpha.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ mod in_gamut {
}
}
#[test]
#[ignore] // unexepected error
fn slash_list() {
assert_eq!(
runner().ok("@use \"sass:list\";\
Expand Down
Loading

0 comments on commit 0291193

Please sign in to comment.