mirror of
https://github.com/danog/dart-sass.git
synced 2024-11-27 04:34:59 +01:00
Add rgba().
This also adds built-in function overloading. This adds some extra layers to the default function-call logic which could be avoided by handling overloading purely in the callbacks of functions that require it, but it's not clear how to share logic gracefully between the perform visitor and function helpers.
This commit is contained in:
parent
5cc5bf1914
commit
227329f9c3
@ -2,13 +2,9 @@
|
||||
// MIT-style license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
import 'ast/sass.dart';
|
||||
|
||||
export 'callable/built_in.dart';
|
||||
export 'callable/user_defined.dart';
|
||||
|
||||
abstract class Callable {
|
||||
String get name;
|
||||
|
||||
ArgumentDeclaration get arguments;
|
||||
}
|
||||
|
@ -9,12 +9,16 @@ import '../value.dart';
|
||||
typedef Value _Callback(List<Value> arguments);
|
||||
|
||||
class BuiltInCallable implements Callable {
|
||||
final _Callback callback;
|
||||
|
||||
final String name;
|
||||
final ArgumentDeclaration arguments;
|
||||
final List<ArgumentDeclaration> overloads;
|
||||
final List<_Callback> callbacks;
|
||||
|
||||
BuiltInCallable(
|
||||
this.name, this.arguments, Value callback(List<Value> arguments))
|
||||
: callback = callback;
|
||||
BuiltInCallable(String name, ArgumentDeclaration arguments,
|
||||
Value callback(List<Value> arguments))
|
||||
: this.overloaded(name, [arguments], [callback]);
|
||||
|
||||
BuiltInCallable.overloaded(this.name, Iterable<ArgumentDeclaration> arguments,
|
||||
Iterable<_Callback> callbacks)
|
||||
: overloads = new List.unmodifiable(arguments),
|
||||
callbacks = new List.unmodifiable(callbacks);
|
||||
}
|
||||
|
@ -19,18 +19,48 @@ void defineCoreFunctions(Environment environment) {
|
||||
var blue = arguments[2].assertNumber("blue");
|
||||
|
||||
return new SassColor.rgb(
|
||||
_percentageOrUnitless(red, 255, "red"),
|
||||
_percentageOrUnitless(green, 255, "green"),
|
||||
_percentageOrUnitless(blue, 255, "blue"));
|
||||
_percentageOrUnitless(red, 255, "red").round(),
|
||||
_percentageOrUnitless(green, 255, "green").round(),
|
||||
_percentageOrUnitless(blue, 255, "blue").round());
|
||||
}));
|
||||
|
||||
environment.setFunction(new BuiltInCallable.overloaded("rgba", [
|
||||
new ArgumentDeclaration([
|
||||
new Argument("red"),
|
||||
new Argument("green"),
|
||||
new Argument("blue"),
|
||||
new Argument("alpha")
|
||||
]),
|
||||
new ArgumentDeclaration([new Argument("color"), new Argument("alpha")]),
|
||||
], [
|
||||
(arguments) {
|
||||
// TODO: support calc strings
|
||||
var red = arguments[0].assertNumber("red");
|
||||
var green = arguments[1].assertNumber("green");
|
||||
var blue = arguments[2].assertNumber("blue");
|
||||
var alpha = arguments[3].assertNumber("alpha");
|
||||
|
||||
return new SassColor.rgb(
|
||||
_percentageOrUnitless(red, 255, "red").round(),
|
||||
_percentageOrUnitless(green, 255, "green").round(),
|
||||
_percentageOrUnitless(blue, 255, "blue").round(),
|
||||
_percentageOrUnitless(alpha, 1, "alpha"));
|
||||
},
|
||||
(arguments) {
|
||||
var color = arguments[0].assertColor("color");
|
||||
var alpha = arguments[0].assertNumber("alpha");
|
||||
|
||||
return color.change(alpha: _percentageOrUnitless(alpha, 1, "alpha"));
|
||||
}
|
||||
]));
|
||||
|
||||
environment.setFunction(new BuiltInCallable(
|
||||
"inspect",
|
||||
new ArgumentDeclaration([new Argument("value")]),
|
||||
(arguments) => new SassString(arguments.single.toString())));
|
||||
}
|
||||
|
||||
int _percentageOrUnitless(SassNumber number, int max, String name) {
|
||||
num _percentageOrUnitless(SassNumber number, num max, String name) {
|
||||
num value;
|
||||
if (!number.hasUnits) {
|
||||
value = number.value;
|
||||
@ -41,5 +71,5 @@ int _percentageOrUnitless(SassNumber number, int max, String name) {
|
||||
'\$$name: Expected $number to have no units or "%".');
|
||||
}
|
||||
|
||||
return value.clamp(0, max).round();
|
||||
return value.clamp(0, max);
|
||||
}
|
||||
|
@ -12,13 +12,19 @@ class SassColor extends Value {
|
||||
final int red;
|
||||
final int green;
|
||||
final int blue;
|
||||
final double alpha;
|
||||
|
||||
SassColor.rgb(this.red, this.green, this.blue);
|
||||
SassColor.rgb(this.red, this.green, this.blue, [double alpha])
|
||||
: alpha = alpha ?? 1.0;
|
||||
|
||||
/*=T*/ accept/*<T>*/(ValueVisitor/*<T>*/ visitor) => visitor.visitColor(this);
|
||||
|
||||
SassColor assertColor([String name]) => this;
|
||||
|
||||
SassColor change({int red, int green, int blue, double alpha}) =>
|
||||
new SassColor.rgb(red ?? this.red, green ?? this.green, blue ?? this.blue,
|
||||
alpha ?? this.alpha);
|
||||
|
||||
Value plus(Value other) {
|
||||
if (other is! SassNumber && other is! SassColor) return super.plus(other);
|
||||
throw new InternalException('Undefined operation "$this + $other".');
|
||||
|
@ -766,9 +766,26 @@ class PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
var named = triple.item2;
|
||||
var separator = triple.item3;
|
||||
|
||||
_verifyArguments(positional, named, callable.arguments, invocation.span);
|
||||
int overloadIndex;
|
||||
for (var i = 0; i < callable.overloads.length - 1; i++) {
|
||||
try {
|
||||
_verifyArguments(
|
||||
positional, named, callable.overloads[i], invocation.span);
|
||||
overloadIndex = i;
|
||||
break;
|
||||
} on SassRuntimeException catch (_) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (overloadIndex == null) {
|
||||
_verifyArguments(
|
||||
positional, named, callable.overloads.last, invocation.span);
|
||||
overloadIndex = callable.overloads.length - 1;
|
||||
}
|
||||
|
||||
var declaredArguments = callable.arguments.arguments;
|
||||
var overload = callable.overloads[overloadIndex];
|
||||
var callback = callable.callbacks[overloadIndex];
|
||||
var declaredArguments = overload.arguments;
|
||||
for (var i = positional.length; i < declaredArguments.length; i++) {
|
||||
var argument = declaredArguments[i];
|
||||
positional.add(
|
||||
@ -776,7 +793,7 @@ class PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
}
|
||||
|
||||
SassArgumentList argumentList;
|
||||
if (callable.arguments.restArgument != null) {
|
||||
if (overload.restArgument != null) {
|
||||
var rest = positional.length > declaredArguments.length
|
||||
? positional.sublist(declaredArguments.length)
|
||||
: const <Value>[];
|
||||
@ -789,8 +806,7 @@ class PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
positional.add(argumentList);
|
||||
}
|
||||
|
||||
var result =
|
||||
_addExceptionSpan(() => callable.callback(positional), invocation.span);
|
||||
var result = _addExceptionSpan(() => callback(positional), invocation.span);
|
||||
|
||||
if (argumentList == null) return result;
|
||||
if (named.isEmpty) return result;
|
||||
|
@ -225,10 +225,17 @@ class _SerializeCssVisitor
|
||||
|
||||
void visitColor(SassColor value) {
|
||||
// TODO(nweiz): Use color names for named colors.
|
||||
_buffer.writeCharCode($hash);
|
||||
_writeHexComponent(value.red);
|
||||
_writeHexComponent(value.green);
|
||||
_writeHexComponent(value.blue);
|
||||
if (value.alpha == 1) {
|
||||
_buffer.writeCharCode($hash);
|
||||
_writeHexComponent(value.red);
|
||||
_writeHexComponent(value.green);
|
||||
_writeHexComponent(value.blue);
|
||||
} else {
|
||||
// TODO: support precision in alpha, make sure we don't write exponential
|
||||
// notation.
|
||||
_buffer.write(
|
||||
"rgb(${value.red}, ${value.green}, ${value.blue}, ${value.alpha})");
|
||||
}
|
||||
}
|
||||
|
||||
void _writeHexComponent(int color) {
|
||||
|
Loading…
Reference in New Issue
Block a user