mirror of
https://github.com/danog/dart-sass.git
synced 2025-01-21 21:31:11 +01:00
SassNumber.assertIndexFor() -> Value.sassIndexToListIndex() (#211)
This commit is contained in:
parent
1e09cec5aa
commit
9d207b13ec
@ -696,17 +696,17 @@ final List<BuiltInCallable> coreFunctions = new UnmodifiableListView([
|
||||
(arguments) => new SassNumber(arguments[0].asList.length)),
|
||||
|
||||
new BuiltInCallable("nth", r"$list, $n", (arguments) {
|
||||
var list = arguments[0].asList;
|
||||
var index = arguments[1].assertNumber("n");
|
||||
return list[index.assertIndexFor(list, "n")];
|
||||
var list = arguments[0];
|
||||
var index = arguments[1];
|
||||
return list.asList[list.sassIndexToListIndex(index, "n")];
|
||||
}),
|
||||
|
||||
new BuiltInCallable("set-nth", r"$list, $n, $value", (arguments) {
|
||||
var list = arguments[0].asList;
|
||||
var index = arguments[1].assertNumber("n");
|
||||
var list = arguments[0];
|
||||
var index = arguments[1];
|
||||
var value = arguments[2];
|
||||
var newList = list.toList();
|
||||
newList[index.assertIndexFor(list, "n")] = value;
|
||||
var newList = list.asList.toList();
|
||||
newList[list.sassIndexToListIndex(index, "n")] = value;
|
||||
return arguments[0].changeListContents(newList);
|
||||
}),
|
||||
|
||||
|
@ -2,6 +2,8 @@
|
||||
// MIT-style license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import 'ast/selector.dart';
|
||||
import 'exception.dart';
|
||||
import 'value/boolean.dart';
|
||||
@ -36,6 +38,13 @@ abstract class Value implements ext.Value {
|
||||
bool get hasBrackets => false;
|
||||
List<Value> get asList => [this];
|
||||
|
||||
/// The length of [asList].
|
||||
///
|
||||
/// This is used to compute [sassIndexToListIndex] without allocating a new
|
||||
/// list.
|
||||
@protected
|
||||
int get lengthAsList => 1;
|
||||
|
||||
/// Whether the value will be represented in CSS as the empty string.
|
||||
bool get isBlank => false;
|
||||
|
||||
@ -61,6 +70,18 @@ abstract class Value implements ext.Value {
|
||||
/// It's not guaranteed to be stable across versions.
|
||||
T accept<T>(ValueVisitor<T> visitor);
|
||||
|
||||
int sassIndexToListIndex(ext.Value sassIndex, [String name]) {
|
||||
var index = sassIndex.assertNumber(name).assertInt(name);
|
||||
if (index == 0) throw _exception("List index may not be 0.", name);
|
||||
if (index.abs() > lengthAsList) {
|
||||
throw _exception(
|
||||
"Invalid index $sassIndex for a list with ${lengthAsList} elements.",
|
||||
name);
|
||||
}
|
||||
|
||||
return index < 0 ? lengthAsList + index : index - 1;
|
||||
}
|
||||
|
||||
SassBoolean assertBoolean([String name]) =>
|
||||
throw _exception("$this is not a boolean.", name);
|
||||
|
||||
|
11
lib/src/value/external/number.dart
vendored
11
lib/src/value/external/number.dart
vendored
@ -69,17 +69,6 @@ abstract class SassNumber extends Value {
|
||||
/// It's used for error reporting.
|
||||
int assertInt([String name]);
|
||||
|
||||
/// Asserts that this is a valid Sass-style index for [list], and returns the
|
||||
/// Dart-style index.
|
||||
///
|
||||
/// A Sass-style index is one-based, and uses negative numbers to count
|
||||
/// backwards from the end of the list.
|
||||
///
|
||||
/// Throws a [SassScriptException] if this isn't an integer or if it isn't a
|
||||
/// valid index for [list]. If this came from a function argument, [name] is
|
||||
/// the argument name (without the `$`). It's used for error reporting.
|
||||
int assertIndexFor(List list, [String name]);
|
||||
|
||||
/// If [value] is between [min] and [max], returns it.
|
||||
///
|
||||
/// If [value] is [fuzzyEquals] to [min] or [max], it's clamped to the
|
||||
|
12
lib/src/value/external/value.dart
vendored
12
lib/src/value/external/value.dart
vendored
@ -58,6 +58,18 @@ abstract class Value {
|
||||
/// and all other values count as single-value lists.
|
||||
List<Value> get asList;
|
||||
|
||||
/// Converts [sassIndex] into a Dart-style index into the list returned by
|
||||
/// [asList].
|
||||
///
|
||||
/// Sass indexes are one-based, while Dart indexes are zero-based. Sass
|
||||
/// indexes may also be negative in order to index from the end of the list.
|
||||
///
|
||||
/// Throws a [SassScriptException] if [sassIndex] isn't a number, if that
|
||||
/// number isn't an integer, or if that integer isn't a valid index for
|
||||
/// [asList]. If [sassIndex] came from a function argument, [name] is the
|
||||
/// argument name (without the `$`). It's used for error reporting.
|
||||
int sassIndexToListIndex(Value sassIndex, [String name]);
|
||||
|
||||
/// Throws a [SassScriptException] if [this] isn't a boolean.
|
||||
///
|
||||
/// Note that generally, functions should use [isTruthy] rather than requiring
|
||||
|
@ -18,6 +18,8 @@ class SassList extends Value implements ext.SassList {
|
||||
|
||||
List<Value> get asList => contents;
|
||||
|
||||
int get lengthAsList => contents.length;
|
||||
|
||||
const SassList.empty({ListSeparator separator, bool brackets: false})
|
||||
: contents = const [],
|
||||
separator = separator ?? ListSeparator.undecided,
|
||||
|
@ -21,6 +21,8 @@ class SassMap extends Value implements ext.SassMap {
|
||||
return result;
|
||||
}
|
||||
|
||||
int get lengthAsList => contents.length;
|
||||
|
||||
/// Returns an empty map.
|
||||
const SassMap.empty() : contents = const {};
|
||||
|
||||
|
@ -200,17 +200,6 @@ class SassNumber extends Value implements ext.SassNumber {
|
||||
throw _exception("$this is not an int.", name);
|
||||
}
|
||||
|
||||
int assertIndexFor(List list, [String name]) {
|
||||
var sassIndex = assertInt(name);
|
||||
if (sassIndex == 0) throw _exception("List index may not be 0.");
|
||||
if (sassIndex.abs() > list.length) {
|
||||
throw _exception(
|
||||
"Invalid index $this for a list with ${list.length} elements.");
|
||||
}
|
||||
|
||||
return sassIndex < 0 ? list.length + sassIndex : sassIndex - 1;
|
||||
}
|
||||
|
||||
num valueInRange(num min, num max, [String name]) {
|
||||
var result = fuzzyCheckRange(value, min, max);
|
||||
if (result != null) return result;
|
||||
|
@ -62,6 +62,39 @@ main() {
|
||||
ListSeparator.comma))));
|
||||
});
|
||||
|
||||
group("sassIndexToListIndex()", () {
|
||||
test("converts a positive index to a Dart index", () {
|
||||
expect(value.sassIndexToListIndex(new SassNumber(1)), equals(0));
|
||||
expect(value.sassIndexToListIndex(new SassNumber(2)), equals(1));
|
||||
expect(value.sassIndexToListIndex(new SassNumber(3)), equals(2));
|
||||
});
|
||||
|
||||
test("converts a negative index to a Dart index", () {
|
||||
expect(value.sassIndexToListIndex(new SassNumber(-1)), equals(2));
|
||||
expect(value.sassIndexToListIndex(new SassNumber(-2)), equals(1));
|
||||
expect(value.sassIndexToListIndex(new SassNumber(-3)), equals(0));
|
||||
});
|
||||
|
||||
test("rejects a non-number", () {
|
||||
expect(() => value.sassIndexToListIndex(new SassString("foo")),
|
||||
throwsSassScriptException);
|
||||
});
|
||||
|
||||
test("rejects a non-integer", () {
|
||||
expect(() => value.sassIndexToListIndex(new SassNumber(1.1)),
|
||||
throwsSassScriptException);
|
||||
});
|
||||
|
||||
test("rejects invalid indices", () {
|
||||
expect(() => value.sassIndexToListIndex(new SassNumber(0)),
|
||||
throwsSassScriptException);
|
||||
expect(() => value.sassIndexToListIndex(new SassNumber(4)),
|
||||
throwsSassScriptException);
|
||||
expect(() => value.sassIndexToListIndex(new SassNumber(-4)),
|
||||
throwsSassScriptException);
|
||||
});
|
||||
});
|
||||
|
||||
test("isn't any other type", () {
|
||||
expect(value.assertBoolean, throwsSassScriptException);
|
||||
expect(value.assertColor, throwsSassScriptException);
|
||||
@ -133,6 +166,15 @@ main() {
|
||||
expect(value.assertNumber, throwsSassScriptException);
|
||||
expect(value.assertString, throwsSassScriptException);
|
||||
});
|
||||
|
||||
test("sassIndexToListIndex() rejects invalid indices", () {
|
||||
expect(() => value.sassIndexToListIndex(new SassNumber(0)),
|
||||
throwsSassScriptException);
|
||||
expect(() => value.sassIndexToListIndex(new SassNumber(1)),
|
||||
throwsSassScriptException);
|
||||
expect(() => value.sassIndexToListIndex(new SassNumber(-1)),
|
||||
throwsSassScriptException);
|
||||
});
|
||||
});
|
||||
|
||||
group("a scalar value", () {
|
||||
@ -152,6 +194,25 @@ main() {
|
||||
expect(list, hasLength(1));
|
||||
expect(list.first, same(value));
|
||||
});
|
||||
|
||||
group("sassIndexToListIndex()", () {
|
||||
test("converts a positive index to a Dart index", () {
|
||||
expect(value.sassIndexToListIndex(new SassNumber(1)), equals(0));
|
||||
});
|
||||
|
||||
test("converts a negative index to a Dart index", () {
|
||||
expect(value.sassIndexToListIndex(new SassNumber(-1)), equals(0));
|
||||
});
|
||||
|
||||
test("rejects invalid indices", () {
|
||||
expect(() => value.sassIndexToListIndex(new SassNumber(0)),
|
||||
throwsSassScriptException);
|
||||
expect(() => value.sassIndexToListIndex(new SassNumber(2)),
|
||||
throwsSassScriptException);
|
||||
expect(() => value.sassIndexToListIndex(new SassNumber(-2)),
|
||||
throwsSassScriptException);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
group("new SassList.empty()", () {
|
||||
|
@ -39,6 +39,27 @@ main() {
|
||||
]));
|
||||
});
|
||||
|
||||
group("sassIndexToListIndex()", () {
|
||||
test("converts a positive index to a Dart index", () {
|
||||
expect(value.sassIndexToListIndex(new SassNumber(1)), equals(0));
|
||||
expect(value.sassIndexToListIndex(new SassNumber(2)), equals(1));
|
||||
});
|
||||
|
||||
test("converts a negative index to a Dart index", () {
|
||||
expect(value.sassIndexToListIndex(new SassNumber(-1)), equals(1));
|
||||
expect(value.sassIndexToListIndex(new SassNumber(-2)), equals(0));
|
||||
});
|
||||
|
||||
test("rejects invalid indices", () {
|
||||
expect(() => value.sassIndexToListIndex(new SassNumber(0)),
|
||||
throwsSassScriptException);
|
||||
expect(() => value.sassIndexToListIndex(new SassNumber(3)),
|
||||
throwsSassScriptException);
|
||||
expect(() => value.sassIndexToListIndex(new SassNumber(-3)),
|
||||
throwsSassScriptException);
|
||||
});
|
||||
});
|
||||
|
||||
test("equals the same map", () {
|
||||
expect(
|
||||
value,
|
||||
@ -128,6 +149,15 @@ main() {
|
||||
test("equals an empty list", () {
|
||||
expect(value, equalsWithHash(new SassList.empty()));
|
||||
});
|
||||
|
||||
test("sassIndexToListIndex() rejects invalid indices", () {
|
||||
expect(() => value.sassIndexToListIndex(new SassNumber(0)),
|
||||
throwsSassScriptException);
|
||||
expect(() => value.sassIndexToListIndex(new SassNumber(1)),
|
||||
throwsSassScriptException);
|
||||
expect(() => value.sassIndexToListIndex(new SassNumber(-1)),
|
||||
throwsSassScriptException);
|
||||
});
|
||||
});
|
||||
|
||||
test("new SassMap.empty() creates an empty map with default metadata", () {
|
||||
|
Loading…
x
Reference in New Issue
Block a user