Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update implementation for lab() tests #2093

Merged
merged 2 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,10 @@ jobs:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
dart_channel: [stable]
include: [{os: ubuntu-latest, dart_channel: dev}]
# TODO(nweiz): Re-enable this when
# https://github.com/dart-lang/sdk/issues/52121#issuecomment-1728534228
# is addressed.
# include: [{os: ubuntu-latest, dart_channel: dev}]

steps:
- uses: actions/checkout@v3
Expand Down
106 changes: 77 additions & 29 deletions lib/src/functions/color.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import '../deprecation.dart';
import '../evaluation_context.dart';
import '../exception.dart';
import '../module/built_in.dart';
import '../parse/scss.dart';
import '../util/map.dart';
import '../util/nullable.dart';
import '../util/number.dart';
Expand Down Expand Up @@ -431,6 +432,17 @@ final module = BuiltInModule("color", functions: <Callable>[
_function("is-legacy", r"$color",
(arguments) => SassBoolean(arguments[0].assertColor("color").isLegacy)),

_function(
"is-missing",
r"$color, $channel",
(arguments) => SassBoolean(arguments[0]
.assertColor("color")
.isChannelMissing(
(arguments[1].assertString("channel")..assertQuoted("channel"))
.text,
colorName: "color",
channelName: "channel"))),

_function(
"is-in-gamut",
r"$color, $space: null",
Expand Down Expand Up @@ -1142,29 +1154,9 @@ Value _parseChannels(String functionName, Value input,
{ColorSpace? space, String? name}) {
if (input.isVar) return _functionString(functionName, [input]);

Value components;
Value? alphaValue;
switch (input.assertCommonListStyle(name, allowSlash: true)) {
case [var components_, var alphaValue_]
when input.separator == ListSeparator.slash:
components = components_;
alphaValue = alphaValue_;

case var inputList when input.separator == ListSeparator.slash:
throw SassScriptException(
"Only 2 slash-separated elements allowed, but ${inputList.length} "
"${pluralize('was', inputList.length, plural: 'were')} passed.");

case [..., SassString(hasQuotes: false, :var text)] when text.contains('/'):
return _functionString(functionName, [input]);

case [...var initial, SassNumber(asSlash: (var before, var after))]:
components = SassList([...initial, before], ListSeparator.space);
alphaValue = after;

case _:
components = input;
}
var parsedSlash = _parseSlashChannels(input, name: name);
if (parsedSlash == null) return _functionString(functionName, [input]);
var (components, alphaValue) = parsedSlash;

List<Value> channels;
SassString? spaceName;
Expand Down Expand Up @@ -1221,11 +1213,13 @@ Value _parseChannels(String functionName, Value input,
: _functionString(functionName, [input]);
}

var alpha = alphaValue == null
? 1.0
: _percentageOrUnitless(alphaValue.assertNumber(name), 1, 'alpha')
.clamp(0, 1)
.toDouble();
var alpha = switch (alphaValue) {
null => 1.0,
SassString(hasQuotes: false, text: 'none') => null,
_ => _percentageOrUnitless(alphaValue.assertNumber(name), 1, 'alpha')
.clamp(0, 1)
.toDouble()
};

// `space` will be null if either `components` or `spaceName` is a `var()`.
// Again, we check this here rather than returning early in those cases so
Expand Down Expand Up @@ -1255,10 +1249,64 @@ Value _parseChannels(String functionName, Value input,
fromRgbFunction: space == ColorSpace.rgb);
}

/// Parses [input]'s slash-separated third number and alpha value, if one
/// exists.
///
/// Returns a single value that contains the space-separated list of components,
/// and an alpha value if one was specified. If this channel set couldn't be
/// parsed and should be returned as-is, returns null.
///
/// Throws a [SassScriptException] if [input] is invalid. If [input] came from a
/// function argument, [name] is the argument name (without the `$`). It's used
/// for error reporting.
(Value components, Value? alpha)? _parseSlashChannels(Value input,
{String? name}) =>
switch (input.assertCommonListStyle(name, allowSlash: true)) {
[var components, var alphaValue]
when input.separator == ListSeparator.slash =>
(components, alphaValue),
var inputList when input.separator == ListSeparator.slash =>
throw SassScriptException(
"Only 2 slash-separated elements allowed, but ${inputList.length} "
"${pluralize('was', inputList.length, plural: 'were')} passed.",
name),
[...var initial, SassString(hasQuotes: false, :var text)] => switch (
text.split('/')) {
[_] => (input, null),
[var channel3 && 'none', var alpha] ||
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

off topic - seeing these dart3 features I'm excited about trying it out again during the next advent of code :)

[var channel3, var alpha && 'none'] =>
switch ((_parseNumberOrNone(channel3), _parseNumberOrNone(alpha))) {
(var channel3Value?, var alphaValue?) => (
SassList([...initial, channel3Value], ListSeparator.space),
alphaValue
),
_ => null
},
_ => null
},
[...var initial, SassNumber(asSlash: (var before, var after))] => (
SassList([...initial, before], ListSeparator.space),
after
),
_ => (input, null)
};

/// Parses [text] as either a Sass number or the unquoted Sass string "none".
///
/// If neither matches, returns null.
Value? _parseNumberOrNone(String text) {
if (text == 'none') return SassString('none', quotes: false);
try {
return ScssParser(text).parseNumber();
} on SassFormatException {
return null;
}
}

/// Creates a [SassColor] for the given [space] from the given channel values,
/// or throws a [SassScriptException] if the channel values are invalid.
SassColor _colorFromChannels(ColorSpace space, SassNumber? channel0,
SassNumber? channel1, SassNumber? channel2, double alpha,
SassNumber? channel1, SassNumber? channel2, double? alpha,
{bool fromRgbFunction = false}) {
switch (space) {
case ColorSpace.hsl:
Expand Down
5 changes: 5 additions & 0 deletions lib/src/parse/stylesheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ abstract class StylesheetParser extends Parser {

Expression parseExpression() => _parseSingleProduction(_expression);

SassNumber parseNumber() {
var expression = _parseSingleProduction(_number);
return SassNumber(expression.value, expression.unit);
}

VariableDeclaration parseVariableDeclaration() =>
_parseSingleProduction(() => lookingAtIdentifier()
? _variableDeclarationWithNamespace()
Expand Down
26 changes: 17 additions & 9 deletions lib/src/visitor/serialize.dart
Original file line number Diff line number Diff line change
Expand Up @@ -570,30 +570,34 @@ final class _SerializeVisitor
..write(value.space)
..writeCharCode($lparen);
_writeChannel(value.channel0OrNull);
if (!_isCompressed && value.space == ColorSpace.lab) {
if (!_isCompressed &&
value.space == ColorSpace.lab &&
!value.isChannel0Missing) {
_buffer.writeCharCode($percent);
}
_buffer.writeCharCode($space);
_writeChannel(value.channel1OrNull);
_buffer.writeCharCode($space);
_writeChannel(value.channel2OrNull);
_maybeWriteSlashAlpha(value.alpha);
_maybeWriteSlashAlpha(value);
_buffer.writeCharCode($rparen);

case ColorSpace.lch || ColorSpace.oklch:
_buffer
..write(value.space)
..writeCharCode($lparen);
_writeChannel(value.channel0OrNull);
if (!_isCompressed && value.space == ColorSpace.lch) {
if (!_isCompressed &&
value.space == ColorSpace.lch &&
!value.isChannel0Missing) {
_buffer.writeCharCode($percent);
}
_buffer.writeCharCode($space);
_writeChannel(value.channel1OrNull);
_buffer.writeCharCode($space);
_writeChannel(value.channel2OrNull);
if (!_isCompressed && !value.isChannel2Missing) _buffer.write('deg');
_maybeWriteSlashAlpha(value.alpha);
_maybeWriteSlashAlpha(value);
_buffer.writeCharCode($rparen);

case _:
Expand All @@ -602,7 +606,7 @@ final class _SerializeVisitor
..write(value.space)
..writeCharCode($space);
_writeBetween(value.channelsOrNull, ' ', _writeChannel);
_maybeWriteSlashAlpha(value.alpha);
_maybeWriteSlashAlpha(value);
_buffer.writeCharCode($rparen);
}
}
Expand Down Expand Up @@ -812,13 +816,17 @@ final class _SerializeVisitor
_buffer.writeCharCode(hexCharFor(color & 0xF));
}

/// Writes the alpha component of a color if [alpha] isn't 1.
void _maybeWriteSlashAlpha(double alpha) {
if (fuzzyEquals(alpha, 1)) return;
/// Writes the alpha component of [color] if it isn't 1.
void _maybeWriteSlashAlpha(SassColor color) {
if (fuzzyEquals(color.alpha, 1)) return;
_writeOptionalSpace();
_buffer.writeCharCode($slash);
_writeOptionalSpace();
_writeNumber(alpha);
if (color.isAlphaMissing) {
_buffer.write('none');
} else {
_writeNumber(color.alpha);
}
}

void visitFunction(SassFunction function) {
Expand Down