Skip to content
This repository was archived by the owner on May 15, 2023. It is now read-only.

Commit e98484a

Browse files
committed
Add support for argument lists
1 parent 5ff4e84 commit e98484a

File tree

5 files changed

+471
-23
lines changed

5 files changed

+471
-23
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
## 1.0.0-beta.10
22

3+
* Support version 1.0.0-beta.12 of the Sass embedded protocol:
4+
* Support `Value.ArgumentList`.
5+
36
* Support slash-separated lists.
47

58
## 1.0.0-beta.9

lib/src/host_callable.dart

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import 'package:sass/sass.dart' as sass;
1010
import 'dispatcher.dart';
1111
import 'embedded_sass.pb.dart';
1212
import 'function_registry.dart';
13+
import 'protofier.dart';
1314
import 'utils.dart';
14-
import 'value.dart';
1515

1616
/// Returns a Sass callable that invokes a function defined on the host with the
1717
/// given [signature].
@@ -41,11 +41,11 @@ sass.Callable hostCallable(Dispatcher dispatcher, FunctionRegistry functions,
4141
return sass.Callable.function(
4242
name, signature.substring(openParen + 1, signature.length - 1),
4343
(arguments) {
44+
var protofier = Protofier(dispatcher, functions, compilationId);
4445
var request = OutboundMessage_FunctionCallRequest()
4546
..compilationId = compilationId
46-
..arguments.addAll([
47-
for (var argument in arguments) protofyValue(functions, argument)
48-
]);
47+
..arguments.addAll(
48+
[for (var argument in arguments) protofier.protofy(argument)]);
4949

5050
if (id != null) {
5151
request.functionId = id;
@@ -57,8 +57,7 @@ sass.Callable hostCallable(Dispatcher dispatcher, FunctionRegistry functions,
5757
try {
5858
switch (response.whichResult()) {
5959
case InboundMessage_FunctionCallResponse_Result.success:
60-
return deprotofyValue(
61-
dispatcher, functions, compilationId, response.success);
60+
return protofier.deprotofyResponse(response);
6261

6362
case InboundMessage_FunctionCallResponse_Result.error:
6463
throw response.error;

lib/src/protofier.dart

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
// Copyright 2019 Google Inc. Use of this source code is governed by an
2+
// MIT-style license that can be found in the LICENSE file or at
3+
// https://opensource.org/licenses/MIT.
4+
5+
import 'package:sass/sass.dart' as sass;
6+
7+
import 'dispatcher.dart';
8+
import 'embedded_sass.pb.dart';
9+
import 'function_registry.dart';
10+
import 'host_callable.dart';
11+
import 'utils.dart';
12+
13+
/// A class that converts Sass [sass.Value] objects into [Value] protobufs.
14+
///
15+
/// A given [Protofier] instance is valid only within the scope of a single
16+
/// custom function call.
17+
class Protofier {
18+
/// The dispatcher, for invoking deprotofied [Value_HostFunction]s.
19+
final Dispatcher _dispatcher;
20+
21+
/// The IDs of first-class functions.
22+
final FunctionRegistry _functions;
23+
24+
/// The ID of the current compilation.
25+
final int _compilationId;
26+
27+
/// Any argument lists transitively contained in [value].
28+
///
29+
/// The IDs of the [Value_ArgumentList] protobufs are always one greater than
30+
/// the index of the corresponding list in this array (since 0 is reserved for
31+
/// argument lists created by the host).
32+
final _argumentLists = <sass.SassArgumentList>[];
33+
34+
/// Creates a [Protofier] that's valid within the scope of a single custom
35+
/// function call.
36+
///
37+
/// The [functions] tracks the IDs of first-class functions so that the host
38+
/// can pass them back to the compiler.
39+
Protofier(this._dispatcher, this._functions, this._compilationId);
40+
41+
/// Converts [value] to its protocol buffer representation.
42+
Value protofy(sass.Value value) {
43+
var result = Value();
44+
if (value is sass.SassString) {
45+
result.string = Value_String()
46+
..text = value.text
47+
..quoted = value.hasQuotes;
48+
} else if (value is sass.SassNumber) {
49+
var number = Value_Number()..value = value.value * 1.0;
50+
number.numerators.addAll(value.numeratorUnits);
51+
number.denominators.addAll(value.denominatorUnits);
52+
result.number = number;
53+
} else if (value is sass.SassColor) {
54+
// TODO(nweiz): If the color is represented as HSL internally, this coerces
55+
// it to RGB. Is it worth providing some visibility into its internal
56+
// representation so we can serialize without converting?
57+
result.rgbColor = Value_RgbColor()
58+
..red = value.red
59+
..green = value.green
60+
..blue = value.blue
61+
..alpha = value.alpha * 1.0;
62+
} else if (value is sass.SassArgumentList) {
63+
_argumentLists.add(value);
64+
var argList = Value_ArgumentList()
65+
..id = _argumentLists.length
66+
..separator = _protofySeparator(value.separator)
67+
..contents.addAll([for (var element in value.asList) protofy(element)]);
68+
value.keywordsWithoutMarking.forEach((key, value) {
69+
argList.keywords[key] = protofy(value);
70+
});
71+
72+
result.argumentList = argList;
73+
} else if (value is sass.SassList) {
74+
result.list = Value_List()
75+
..separator = _protofySeparator(value.separator)
76+
..hasBrackets = value.hasBrackets
77+
..contents.addAll([for (var element in value.asList) protofy(element)]);
78+
} else if (value is sass.SassMap) {
79+
var map = Value_Map();
80+
value.contents.forEach((key, value) {
81+
map.entries.add(Value_Map_Entry()
82+
..key = protofy(key)
83+
..value = protofy(value));
84+
});
85+
result.map = map;
86+
} else if (value is sass.SassFunction) {
87+
result.compilerFunction = _functions.protofy(value);
88+
} else if (value == sass.sassTrue) {
89+
result.singleton = SingletonValue.TRUE;
90+
} else if (value == sass.sassFalse) {
91+
result.singleton = SingletonValue.FALSE;
92+
} else if (value == sass.sassNull) {
93+
result.singleton = SingletonValue.NULL;
94+
} else {
95+
throw "Unknown Value $value";
96+
}
97+
return result;
98+
}
99+
100+
/// Converts [separator] to its protocol buffer representation.
101+
ListSeparator _protofySeparator(sass.ListSeparator separator) {
102+
switch (separator) {
103+
case sass.ListSeparator.comma:
104+
return ListSeparator.COMMA;
105+
case sass.ListSeparator.space:
106+
return ListSeparator.SPACE;
107+
case sass.ListSeparator.slash:
108+
return ListSeparator.SLASH;
109+
case sass.ListSeparator.undecided:
110+
return ListSeparator.UNDECIDED;
111+
default:
112+
throw "Unknown ListSeparator $separator";
113+
}
114+
}
115+
116+
/// Converts [response]'s return value to its Sass representation.
117+
sass.Value deprotofyResponse(InboundMessage_FunctionCallResponse response) {
118+
for (var id in response.accessedArgumentLists) {
119+
// Mark the `keywords` field as accessed.
120+
_argumentListForId(id).keywords;
121+
}
122+
123+
return _deprotofy(response.success);
124+
}
125+
126+
/// Converts [value] to its Sass representation.
127+
sass.Value _deprotofy(Value value) {
128+
try {
129+
switch (value.whichValue()) {
130+
case Value_Value.string:
131+
return value.string.text.isEmpty
132+
? sass.SassString.empty(quotes: value.string.quoted)
133+
: sass.SassString(value.string.text, quotes: value.string.quoted);
134+
135+
case Value_Value.number:
136+
return sass.SassNumber.withUnits(value.number.value,
137+
numeratorUnits: value.number.numerators,
138+
denominatorUnits: value.number.denominators);
139+
140+
case Value_Value.rgbColor:
141+
return sass.SassColor.rgb(value.rgbColor.red, value.rgbColor.green,
142+
value.rgbColor.blue, value.rgbColor.alpha);
143+
144+
case Value_Value.hslColor:
145+
return sass.SassColor.hsl(
146+
value.hslColor.hue,
147+
value.hslColor.saturation,
148+
value.hslColor.lightness,
149+
value.hslColor.alpha);
150+
151+
case Value_Value.argumentList:
152+
if (value.argumentList.id != 0) {
153+
return _argumentListForId(value.argumentList.id);
154+
}
155+
156+
var separator = _deprotofySeparator(value.argumentList.separator);
157+
var length = value.argumentList.contents.length;
158+
if (separator == sass.ListSeparator.undecided && length > 1) {
159+
throw paramsError(
160+
"List $value can't have an undecided separator because it has "
161+
"$length elements");
162+
}
163+
164+
return sass.SassArgumentList([
165+
for (var element in value.argumentList.contents) _deprotofy(element)
166+
], {
167+
for (var entry in value.argumentList.keywords.entries)
168+
entry.key: _deprotofy(entry.value)
169+
}, separator);
170+
171+
case Value_Value.list:
172+
var separator = _deprotofySeparator(value.list.separator);
173+
if (value.list.contents.isEmpty) {
174+
return sass.SassList.empty(
175+
separator: separator, brackets: value.list.hasBrackets);
176+
}
177+
178+
var length = value.list.contents.length;
179+
if (separator == sass.ListSeparator.undecided && length > 1) {
180+
throw paramsError(
181+
"List $value can't have an undecided separator because it has "
182+
"$length elements");
183+
}
184+
185+
return sass.SassList([
186+
for (var element in value.list.contents) _deprotofy(element)
187+
], separator, brackets: value.list.hasBrackets);
188+
189+
case Value_Value.map:
190+
return value.map.entries.isEmpty
191+
? const sass.SassMap.empty()
192+
: sass.SassMap({
193+
for (var entry in value.map.entries)
194+
_deprotofy(entry.key): _deprotofy(entry.value)
195+
});
196+
197+
case Value_Value.compilerFunction:
198+
var id = value.compilerFunction.id;
199+
var function = _functions[id];
200+
if (function == null) {
201+
throw paramsError(
202+
"CompilerFunction.id $id doesn't match any known functions");
203+
}
204+
205+
return function;
206+
207+
case Value_Value.hostFunction:
208+
return sass.SassFunction(hostCallable(_dispatcher, _functions,
209+
_compilationId, value.hostFunction.signature,
210+
id: value.hostFunction.id));
211+
212+
case Value_Value.singleton:
213+
switch (value.singleton) {
214+
case SingletonValue.TRUE:
215+
return sass.sassTrue;
216+
case SingletonValue.FALSE:
217+
return sass.sassFalse;
218+
case SingletonValue.NULL:
219+
return sass.sassNull;
220+
default:
221+
throw "Unknown Value.singleton ${value.singleton}";
222+
}
223+
224+
case Value_Value.notSet:
225+
throw mandatoryError("Value.value");
226+
}
227+
} on RangeError catch (error) {
228+
var name = error.name;
229+
if (name == null || error.start == null || error.end == null) {
230+
throw paramsError(error.toString());
231+
}
232+
233+
if (value.whichValue() == Value_Value.rgbColor) {
234+
name = 'RgbColor.$name';
235+
} else if (value.whichValue() == Value_Value.hslColor) {
236+
name = 'HslColor.$name';
237+
}
238+
239+
throw paramsError(
240+
'$name must be between ${error.start} and ${error.end}, was '
241+
'${error.invalidValue}');
242+
}
243+
}
244+
245+
/// Returns the argument list in [_argumentLists] that corresponds to [id].
246+
sass.SassArgumentList _argumentListForId(int id) {
247+
if (id < 1) {
248+
throw paramsError(
249+
"Value.ArgumentList.id $id can't be marked as accessed");
250+
} else if (id > _argumentLists.length) {
251+
throw paramsError(
252+
"Value.ArgumentList.id $id doesn't match any known argument "
253+
"lists");
254+
} else {
255+
return _argumentLists[id - 1];
256+
}
257+
}
258+
259+
/// Converts [separator] to its Sass representation.
260+
sass.ListSeparator _deprotofySeparator(ListSeparator separator) {
261+
switch (separator) {
262+
case ListSeparator.COMMA:
263+
return sass.ListSeparator.comma;
264+
case ListSeparator.SPACE:
265+
return sass.ListSeparator.space;
266+
case ListSeparator.SLASH:
267+
return sass.ListSeparator.slash;
268+
case ListSeparator.UNDECIDED:
269+
return sass.ListSeparator.undecided;
270+
default:
271+
throw "Unknown separator $separator";
272+
}
273+
}
274+
}

pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: sass_embedded
2-
version: 1.0.0-dev
2+
version: 1.0.0-beta.10
33
description: An implementation of the Sass embedded protocol using Dart Sass.
44
author: Sass Team
55
homepage: https://github.com/sass/dart-sass-embedded
@@ -14,7 +14,7 @@ dependencies:
1414
async: ">=1.13.0 <3.0.0"
1515
meta: ^1.1.0
1616
protobuf: ^2.0.0
17-
sass: ^1.36.0
17+
sass: ^1.38.0
1818
source_span: ^1.1.0
1919
stack_trace: ^1.6.0
2020
stream_channel: ">=1.6.0 <3.0.0"

0 commit comments

Comments
 (0)