/
callable.dart
133 lines (127 loc) · 5.94 KB
/
callable.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Copyright 2016 Google Inc. Use of this source code is governed by an
// MIT-style license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
import 'package:meta/meta.dart';
import 'package:tuple/tuple.dart';
import 'ast/sass.dart';
import 'callable/async.dart';
import 'callable/built_in.dart';
import 'exception.dart';
import 'util/function_signature.dart';
import 'value.dart';
export 'callable/async.dart';
export 'callable/async_built_in.dart' show AsyncBuiltInCallable;
export 'callable/built_in.dart' show BuiltInCallable;
export 'callable/plain_css.dart';
export 'callable/user_defined.dart';
/// An interface for functions and mixins that can be invoked from Sass by
/// passing in arguments.
///
/// This extends [AsyncCallable] because all synchronous callables are also
/// usable in asynchronous contexts. [Callable]s are usable with both the
/// synchronous and asynchronous `compile()` functions, and as such should be
/// used in preference to [AsyncCallable]s if possible.
///
/// When writing custom functions, it's important to make them as user-friendly
/// and as close to the standards set by Sass's core functions as possible. Some
/// good guidelines to follow include:
///
/// * Use `Value.assert*` methods, like [Value.assertString], to cast untyped
/// `Value` objects to more specific types. For values from the argument list,
/// pass in the argument name as well. This ensures that the user gets good
/// error messages when they pass in the wrong type to your function.
///
/// * Individual classes may have more specific `assert*` methods, like
/// [SassNumber.assertInt], which should be used when possible.
///
/// * In Sass, every value counts as a list. Functions should avoid casting
/// values to the `SassList` type, and should use the [Value.asList] method
/// instead.
///
/// * When manipulating values like lists, strings, and numbers that have
/// metadata (comma versus space separated, bracketed versus unbracketed,
/// quoted versus unquoted, units), the output metadata should match the input
/// metadata. For lists, the [Value.withListContents] method can be used to do
/// this automatically.
///
/// * When in doubt, lists should default to comma-separated, strings should
/// default to quoted, and number should default to unitless.
///
/// * In Sass, lists and strings use one-based indexing and use negative indices
/// to index from the end of value. Functions should follow these conventions.
/// The [Value.sassIndexToListIndex] and [SassString.sassIndexToStringIndex]
/// methods can be used to do this automatically.
///
/// * String indexes in Sass refer to Unicode code points while Dart string
/// indices refer to UTF-16 code units. For example, the character U+1F60A,
/// Smiling Face With Smiling Eyes, is a single Unicode code point but is
/// represented in UTF-16 as two code units (`0xD83D` and `0xDE0A`). So in
/// Dart, `"a😊b".codeUnitAt(1)` returns `0xD83D`, whereas in Sass
/// `str-slice("a😊b", 1, 1)` returns `"😊"`. Functions should follow this
/// convention. The [SassString.sassIndexToStringIndex] and
/// [SassString.sassIndexToRuneIndex] methods can be used to do this
/// automatically, and the [SassString.sassLength] getter can be used to
/// access a string's length in code points.
///
/// {@category Compile}
@sealed
abstract class Callable extends AsyncCallable {
@Deprecated('Use `Callable.function` instead.')
factory Callable(String name, String arguments,
Value callback(List<Value> arguments)) =>
Callable.function(name, arguments, callback);
/// Creates a function with the given [name] and [arguments] that runs
/// [callback] when called.
///
/// The argument declaration is parsed from [arguments], which uses the same
/// syntax as an argument list written in Sass (not including parentheses).
/// The [arguments] list may be empty to indicate that the function takes no
/// arguments. Arguments may also have default values. Throws a
/// [SassFormatException] if parsing fails.
///
/// Any exceptions thrown by [callback] are automatically converted to Sass
/// errors and associated with the function call.
///
/// For example:
///
/// ```dart
/// new Callable.function("str-split", r'$string, $divider: " "',
/// (arguments) {
/// var string = arguments[0].assertString("string");
/// var divider = arguments[1].assertString("divider");
/// return new SassList(
/// string.value.split(divider.value).map((substring) =>
/// new SassString(substring, quotes: string.hasQuotes)),
/// ListSeparator.comma);
/// });
/// ```
///
/// Functions may also take variable length argument lists. These are declared
/// the same way as in Sass, and are passed as the final argument to the
/// callback. For example:
///
/// ```dart
/// new Callable.function("str-join", r'$strings...', (arguments) {
/// var args = arguments.first as SassArgumentList;
/// var strings = args.map((arg) => arg.assertString()).toList();
/// return new SassString(strings.map((string) => string.text).join(),
/// quotes: strings.any((string) => string.hasQuotes));
/// });
/// ```
///
/// Note that the argument list is always an instance of [SassArgumentList],
/// which provides access to keyword arguments using
/// [SassArgumentList.keywords].
factory Callable.function(String name, String arguments,
Value callback(List<Value> arguments)) =>
BuiltInCallable.function(name, arguments, callback);
/// Creates a host callable with a single [signature] and a single [callback].
///
/// Throws a [SassFormatException] if parsing fails.
factory Callable.host(String signature, Value callback(List<Value> arguments),
{bool requireParens = true}) {
Tuple2<String, ArgumentDeclaration> tuple =
parseSignature(signature, requireParens: requireParens);
return BuiltInCallable.parsed(tuple.item1, tuple.item2, callback);
}
}