sassc 2.2.1 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +2 -0
- data/CHANGELOG.md +18 -0
- data/Rakefile +1 -3
- data/ext/extconf.rb +13 -5
- data/ext/libsass/VERSION +1 -1
- data/ext/libsass/include/sass/base.h +2 -1
- data/ext/libsass/include/sass/context.h +4 -0
- data/ext/libsass/src/MurmurHash2.hpp +91 -0
- data/ext/libsass/src/ast.cpp +158 -168
- data/ext/libsass/src/ast.hpp +389 -230
- data/ext/libsass/src/ast_def_macros.hpp +18 -10
- data/ext/libsass/src/ast_fwd_decl.cpp +4 -3
- data/ext/libsass/src/ast_fwd_decl.hpp +98 -165
- data/ext/libsass/src/ast_helpers.hpp +292 -0
- data/ext/libsass/src/ast_sel_cmp.cpp +219 -732
- data/ext/libsass/src/ast_sel_super.cpp +539 -0
- data/ext/libsass/src/ast_sel_unify.cpp +207 -212
- data/ext/libsass/src/ast_sel_weave.cpp +616 -0
- data/ext/libsass/src/ast_selectors.cpp +594 -1026
- data/ext/libsass/src/ast_selectors.hpp +339 -385
- data/ext/libsass/src/ast_supports.cpp +36 -52
- data/ext/libsass/src/ast_supports.hpp +29 -29
- data/ext/libsass/src/ast_values.cpp +271 -84
- data/ext/libsass/src/ast_values.hpp +116 -107
- data/ext/libsass/src/backtrace.cpp +9 -9
- data/ext/libsass/src/backtrace.hpp +5 -5
- data/ext/libsass/src/base64vlq.cpp +2 -2
- data/ext/libsass/src/base64vlq.hpp +1 -1
- data/ext/libsass/src/bind.cpp +18 -18
- data/ext/libsass/src/bind.hpp +1 -1
- data/ext/libsass/src/c2ast.cpp +3 -3
- data/ext/libsass/src/c2ast.hpp +1 -1
- data/ext/libsass/src/cencode.c +4 -6
- data/ext/libsass/src/check_nesting.cpp +40 -41
- data/ext/libsass/src/check_nesting.hpp +6 -2
- data/ext/libsass/src/color_maps.cpp +14 -13
- data/ext/libsass/src/color_maps.hpp +1 -9
- data/ext/libsass/src/constants.cpp +5 -0
- data/ext/libsass/src/constants.hpp +6 -0
- data/ext/libsass/src/context.cpp +92 -119
- data/ext/libsass/src/context.hpp +41 -53
- data/ext/libsass/src/cssize.cpp +66 -149
- data/ext/libsass/src/cssize.hpp +17 -23
- data/ext/libsass/src/dart_helpers.hpp +199 -0
- data/ext/libsass/src/debugger.hpp +451 -295
- data/ext/libsass/src/emitter.cpp +15 -16
- data/ext/libsass/src/emitter.hpp +10 -12
- data/ext/libsass/src/environment.cpp +27 -27
- data/ext/libsass/src/environment.hpp +29 -24
- data/ext/libsass/src/error_handling.cpp +62 -41
- data/ext/libsass/src/error_handling.hpp +61 -51
- data/ext/libsass/src/eval.cpp +167 -281
- data/ext/libsass/src/eval.hpp +27 -29
- data/ext/libsass/src/eval_selectors.cpp +75 -0
- data/ext/libsass/src/expand.cpp +275 -222
- data/ext/libsass/src/expand.hpp +36 -16
- data/ext/libsass/src/extender.cpp +1188 -0
- data/ext/libsass/src/extender.hpp +399 -0
- data/ext/libsass/src/extension.cpp +43 -0
- data/ext/libsass/src/extension.hpp +89 -0
- data/ext/libsass/src/file.cpp +81 -72
- data/ext/libsass/src/file.hpp +28 -37
- data/ext/libsass/src/fn_colors.cpp +20 -18
- data/ext/libsass/src/fn_lists.cpp +30 -29
- data/ext/libsass/src/fn_maps.cpp +3 -3
- data/ext/libsass/src/fn_miscs.cpp +34 -46
- data/ext/libsass/src/fn_numbers.cpp +20 -13
- data/ext/libsass/src/fn_selectors.cpp +98 -128
- data/ext/libsass/src/fn_strings.cpp +47 -33
- data/ext/libsass/src/fn_utils.cpp +31 -29
- data/ext/libsass/src/fn_utils.hpp +17 -11
- data/ext/libsass/src/inspect.cpp +186 -148
- data/ext/libsass/src/inspect.hpp +31 -29
- data/ext/libsass/src/lexer.cpp +20 -82
- data/ext/libsass/src/lexer.hpp +5 -16
- data/ext/libsass/src/listize.cpp +23 -37
- data/ext/libsass/src/listize.hpp +8 -9
- data/ext/libsass/src/mapping.hpp +1 -0
- data/ext/libsass/src/memory/allocator.cpp +48 -0
- data/ext/libsass/src/memory/allocator.hpp +138 -0
- data/ext/libsass/src/memory/config.hpp +20 -0
- data/ext/libsass/src/memory/memory_pool.hpp +186 -0
- data/ext/libsass/src/memory/{SharedPtr.cpp → shared_ptr.cpp} +2 -2
- data/ext/libsass/src/memory/{SharedPtr.hpp → shared_ptr.hpp} +55 -9
- data/ext/libsass/src/memory.hpp +12 -0
- data/ext/libsass/src/operation.hpp +71 -61
- data/ext/libsass/src/operators.cpp +19 -18
- data/ext/libsass/src/operators.hpp +11 -11
- data/ext/libsass/src/ordered_map.hpp +112 -0
- data/ext/libsass/src/output.cpp +45 -64
- data/ext/libsass/src/output.hpp +6 -6
- data/ext/libsass/src/parser.cpp +512 -700
- data/ext/libsass/src/parser.hpp +89 -97
- data/ext/libsass/src/parser_selectors.cpp +189 -0
- data/ext/libsass/src/permutate.hpp +164 -0
- data/ext/libsass/src/plugins.cpp +7 -7
- data/ext/libsass/src/plugins.hpp +8 -8
- data/ext/libsass/src/position.cpp +7 -26
- data/ext/libsass/src/position.hpp +44 -21
- data/ext/libsass/src/prelexer.cpp +6 -6
- data/ext/libsass/src/remove_placeholders.cpp +55 -56
- data/ext/libsass/src/remove_placeholders.hpp +21 -18
- data/ext/libsass/src/sass.cpp +16 -15
- data/ext/libsass/src/sass.hpp +10 -5
- data/ext/libsass/src/sass2scss.cpp +4 -4
- data/ext/libsass/src/sass_context.cpp +91 -122
- data/ext/libsass/src/sass_context.hpp +2 -2
- data/ext/libsass/src/sass_functions.cpp +1 -1
- data/ext/libsass/src/sass_values.cpp +8 -11
- data/ext/libsass/src/settings.hpp +19 -0
- data/ext/libsass/src/source.cpp +69 -0
- data/ext/libsass/src/source.hpp +95 -0
- data/ext/libsass/src/source_data.hpp +32 -0
- data/ext/libsass/src/source_map.cpp +22 -18
- data/ext/libsass/src/source_map.hpp +12 -9
- data/ext/libsass/src/stylesheet.cpp +22 -0
- data/ext/libsass/src/stylesheet.hpp +57 -0
- data/ext/libsass/src/to_value.cpp +2 -2
- data/ext/libsass/src/to_value.hpp +1 -1
- data/ext/libsass/src/units.cpp +24 -22
- data/ext/libsass/src/units.hpp +8 -8
- data/ext/libsass/src/utf8_string.cpp +9 -10
- data/ext/libsass/src/utf8_string.hpp +7 -6
- data/ext/libsass/src/util.cpp +48 -50
- data/ext/libsass/src/util.hpp +20 -21
- data/ext/libsass/src/util_string.cpp +111 -61
- data/ext/libsass/src/util_string.hpp +62 -8
- data/ext/libsass/src/values.cpp +12 -12
- data/lib/sassc/engine.rb +5 -3
- data/lib/sassc/functions_handler.rb +8 -8
- data/lib/sassc/native.rb +4 -6
- data/lib/sassc/script.rb +4 -4
- data/lib/sassc/version.rb +1 -1
- data/test/functions_test.rb +18 -1
- data/test/native_test.rb +4 -4
- metadata +29 -15
- data/ext/libsass/src/extend.cpp +0 -2132
- data/ext/libsass/src/extend.hpp +0 -86
- data/ext/libsass/src/node.cpp +0 -322
- data/ext/libsass/src/node.hpp +0 -118
- data/ext/libsass/src/paths.hpp +0 -71
- data/ext/libsass/src/sass_util.cpp +0 -152
- data/ext/libsass/src/sass_util.hpp +0 -256
- data/ext/libsass/src/subset_map.cpp +0 -58
- data/ext/libsass/src/subset_map.hpp +0 -76
- data/lib/sassc/native/lib_c.rb +0 -21
@@ -0,0 +1,1188 @@
|
|
1
|
+
// sass.hpp must go before all system headers to get the
|
2
|
+
// __EXTENSIONS__ fix on Solaris.
|
3
|
+
#include "sass.hpp"
|
4
|
+
#include "ast.hpp"
|
5
|
+
|
6
|
+
#include "extender.hpp"
|
7
|
+
#include "permutate.hpp"
|
8
|
+
#include "dart_helpers.hpp"
|
9
|
+
|
10
|
+
namespace Sass {
|
11
|
+
|
12
|
+
// ##########################################################################
|
13
|
+
// Constructor without default [mode].
|
14
|
+
// [traces] are needed to throw errors.
|
15
|
+
// ##########################################################################
|
16
|
+
Extender::Extender(Backtraces& traces) :
|
17
|
+
mode(NORMAL),
|
18
|
+
traces(traces),
|
19
|
+
selectors(),
|
20
|
+
extensions(),
|
21
|
+
extensionsByExtender(),
|
22
|
+
mediaContexts(),
|
23
|
+
sourceSpecificity(),
|
24
|
+
originals()
|
25
|
+
{}
|
26
|
+
|
27
|
+
// ##########################################################################
|
28
|
+
// Constructor with specific [mode].
|
29
|
+
// [traces] are needed to throw errors.
|
30
|
+
// ##########################################################################
|
31
|
+
Extender::Extender(ExtendMode mode, Backtraces& traces) :
|
32
|
+
mode(mode),
|
33
|
+
traces(traces),
|
34
|
+
selectors(),
|
35
|
+
extensions(),
|
36
|
+
extensionsByExtender(),
|
37
|
+
mediaContexts(),
|
38
|
+
sourceSpecificity(),
|
39
|
+
originals()
|
40
|
+
{}
|
41
|
+
|
42
|
+
// ##########################################################################
|
43
|
+
// Extends [selector] with [source] extender and [targets] extendees.
|
44
|
+
// This works as though `source {@extend target}` were written in the
|
45
|
+
// stylesheet, with the exception that [target] can contain compound
|
46
|
+
// selectors which must be extended as a unit.
|
47
|
+
// ##########################################################################
|
48
|
+
SelectorListObj Extender::extend(
|
49
|
+
SelectorListObj& selector,
|
50
|
+
const SelectorListObj& source,
|
51
|
+
const SelectorListObj& targets,
|
52
|
+
Backtraces& traces)
|
53
|
+
{
|
54
|
+
return extendOrReplace(selector, source, targets, ExtendMode::TARGETS, traces);
|
55
|
+
}
|
56
|
+
// EO Extender::extend
|
57
|
+
|
58
|
+
// ##########################################################################
|
59
|
+
// Returns a copy of [selector] with [targets] replaced by [source].
|
60
|
+
// ##########################################################################
|
61
|
+
SelectorListObj Extender::replace(
|
62
|
+
SelectorListObj& selector,
|
63
|
+
const SelectorListObj& source,
|
64
|
+
const SelectorListObj& targets,
|
65
|
+
Backtraces& traces)
|
66
|
+
{
|
67
|
+
return extendOrReplace(selector, source, targets, ExtendMode::REPLACE, traces);
|
68
|
+
}
|
69
|
+
// EO Extender::replace
|
70
|
+
|
71
|
+
// ##########################################################################
|
72
|
+
// A helper function for [extend] and [replace].
|
73
|
+
// ##########################################################################
|
74
|
+
SelectorListObj Extender::extendOrReplace(
|
75
|
+
SelectorListObj& selector,
|
76
|
+
const SelectorListObj& source,
|
77
|
+
const SelectorListObj& targets,
|
78
|
+
const ExtendMode mode,
|
79
|
+
Backtraces& traces)
|
80
|
+
{
|
81
|
+
ExtSelExtMapEntry extenders;
|
82
|
+
|
83
|
+
for (auto complex : source->elements()) {
|
84
|
+
// Extension.oneOff(complex as ComplexSelector)
|
85
|
+
extenders.insert(complex, Extension(complex));
|
86
|
+
}
|
87
|
+
|
88
|
+
for (auto complex : targets->elements()) {
|
89
|
+
|
90
|
+
// This seems superfluous, check is done before!?
|
91
|
+
// if (complex->length() != 1) {
|
92
|
+
// error("complex selectors may not be extended.", complex->pstate(), traces);
|
93
|
+
// }
|
94
|
+
|
95
|
+
if (const CompoundSelector* compound = complex->first()->getCompound()) {
|
96
|
+
|
97
|
+
ExtSelExtMap extensions;
|
98
|
+
|
99
|
+
for (const SimpleSelectorObj& simple : compound->elements()) {
|
100
|
+
extensions.insert(std::make_pair(simple, extenders));
|
101
|
+
}
|
102
|
+
|
103
|
+
Extender extender(mode, traces);
|
104
|
+
|
105
|
+
if (!selector->is_invisible()) {
|
106
|
+
for (auto sel : selector->elements()) {
|
107
|
+
extender.originals.insert(sel);
|
108
|
+
}
|
109
|
+
}
|
110
|
+
|
111
|
+
selector = extender.extendList(selector, extensions, {});
|
112
|
+
|
113
|
+
}
|
114
|
+
|
115
|
+
}
|
116
|
+
|
117
|
+
return selector;
|
118
|
+
|
119
|
+
}
|
120
|
+
// EO extendOrReplace
|
121
|
+
|
122
|
+
// ##########################################################################
|
123
|
+
// The set of all simple selectors in style rules handled
|
124
|
+
// by this extender. This includes simple selectors that
|
125
|
+
// were added because of downstream extensions.
|
126
|
+
// ##########################################################################
|
127
|
+
ExtSmplSelSet Extender::getSimpleSelectors() const
|
128
|
+
{
|
129
|
+
ExtSmplSelSet set;
|
130
|
+
for (auto& entry : selectors) {
|
131
|
+
set.insert(entry.first);
|
132
|
+
}
|
133
|
+
return set;
|
134
|
+
}
|
135
|
+
// EO getSimpleSelectors
|
136
|
+
|
137
|
+
// ##########################################################################
|
138
|
+
// Check for extends that have not been satisfied.
|
139
|
+
// Returns true if any non-optional extension did not
|
140
|
+
// extend any selector. Updates the passed reference
|
141
|
+
// to point to that Extension for further analysis.
|
142
|
+
// ##########################################################################
|
143
|
+
bool Extender::checkForUnsatisfiedExtends(Extension& unsatisfied) const
|
144
|
+
{
|
145
|
+
if (selectors.empty()) return false;
|
146
|
+
ExtSmplSelSet originals = getSimpleSelectors();
|
147
|
+
for (auto target : extensions) {
|
148
|
+
SimpleSelector* key = target.first;
|
149
|
+
ExtSelExtMapEntry& val = target.second;
|
150
|
+
if (val.empty()) continue;
|
151
|
+
if (originals.find(key) == originals.end()) {
|
152
|
+
const Extension& extension = val.front().second;
|
153
|
+
if (extension.isOptional) continue;
|
154
|
+
unsatisfied = extension;
|
155
|
+
return true;
|
156
|
+
}
|
157
|
+
}
|
158
|
+
return false;
|
159
|
+
}
|
160
|
+
// EO checkUnsatisfiedExtends
|
161
|
+
|
162
|
+
// ##########################################################################
|
163
|
+
// Adds [selector] to this extender, with [selectorSpan] as the span covering
|
164
|
+
// the selector and [ruleSpan] as the span covering the entire style rule.
|
165
|
+
// Extends [selector] using any registered extensions, then returns an empty
|
166
|
+
// [ModifiableCssStyleRule] with the resulting selector. If any more relevant
|
167
|
+
// extensions are added, the returned rule is automatically updated.
|
168
|
+
// The [mediaContext] is the media query context in which the selector was
|
169
|
+
// defined, or `null` if it was defined at the top level of the document.
|
170
|
+
// ##########################################################################
|
171
|
+
void Extender::addSelector(
|
172
|
+
const SelectorListObj& selector,
|
173
|
+
const CssMediaRuleObj& mediaContext)
|
174
|
+
{
|
175
|
+
|
176
|
+
// Note: dart-sass makes a copy here AFAICT
|
177
|
+
// Note: probably why we have originalStack
|
178
|
+
// SelectorListObj original = selector;
|
179
|
+
|
180
|
+
if (!selector->isInvisible()) {
|
181
|
+
for (auto complex : selector->elements()) {
|
182
|
+
originals.insert(complex);
|
183
|
+
}
|
184
|
+
}
|
185
|
+
|
186
|
+
if (!extensions.empty()) {
|
187
|
+
|
188
|
+
SelectorListObj res = extendList(selector, extensions, mediaContext);
|
189
|
+
|
190
|
+
selector->elements(res->elements());
|
191
|
+
|
192
|
+
}
|
193
|
+
|
194
|
+
if (!mediaContext.isNull()) {
|
195
|
+
mediaContexts.insert(selector, mediaContext);
|
196
|
+
}
|
197
|
+
|
198
|
+
registerSelector(selector, selector);
|
199
|
+
|
200
|
+
}
|
201
|
+
// EO addSelector
|
202
|
+
|
203
|
+
// ##########################################################################
|
204
|
+
// Registers the [SimpleSelector]s in [list]
|
205
|
+
// to point to [rule] in [selectors].
|
206
|
+
// ##########################################################################
|
207
|
+
void Extender::registerSelector(
|
208
|
+
const SelectorListObj& list,
|
209
|
+
const SelectorListObj& rule)
|
210
|
+
{
|
211
|
+
if (list.isNull() || list->empty()) return;
|
212
|
+
for (auto complex : list->elements()) {
|
213
|
+
for (auto component : complex->elements()) {
|
214
|
+
if (auto compound = component->getCompound()) {
|
215
|
+
for (SimpleSelector* simple : compound->elements()) {
|
216
|
+
selectors[simple].insert(rule);
|
217
|
+
if (auto pseudo = simple->getPseudoSelector()) {
|
218
|
+
if (pseudo->selector()) {
|
219
|
+
auto sel = pseudo->selector();
|
220
|
+
registerSelector(sel, rule);
|
221
|
+
}
|
222
|
+
}
|
223
|
+
}
|
224
|
+
}
|
225
|
+
}
|
226
|
+
}
|
227
|
+
}
|
228
|
+
// EO registerSelector
|
229
|
+
|
230
|
+
// ##########################################################################
|
231
|
+
// Returns an extension that combines [left] and [right]. Throws
|
232
|
+
// a [SassException] if [left] and [right] have incompatible
|
233
|
+
// media contexts. Throws an [ArgumentError] if [left]
|
234
|
+
// and [right] don't have the same extender and target.
|
235
|
+
// ##########################################################################
|
236
|
+
Extension Extender::mergeExtension(
|
237
|
+
const Extension& lhs,
|
238
|
+
const Extension& rhs)
|
239
|
+
{
|
240
|
+
// If one extension is optional and doesn't add a
|
241
|
+
// special media context, it doesn't need to be merged.
|
242
|
+
if (rhs.isOptional && rhs.mediaContext.isNull()) return lhs;
|
243
|
+
if (lhs.isOptional && lhs.mediaContext.isNull()) return rhs;
|
244
|
+
|
245
|
+
Extension rv(lhs);
|
246
|
+
// ToDo: is this right?
|
247
|
+
rv.isOptional = true;
|
248
|
+
rv.isOriginal = false;
|
249
|
+
return rv;
|
250
|
+
}
|
251
|
+
// EO mergeExtension
|
252
|
+
|
253
|
+
// ##########################################################################
|
254
|
+
// Helper function to copy extension between maps
|
255
|
+
// ##########################################################################
|
256
|
+
// Seems only relevant for sass 4.0 modules
|
257
|
+
// ##########################################################################
|
258
|
+
/* void mapCopyExts(
|
259
|
+
ExtSelExtMap& dest,
|
260
|
+
const ExtSelExtMap& source)
|
261
|
+
{
|
262
|
+
for (auto it : source) {
|
263
|
+
SimpleSelectorObj key = it.first;
|
264
|
+
ExtSelExtMapEntry& inner = it.second;
|
265
|
+
ExtSelExtMap::iterator dmap = dest.find(key);
|
266
|
+
if (dmap == dest.end()) {
|
267
|
+
dest.insert(std::make_pair(key, inner));
|
268
|
+
}
|
269
|
+
else {
|
270
|
+
ExtSelExtMapEntry& imap = dmap->second;
|
271
|
+
// ToDo: optimize ordered_map API!
|
272
|
+
// ToDo: we iterate and fetch the value
|
273
|
+
for (ComplexSelectorObj& it2 : inner) {
|
274
|
+
imap.insert(it2, inner.get(it2));
|
275
|
+
}
|
276
|
+
}
|
277
|
+
}
|
278
|
+
} */
|
279
|
+
// EO mapCopyExts
|
280
|
+
|
281
|
+
// ##########################################################################
|
282
|
+
// Adds an extension to this extender. The [extender] is the selector for the
|
283
|
+
// style rule in which the extension is defined, and [target] is the selector
|
284
|
+
// passed to `@extend`. The [extend] provides the extend span and indicates
|
285
|
+
// whether the extension is optional. The [mediaContext] defines the media query
|
286
|
+
// context in which the extension is defined. It can only extend selectors
|
287
|
+
// within the same context. A `null` context indicates no media queries.
|
288
|
+
// ##########################################################################
|
289
|
+
// ToDo: rename extender to parent, since it is not involved in extending stuff
|
290
|
+
// ToDo: check why dart sass passes the ExtendRule around (is this the main selector?)
|
291
|
+
// ##########################################################################
|
292
|
+
// Note: this function could need some logic cleanup
|
293
|
+
// ##########################################################################
|
294
|
+
void Extender::addExtension(
|
295
|
+
const SelectorListObj& extender,
|
296
|
+
const SimpleSelectorObj& target,
|
297
|
+
const CssMediaRuleObj& mediaQueryContext,
|
298
|
+
bool is_optional)
|
299
|
+
{
|
300
|
+
|
301
|
+
auto rules = selectors.find(target);
|
302
|
+
bool hasRule = rules != selectors.end();
|
303
|
+
|
304
|
+
ExtSelExtMapEntry newExtensions;
|
305
|
+
|
306
|
+
// ToDo: we check this here first and fetch the same? item again after the loop!?
|
307
|
+
bool hasExistingExtensions = extensionsByExtender.find(target) != extensionsByExtender.end();
|
308
|
+
|
309
|
+
ExtSelExtMapEntry& sources = extensions[target];
|
310
|
+
|
311
|
+
for (auto& complex : extender->elements()) {
|
312
|
+
|
313
|
+
Extension state(complex);
|
314
|
+
// ToDo: fine-tune public API
|
315
|
+
state.target = target;
|
316
|
+
state.isOptional = is_optional;
|
317
|
+
state.mediaContext = mediaQueryContext;
|
318
|
+
|
319
|
+
if (sources.hasKey(complex)) {
|
320
|
+
// If there's already an extend from [extender] to [target],
|
321
|
+
// we don't need to re-run the extension. We may need to
|
322
|
+
// mark the extension as mandatory, though.
|
323
|
+
// sources.insert(complex, mergeExtension(existingState->second, state);
|
324
|
+
// ToDo: implement behavior once use case is found!?
|
325
|
+
continue;
|
326
|
+
}
|
327
|
+
|
328
|
+
sources.insert(complex, state);
|
329
|
+
|
330
|
+
for (auto& component : complex->elements()) {
|
331
|
+
if (auto compound = component->getCompound()) {
|
332
|
+
for (auto& simple : compound->elements()) {
|
333
|
+
extensionsByExtender[simple].push_back(state);
|
334
|
+
if (sourceSpecificity.find(simple) == sourceSpecificity.end()) {
|
335
|
+
// Only source specificity for the original selector is relevant.
|
336
|
+
// Selectors generated by `@extend` don't get new specificity.
|
337
|
+
sourceSpecificity[simple] = complex->maxSpecificity();
|
338
|
+
}
|
339
|
+
}
|
340
|
+
}
|
341
|
+
}
|
342
|
+
|
343
|
+
if (hasRule || hasExistingExtensions) {
|
344
|
+
newExtensions.insert(complex, state);
|
345
|
+
}
|
346
|
+
|
347
|
+
}
|
348
|
+
// EO foreach complex
|
349
|
+
|
350
|
+
if (newExtensions.empty()) {
|
351
|
+
return;
|
352
|
+
}
|
353
|
+
|
354
|
+
ExtSelExtMap newExtensionsByTarget;
|
355
|
+
newExtensionsByTarget.insert(std::make_pair(target, newExtensions));
|
356
|
+
// ToDo: do we really need to fetch again (see top off fn)
|
357
|
+
auto existingExtensions = extensionsByExtender.find(target);
|
358
|
+
if (existingExtensions != extensionsByExtender.end()) {
|
359
|
+
if (hasExistingExtensions && !existingExtensions->second.empty()) {
|
360
|
+
// Seems only relevant for sass 4.0 modules
|
361
|
+
// auto additionalExtensions =
|
362
|
+
extendExistingExtensions(existingExtensions->second, newExtensionsByTarget);
|
363
|
+
// Seems only relevant for sass 4.0 modules
|
364
|
+
/* if (!additionalExtensions.empty()) {
|
365
|
+
mapCopyExts(newExtensionsByTarget, additionalExtensions);
|
366
|
+
} */
|
367
|
+
}
|
368
|
+
}
|
369
|
+
|
370
|
+
if (hasRule) {
|
371
|
+
extendExistingStyleRules(selectors[target], newExtensionsByTarget);
|
372
|
+
}
|
373
|
+
|
374
|
+
}
|
375
|
+
// EO addExtension
|
376
|
+
|
377
|
+
// ##########################################################################
|
378
|
+
// Extend [extensions] using [newExtensions].
|
379
|
+
// ##########################################################################
|
380
|
+
// Note: dart-sass throws an error in here
|
381
|
+
// ##########################################################################
|
382
|
+
void Extender::extendExistingStyleRules(
|
383
|
+
const ExtListSelSet& rules,
|
384
|
+
const ExtSelExtMap& newExtensions)
|
385
|
+
{
|
386
|
+
// Is a modifyableCssStyleRUle in dart sass
|
387
|
+
for (const SelectorListObj& rule : rules) {
|
388
|
+
const SelectorListObj& oldValue = SASS_MEMORY_COPY(rule);
|
389
|
+
CssMediaRuleObj mediaContext;
|
390
|
+
if (mediaContexts.hasKey(rule)) mediaContext = mediaContexts.get(rule);
|
391
|
+
SelectorListObj ext = extendList(rule, newExtensions, mediaContext);
|
392
|
+
// If no extends actually happened (for example because unification
|
393
|
+
// failed), we don't need to re-register the selector.
|
394
|
+
if (ObjEqualityFn(oldValue, ext)) continue;
|
395
|
+
rule->elements(ext->elements());
|
396
|
+
registerSelector(rule, rule);
|
397
|
+
|
398
|
+
}
|
399
|
+
}
|
400
|
+
// EO extendExistingStyleRules
|
401
|
+
|
402
|
+
// ##########################################################################
|
403
|
+
// Extend [extensions] using [newExtensions]. Note that this does duplicate
|
404
|
+
// some work done by [_extendExistingStyleRules], but it's necessary to
|
405
|
+
// expand each extension's extender separately without reference to the full
|
406
|
+
// selector list, so that relevant results don't get trimmed too early.
|
407
|
+
//
|
408
|
+
// Returns extensions that should be added to [newExtensions] before
|
409
|
+
// extending selectors in order to properly handle extension loops such as:
|
410
|
+
//
|
411
|
+
// .c {x: y; @extend .a}
|
412
|
+
// .x.y.a {@extend .b}
|
413
|
+
// .z.b {@extend .c}
|
414
|
+
//
|
415
|
+
// Returns `null` (Note: empty map) if there are no extensions to add.
|
416
|
+
// ##########################################################################
|
417
|
+
// Note: maybe refactor to return `bool` (and pass reference)
|
418
|
+
// Note: dart-sass throws an error in here
|
419
|
+
// ##########################################################################
|
420
|
+
ExtSelExtMap Extender::extendExistingExtensions(
|
421
|
+
// Taking in a reference here makes MSVC debug stuck!?
|
422
|
+
const sass::vector<Extension>& oldExtensions,
|
423
|
+
const ExtSelExtMap& newExtensions)
|
424
|
+
{
|
425
|
+
|
426
|
+
ExtSelExtMap additionalExtensions;
|
427
|
+
|
428
|
+
// During the loop `oldExtensions` vector might be changed.
|
429
|
+
// Callers normally pass this from `extensionsByExtender` and
|
430
|
+
// that points back to the `sources` vector from `extensions`.
|
431
|
+
for (size_t i = 0, iL = oldExtensions.size(); i < iL; i += 1) {
|
432
|
+
const Extension& extension = oldExtensions[i];
|
433
|
+
ExtSelExtMapEntry& sources = extensions[extension.target];
|
434
|
+
sass::vector<ComplexSelectorObj> selectors(extendComplex(
|
435
|
+
extension.extender,
|
436
|
+
newExtensions,
|
437
|
+
extension.mediaContext
|
438
|
+
));
|
439
|
+
|
440
|
+
if (selectors.empty()) {
|
441
|
+
continue;
|
442
|
+
}
|
443
|
+
|
444
|
+
// ToDo: "catch" error from extend
|
445
|
+
|
446
|
+
bool first = false, containsExtension =
|
447
|
+
ObjEqualityFn(selectors.front(), extension.extender);
|
448
|
+
for (const ComplexSelectorObj& complex : selectors) {
|
449
|
+
// If the output contains the original complex
|
450
|
+
// selector, there's no need to recreate it.
|
451
|
+
if (containsExtension && first) {
|
452
|
+
first = false;
|
453
|
+
continue;
|
454
|
+
}
|
455
|
+
|
456
|
+
const Extension withExtender =
|
457
|
+
extension.withExtender(complex);
|
458
|
+
if (sources.hasKey(complex)) {
|
459
|
+
sources.insert(complex, mergeExtension(
|
460
|
+
sources.get(complex), withExtender));
|
461
|
+
}
|
462
|
+
else {
|
463
|
+
sources.insert(complex, withExtender);
|
464
|
+
/*
|
465
|
+
// Seems only relevant for sass 4.0 modules
|
466
|
+
for (auto& component : complex->elements()) {
|
467
|
+
if (auto compound = component->getCompound()) {
|
468
|
+
for (auto& simple : compound->elements()) {
|
469
|
+
extensionsByExtender[simple].push_back(withExtender);
|
470
|
+
}
|
471
|
+
}
|
472
|
+
}
|
473
|
+
if (newExtensions.find(extension.target) != newExtensions.end()) {
|
474
|
+
additionalExtensions[extension.target].insert(complex, withExtender);
|
475
|
+
}
|
476
|
+
*/
|
477
|
+
}
|
478
|
+
}
|
479
|
+
|
480
|
+
// If [selectors] doesn't contain [extension.extender],
|
481
|
+
// for example if it was replaced due to :not() expansion,
|
482
|
+
// we must get rid of the old version.
|
483
|
+
/*
|
484
|
+
// Seems only relevant for sass 4.0 modules
|
485
|
+
if (!containsExtension) {
|
486
|
+
sources.erase(extension.extender);
|
487
|
+
}
|
488
|
+
*/
|
489
|
+
|
490
|
+
}
|
491
|
+
|
492
|
+
return additionalExtensions;
|
493
|
+
|
494
|
+
}
|
495
|
+
// EO extendExistingExtensions
|
496
|
+
|
497
|
+
// ##########################################################################
|
498
|
+
// Extends [list] using [extensions].
|
499
|
+
// ##########################################################################
|
500
|
+
SelectorListObj Extender::extendList(
|
501
|
+
const SelectorListObj& list,
|
502
|
+
const ExtSelExtMap& extensions,
|
503
|
+
const CssMediaRuleObj& mediaQueryContext)
|
504
|
+
{
|
505
|
+
|
506
|
+
// This could be written more simply using [List.map], but we want to
|
507
|
+
// avoid any allocations in the common case where no extends apply.
|
508
|
+
sass::vector<ComplexSelectorObj> extended;
|
509
|
+
for (size_t i = 0; i < list->length(); i++) {
|
510
|
+
const ComplexSelectorObj& complex = list->get(i);
|
511
|
+
sass::vector<ComplexSelectorObj> result =
|
512
|
+
extendComplex(complex, extensions, mediaQueryContext);
|
513
|
+
if (result.empty()) {
|
514
|
+
if (!extended.empty()) {
|
515
|
+
extended.push_back(complex);
|
516
|
+
}
|
517
|
+
}
|
518
|
+
else {
|
519
|
+
if (extended.empty()) {
|
520
|
+
for (size_t n = 0; n < i; n += 1) {
|
521
|
+
extended.push_back(list->get(n));
|
522
|
+
}
|
523
|
+
}
|
524
|
+
for (auto sel : result) {
|
525
|
+
extended.push_back(sel);
|
526
|
+
}
|
527
|
+
}
|
528
|
+
}
|
529
|
+
|
530
|
+
if (extended.empty()) {
|
531
|
+
return list;
|
532
|
+
}
|
533
|
+
|
534
|
+
SelectorListObj rv = SASS_MEMORY_NEW(SelectorList, list->pstate());
|
535
|
+
rv->concat(trim(extended, originals));
|
536
|
+
return rv;
|
537
|
+
|
538
|
+
}
|
539
|
+
// EO extendList
|
540
|
+
|
541
|
+
// ##########################################################################
|
542
|
+
// Extends [complex] using [extensions], and
|
543
|
+
// returns the contents of a [SelectorList].
|
544
|
+
// ##########################################################################
|
545
|
+
sass::vector<ComplexSelectorObj> Extender::extendComplex(
|
546
|
+
// Taking in a reference here makes MSVC debug stuck!?
|
547
|
+
const ComplexSelectorObj& complex,
|
548
|
+
const ExtSelExtMap& extensions,
|
549
|
+
const CssMediaRuleObj& mediaQueryContext)
|
550
|
+
{
|
551
|
+
|
552
|
+
// The complex selectors that each compound selector in [complex.components]
|
553
|
+
// can expand to.
|
554
|
+
//
|
555
|
+
// For example, given
|
556
|
+
//
|
557
|
+
// .a .b {...}
|
558
|
+
// .x .y {@extend .b}
|
559
|
+
//
|
560
|
+
// this will contain
|
561
|
+
//
|
562
|
+
// [
|
563
|
+
// [.a],
|
564
|
+
// [.b, .x .y]
|
565
|
+
// ]
|
566
|
+
//
|
567
|
+
// This could be written more simply using [List.map], but we want to avoid
|
568
|
+
// any allocations in the common case where no extends apply.
|
569
|
+
|
570
|
+
sass::vector<ComplexSelectorObj> result;
|
571
|
+
sass::vector<sass::vector<ComplexSelectorObj>> extendedNotExpanded;
|
572
|
+
bool isOriginal = originals.find(complex) != originals.end();
|
573
|
+
for (size_t i = 0; i < complex->length(); i += 1) {
|
574
|
+
const SelectorComponentObj& component = complex->get(i);
|
575
|
+
if (CompoundSelector* compound = Cast<CompoundSelector>(component)) {
|
576
|
+
sass::vector<ComplexSelectorObj> extended = extendCompound(
|
577
|
+
compound, extensions, mediaQueryContext, isOriginal);
|
578
|
+
if (extended.empty()) {
|
579
|
+
if (!extendedNotExpanded.empty()) {
|
580
|
+
extendedNotExpanded.push_back({
|
581
|
+
compound->wrapInComplex()
|
582
|
+
});
|
583
|
+
}
|
584
|
+
}
|
585
|
+
else {
|
586
|
+
// Note: dart-sass checks for null!?
|
587
|
+
if (extendedNotExpanded.empty()) {
|
588
|
+
for (size_t n = 0; n < i; n++) {
|
589
|
+
extendedNotExpanded.push_back({
|
590
|
+
complex->at(n)->wrapInComplex()
|
591
|
+
});
|
592
|
+
}
|
593
|
+
}
|
594
|
+
extendedNotExpanded.push_back(extended);
|
595
|
+
}
|
596
|
+
}
|
597
|
+
else {
|
598
|
+
// Note: dart-sass checks for null!?
|
599
|
+
if (!extendedNotExpanded.empty()) {
|
600
|
+
extendedNotExpanded.push_back({
|
601
|
+
component->wrapInComplex()
|
602
|
+
});
|
603
|
+
}
|
604
|
+
}
|
605
|
+
}
|
606
|
+
|
607
|
+
// Note: dart-sass checks for null!?
|
608
|
+
if (extendedNotExpanded.empty()) {
|
609
|
+
return {};
|
610
|
+
}
|
611
|
+
|
612
|
+
bool first = true;
|
613
|
+
|
614
|
+
// ToDo: either change weave or paths to work with the same data?
|
615
|
+
sass::vector<sass::vector<ComplexSelectorObj>>
|
616
|
+
paths = permutate(extendedNotExpanded);
|
617
|
+
|
618
|
+
for (const sass::vector<ComplexSelectorObj>& path : paths) {
|
619
|
+
// Unpack the inner complex selector to component list
|
620
|
+
sass::vector<sass::vector<SelectorComponentObj>> _paths;
|
621
|
+
for (const ComplexSelectorObj& sel : path) {
|
622
|
+
_paths.insert(_paths.end(), sel->elements());
|
623
|
+
}
|
624
|
+
|
625
|
+
sass::vector<sass::vector<SelectorComponentObj>> weaved = weave(_paths);
|
626
|
+
|
627
|
+
for (sass::vector<SelectorComponentObj>& components : weaved) {
|
628
|
+
|
629
|
+
ComplexSelectorObj cplx = SASS_MEMORY_NEW(ComplexSelector, "[phony]");
|
630
|
+
cplx->hasPreLineFeed(complex->hasPreLineFeed());
|
631
|
+
for (auto& pp : path) {
|
632
|
+
if (pp->hasPreLineFeed()) {
|
633
|
+
cplx->hasPreLineFeed(true);
|
634
|
+
}
|
635
|
+
}
|
636
|
+
cplx->elements(components);
|
637
|
+
|
638
|
+
// Make sure that copies of [complex] retain their status
|
639
|
+
// as "original" selectors. This includes selectors that
|
640
|
+
// are modified because a :not() was extended into.
|
641
|
+
if (first && originals.find(complex) != originals.end()) {
|
642
|
+
originals.insert(cplx);
|
643
|
+
}
|
644
|
+
first = false;
|
645
|
+
|
646
|
+
result.push_back(cplx);
|
647
|
+
|
648
|
+
}
|
649
|
+
|
650
|
+
}
|
651
|
+
|
652
|
+
return result;
|
653
|
+
}
|
654
|
+
// EO extendComplex
|
655
|
+
|
656
|
+
// ##########################################################################
|
657
|
+
// Returns a one-off [Extension] whose
|
658
|
+
// extender is composed solely of [simple].
|
659
|
+
// ##########################################################################
|
660
|
+
Extension Extender::extensionForSimple(
|
661
|
+
const SimpleSelectorObj& simple) const
|
662
|
+
{
|
663
|
+
Extension extension(simple->wrapInComplex());
|
664
|
+
extension.specificity = maxSourceSpecificity(simple);
|
665
|
+
extension.isOriginal = true;
|
666
|
+
return extension;
|
667
|
+
}
|
668
|
+
// Extender::extensionForSimple
|
669
|
+
|
670
|
+
// ##########################################################################
|
671
|
+
// Returns a one-off [Extension] whose extender is composed
|
672
|
+
// solely of a compound selector containing [simples].
|
673
|
+
// ##########################################################################
|
674
|
+
Extension Extender::extensionForCompound(
|
675
|
+
// Taking in a reference here makes MSVC debug stuck!?
|
676
|
+
const sass::vector<SimpleSelectorObj>& simples) const
|
677
|
+
{
|
678
|
+
CompoundSelectorObj compound = SASS_MEMORY_NEW(CompoundSelector, SourceSpan("[ext]"));
|
679
|
+
compound->concat(simples);
|
680
|
+
Extension extension(compound->wrapInComplex());
|
681
|
+
// extension.specificity = sourceSpecificity[simple];
|
682
|
+
extension.isOriginal = true;
|
683
|
+
return extension;
|
684
|
+
}
|
685
|
+
// EO extensionForCompound
|
686
|
+
|
687
|
+
// ##########################################################################
|
688
|
+
// Extends [compound] using [extensions], and returns the
|
689
|
+
// contents of a [SelectorList]. The [inOriginal] parameter
|
690
|
+
// indicates whether this is in an original complex selector,
|
691
|
+
// meaning that [compound] should not be trimmed out.
|
692
|
+
// ##########################################################################
|
693
|
+
sass::vector<ComplexSelectorObj> Extender::extendCompound(
|
694
|
+
const CompoundSelectorObj& compound,
|
695
|
+
const ExtSelExtMap& extensions,
|
696
|
+
const CssMediaRuleObj& mediaQueryContext,
|
697
|
+
bool inOriginal)
|
698
|
+
{
|
699
|
+
|
700
|
+
// If there's more than one target and they all need to
|
701
|
+
// match, we track which targets are actually extended.
|
702
|
+
ExtSmplSelSet targetsUsed2;
|
703
|
+
|
704
|
+
ExtSmplSelSet* targetsUsed = nullptr;
|
705
|
+
|
706
|
+
if (mode != ExtendMode::NORMAL && extensions.size() > 1) {
|
707
|
+
targetsUsed = &targetsUsed2;
|
708
|
+
}
|
709
|
+
|
710
|
+
sass::vector<ComplexSelectorObj> result;
|
711
|
+
// The complex selectors produced from each component of [compound].
|
712
|
+
sass::vector<sass::vector<Extension>> options;
|
713
|
+
|
714
|
+
for (size_t i = 0; i < compound->length(); i++) {
|
715
|
+
const SimpleSelectorObj& simple = compound->get(i);
|
716
|
+
auto extended = extendSimple(simple, extensions, mediaQueryContext, targetsUsed);
|
717
|
+
if (extended.empty()) {
|
718
|
+
if (!options.empty()) {
|
719
|
+
options.push_back({ extensionForSimple(simple) });
|
720
|
+
}
|
721
|
+
}
|
722
|
+
else {
|
723
|
+
if (options.empty()) {
|
724
|
+
if (i != 0) {
|
725
|
+
sass::vector<SimpleSelectorObj> in;
|
726
|
+
for (size_t n = 0; n < i; n += 1) {
|
727
|
+
in.push_back(compound->get(n));
|
728
|
+
}
|
729
|
+
options.push_back({ extensionForCompound(in) });
|
730
|
+
}
|
731
|
+
}
|
732
|
+
options.insert(options.end(),
|
733
|
+
extended.begin(), extended.end());
|
734
|
+
}
|
735
|
+
}
|
736
|
+
|
737
|
+
if (options.empty()) {
|
738
|
+
return {};
|
739
|
+
}
|
740
|
+
|
741
|
+
// If [_mode] isn't [ExtendMode.normal] and we didn't use all
|
742
|
+
// the targets in [extensions], extension fails for [compound].
|
743
|
+
if (targetsUsed != nullptr) {
|
744
|
+
|
745
|
+
if (targetsUsed->size() != extensions.size()) {
|
746
|
+
if (!targetsUsed->empty()) {
|
747
|
+
return {};
|
748
|
+
}
|
749
|
+
}
|
750
|
+
}
|
751
|
+
|
752
|
+
// Optimize for the simple case of a single simple
|
753
|
+
// selector that doesn't need any unification.
|
754
|
+
if (options.size() == 1) {
|
755
|
+
sass::vector<Extension> exts = options[0];
|
756
|
+
for (size_t n = 0; n < exts.size(); n += 1) {
|
757
|
+
exts[n].assertCompatibleMediaContext(mediaQueryContext, traces);
|
758
|
+
result.push_back(exts[n].extender);
|
759
|
+
}
|
760
|
+
return result;
|
761
|
+
}
|
762
|
+
|
763
|
+
// Find all paths through [options]. In this case, each path represents a
|
764
|
+
// different unification of the base selector. For example, if we have:
|
765
|
+
//
|
766
|
+
// .a.b {...}
|
767
|
+
// .w .x {@extend .a}
|
768
|
+
// .y .z {@extend .b}
|
769
|
+
//
|
770
|
+
// then [options] is `[[.a, .w .x], [.b, .y .z]]` and `paths(options)` is
|
771
|
+
//
|
772
|
+
// [
|
773
|
+
// [.a, .b],
|
774
|
+
// [.a, .y .z],
|
775
|
+
// [.w .x, .b],
|
776
|
+
// [.w .x, .y .z]
|
777
|
+
// ]
|
778
|
+
//
|
779
|
+
// We then unify each path to get a list of complex selectors:
|
780
|
+
//
|
781
|
+
// [
|
782
|
+
// [.a.b],
|
783
|
+
// [.y .a.z],
|
784
|
+
// [.w .x.b],
|
785
|
+
// [.w .y .x.z, .y .w .x.z]
|
786
|
+
// ]
|
787
|
+
|
788
|
+
bool first = mode != ExtendMode::REPLACE;
|
789
|
+
sass::vector<ComplexSelectorObj> unifiedPaths;
|
790
|
+
sass::vector<sass::vector<Extension>> prePaths = permutate(options);
|
791
|
+
|
792
|
+
for (size_t i = 0; i < prePaths.size(); i += 1) {
|
793
|
+
sass::vector<sass::vector<SelectorComponentObj>> complexes;
|
794
|
+
const sass::vector<Extension>& path = prePaths[i];
|
795
|
+
if (first) {
|
796
|
+
// The first path is always the original selector. We can't just
|
797
|
+
// return [compound] directly because pseudo selectors may be
|
798
|
+
// modified, but we don't have to do any unification.
|
799
|
+
first = false;
|
800
|
+
CompoundSelectorObj mergedSelector =
|
801
|
+
SASS_MEMORY_NEW(CompoundSelector, "[ext]");
|
802
|
+
for (size_t n = 0; n < path.size(); n += 1) {
|
803
|
+
const ComplexSelectorObj& sel = path[n].extender;
|
804
|
+
if (CompoundSelectorObj compound = Cast<CompoundSelector>(sel->last())) {
|
805
|
+
mergedSelector->concat(compound->elements());
|
806
|
+
}
|
807
|
+
}
|
808
|
+
complexes.push_back({ mergedSelector });
|
809
|
+
}
|
810
|
+
else {
|
811
|
+
sass::vector<SimpleSelectorObj> originals;
|
812
|
+
sass::vector<sass::vector<SelectorComponentObj>> toUnify;
|
813
|
+
|
814
|
+
for (auto& state : path) {
|
815
|
+
if (state.isOriginal) {
|
816
|
+
const ComplexSelectorObj& sel = state.extender;
|
817
|
+
if (const CompoundSelector* compound = Cast<CompoundSelector>(sel->last())) {
|
818
|
+
originals.insert(originals.end(), compound->last());
|
819
|
+
}
|
820
|
+
}
|
821
|
+
else {
|
822
|
+
toUnify.push_back(state.extender->elements());
|
823
|
+
}
|
824
|
+
}
|
825
|
+
if (!originals.empty()) {
|
826
|
+
CompoundSelectorObj merged =
|
827
|
+
SASS_MEMORY_NEW(CompoundSelector, "[phony]");
|
828
|
+
merged->concat(originals);
|
829
|
+
toUnify.insert(toUnify.begin(), { merged });
|
830
|
+
}
|
831
|
+
complexes = unifyComplex(toUnify);
|
832
|
+
if (complexes.empty()) {
|
833
|
+
return {};
|
834
|
+
}
|
835
|
+
|
836
|
+
}
|
837
|
+
|
838
|
+
bool lineBreak = false;
|
839
|
+
// var specificity = _sourceSpecificityFor(compound);
|
840
|
+
for (const Extension& state : path) {
|
841
|
+
state.assertCompatibleMediaContext(mediaQueryContext, traces);
|
842
|
+
lineBreak = lineBreak || state.extender->hasPreLineFeed();
|
843
|
+
// specificity = math.max(specificity, state.specificity);
|
844
|
+
}
|
845
|
+
|
846
|
+
for (sass::vector<SelectorComponentObj>& components : complexes) {
|
847
|
+
auto sel = SASS_MEMORY_NEW(ComplexSelector, "[ext]");
|
848
|
+
sel->hasPreLineFeed(lineBreak);
|
849
|
+
sel->elements(components);
|
850
|
+
unifiedPaths.push_back(sel);
|
851
|
+
}
|
852
|
+
|
853
|
+
}
|
854
|
+
|
855
|
+
return unifiedPaths;
|
856
|
+
}
|
857
|
+
// EO extendCompound
|
858
|
+
|
859
|
+
// ##########################################################################
|
860
|
+
// Extends [simple] without extending the
|
861
|
+
// contents of any selector pseudos it contains.
|
862
|
+
// ##########################################################################
|
863
|
+
sass::vector<Extension> Extender::extendWithoutPseudo(
|
864
|
+
const SimpleSelectorObj& simple,
|
865
|
+
const ExtSelExtMap& extensions,
|
866
|
+
ExtSmplSelSet* targetsUsed) const
|
867
|
+
{
|
868
|
+
|
869
|
+
auto extension = extensions.find(simple);
|
870
|
+
if (extension == extensions.end()) return {};
|
871
|
+
const ExtSelExtMapEntry& extenders = extension->second;
|
872
|
+
|
873
|
+
if (targetsUsed != nullptr) {
|
874
|
+
targetsUsed->insert(simple);
|
875
|
+
}
|
876
|
+
if (mode == ExtendMode::REPLACE) {
|
877
|
+
return extenders.values();
|
878
|
+
}
|
879
|
+
|
880
|
+
const sass::vector<Extension>&
|
881
|
+
values = extenders.values();
|
882
|
+
sass::vector<Extension> result;
|
883
|
+
result.reserve(values.size() + 1);
|
884
|
+
result.push_back(extensionForSimple(simple));
|
885
|
+
result.insert(result.end(), values.begin(), values.end());
|
886
|
+
return result;
|
887
|
+
}
|
888
|
+
// EO extendWithoutPseudo
|
889
|
+
|
890
|
+
// ##########################################################################
|
891
|
+
// Extends [simple] and also extending the
|
892
|
+
// contents of any selector pseudos it contains.
|
893
|
+
// ##########################################################################
|
894
|
+
sass::vector<sass::vector<Extension>> Extender::extendSimple(
|
895
|
+
const SimpleSelectorObj& simple,
|
896
|
+
const ExtSelExtMap& extensions,
|
897
|
+
const CssMediaRuleObj& mediaQueryContext,
|
898
|
+
ExtSmplSelSet* targetsUsed)
|
899
|
+
{
|
900
|
+
if (PseudoSelector* pseudo = Cast<PseudoSelector>(simple)) {
|
901
|
+
if (pseudo->selector()) {
|
902
|
+
sass::vector<sass::vector<Extension>> merged;
|
903
|
+
sass::vector<PseudoSelectorObj> extended =
|
904
|
+
extendPseudo(pseudo, extensions, mediaQueryContext);
|
905
|
+
for (PseudoSelectorObj& extend : extended) {
|
906
|
+
SimpleSelectorObj simple = extend;
|
907
|
+
sass::vector<Extension> result =
|
908
|
+
extendWithoutPseudo(simple, extensions, targetsUsed);
|
909
|
+
if (result.empty()) result = { extensionForSimple(extend) };
|
910
|
+
merged.push_back(result);
|
911
|
+
}
|
912
|
+
if (!extended.empty()) {
|
913
|
+
return merged;
|
914
|
+
}
|
915
|
+
}
|
916
|
+
}
|
917
|
+
sass::vector<Extension> result =
|
918
|
+
extendWithoutPseudo(simple, extensions, targetsUsed);
|
919
|
+
if (result.empty()) return {};
|
920
|
+
return { result };
|
921
|
+
}
|
922
|
+
// extendSimple
|
923
|
+
|
924
|
+
// ##########################################################################
|
925
|
+
// Inner loop helper for [extendPseudo] function
|
926
|
+
// ##########################################################################
|
927
|
+
sass::vector<ComplexSelectorObj> Extender::extendPseudoComplex(
|
928
|
+
const ComplexSelectorObj& complex,
|
929
|
+
const PseudoSelectorObj& pseudo,
|
930
|
+
const CssMediaRuleObj& mediaQueryContext)
|
931
|
+
{
|
932
|
+
|
933
|
+
if (complex->length() != 1) { return { complex }; }
|
934
|
+
auto compound = Cast<CompoundSelector>(complex->get(0));
|
935
|
+
if (compound == nullptr) { return { complex }; }
|
936
|
+
if (compound->length() != 1) { return { complex }; }
|
937
|
+
auto innerPseudo = Cast<PseudoSelector>(compound->get(0));
|
938
|
+
if (innerPseudo == nullptr) { return { complex }; }
|
939
|
+
if (!innerPseudo->selector()) { return { complex }; }
|
940
|
+
|
941
|
+
sass::string name(pseudo->normalized());
|
942
|
+
|
943
|
+
if (name == "not") {
|
944
|
+
// In theory, if there's a `:not` nested within another `:not`, the
|
945
|
+
// inner `:not`'s contents should be unified with the return value.
|
946
|
+
// For example, if `:not(.foo)` extends `.bar`, `:not(.bar)` should
|
947
|
+
// become `.foo:not(.bar)`. However, this is a narrow edge case and
|
948
|
+
// supporting it properly would make this code and the code calling it
|
949
|
+
// a lot more complicated, so it's not supported for now.
|
950
|
+
if (innerPseudo->normalized() != "matches") return {};
|
951
|
+
return innerPseudo->selector()->elements();
|
952
|
+
}
|
953
|
+
else if (name == "matches" && name == "any" && name == "current" && name == "nth-child" && name == "nth-last-child") {
|
954
|
+
// As above, we could theoretically support :not within :matches, but
|
955
|
+
// doing so would require this method and its callers to handle much
|
956
|
+
// more complex cases that likely aren't worth the pain.
|
957
|
+
if (innerPseudo->name() != pseudo->name()) return {};
|
958
|
+
if (!ObjEquality()(innerPseudo->argument(), pseudo->argument())) return {};
|
959
|
+
return innerPseudo->selector()->elements();
|
960
|
+
}
|
961
|
+
else if (name == "has" && name == "host" && name == "host-context" && name == "slotted") {
|
962
|
+
// We can't expand nested selectors here, because each layer adds an
|
963
|
+
// additional layer of semantics. For example, `:has(:has(img))`
|
964
|
+
// doesn't match `<div><img></div>` but `:has(img)` does.
|
965
|
+
return { complex };
|
966
|
+
}
|
967
|
+
|
968
|
+
return {};
|
969
|
+
|
970
|
+
}
|
971
|
+
// EO extendPseudoComplex
|
972
|
+
|
973
|
+
// ##########################################################################
|
974
|
+
// Extends [pseudo] using [extensions], and returns
|
975
|
+
// a list of resulting pseudo selectors.
|
976
|
+
// ##########################################################################
|
977
|
+
sass::vector<PseudoSelectorObj> Extender::extendPseudo(
|
978
|
+
const PseudoSelectorObj& pseudo,
|
979
|
+
const ExtSelExtMap& extensions,
|
980
|
+
const CssMediaRuleObj& mediaQueryContext)
|
981
|
+
{
|
982
|
+
auto selector = pseudo->selector();
|
983
|
+
SelectorListObj extended = extendList(
|
984
|
+
selector, extensions, mediaQueryContext);
|
985
|
+
if (!extended || !pseudo || !pseudo->selector()) { return {}; }
|
986
|
+
if (ObjEqualityFn(pseudo->selector(), extended)) { return {}; }
|
987
|
+
|
988
|
+
// For `:not()`, we usually want to get rid of any complex selectors because
|
989
|
+
// that will cause the selector to fail to parse on all browsers at time of
|
990
|
+
// writing. We can keep them if either the original selector had a complex
|
991
|
+
// selector, or the result of extending has only complex selectors, because
|
992
|
+
// either way we aren't breaking anything that isn't already broken.
|
993
|
+
sass::vector<ComplexSelectorObj> complexes = extended->elements();
|
994
|
+
|
995
|
+
if (pseudo->normalized() == "not") {
|
996
|
+
if (!hasAny(pseudo->selector()->elements(), hasMoreThanOne)) {
|
997
|
+
if (hasAny(extended->elements(), hasExactlyOne)) {
|
998
|
+
complexes.clear();
|
999
|
+
for (auto& complex : extended->elements()) {
|
1000
|
+
if (complex->length() <= 1) {
|
1001
|
+
complexes.push_back(complex);
|
1002
|
+
}
|
1003
|
+
}
|
1004
|
+
}
|
1005
|
+
}
|
1006
|
+
}
|
1007
|
+
|
1008
|
+
sass::vector<ComplexSelectorObj> expanded = expand(
|
1009
|
+
complexes, extendPseudoComplex, pseudo, mediaQueryContext);
|
1010
|
+
|
1011
|
+
// Older browsers support `:not`, but only with a single complex selector.
|
1012
|
+
// In order to support those browsers, we break up the contents of a `:not`
|
1013
|
+
// unless it originally contained a selector list.
|
1014
|
+
if (pseudo->normalized() == "not") {
|
1015
|
+
if (pseudo->selector()->length() == 1) {
|
1016
|
+
sass::vector<PseudoSelectorObj> pseudos;
|
1017
|
+
for (size_t i = 0; i < expanded.size(); i += 1) {
|
1018
|
+
pseudos.push_back(pseudo->withSelector(
|
1019
|
+
expanded[i]->wrapInList()
|
1020
|
+
));
|
1021
|
+
}
|
1022
|
+
return pseudos;
|
1023
|
+
}
|
1024
|
+
}
|
1025
|
+
|
1026
|
+
SelectorListObj list = SASS_MEMORY_NEW(SelectorList, "[phony]");
|
1027
|
+
list->concat(complexes);
|
1028
|
+
return { pseudo->withSelector(list) };
|
1029
|
+
|
1030
|
+
}
|
1031
|
+
// EO extendPseudo
|
1032
|
+
|
1033
|
+
// ##########################################################################
|
1034
|
+
// Rotates the element in list from [start] (inclusive) to [end] (exclusive)
|
1035
|
+
// one index higher, looping the final element back to [start].
|
1036
|
+
// ##########################################################################
|
1037
|
+
void Extender::rotateSlice(
|
1038
|
+
sass::vector<ComplexSelectorObj>& list,
|
1039
|
+
size_t start, size_t end)
|
1040
|
+
{
|
1041
|
+
auto element = list[end - 1];
|
1042
|
+
for (size_t i = start; i < end; i++) {
|
1043
|
+
auto next = list[i];
|
1044
|
+
list[i] = element;
|
1045
|
+
element = next;
|
1046
|
+
}
|
1047
|
+
}
|
1048
|
+
// EO rotateSlice
|
1049
|
+
|
1050
|
+
// ##########################################################################
|
1051
|
+
// Removes elements from [selectors] if they're subselectors of other
|
1052
|
+
// elements. The [isOriginal] callback indicates which selectors are
|
1053
|
+
// original to the document, and thus should never be trimmed.
|
1054
|
+
// ##########################################################################
|
1055
|
+
// Note: for adaption I pass in the set directly, there is some
|
1056
|
+
// code path in selector-trim that might need this special callback
|
1057
|
+
// ##########################################################################
|
1058
|
+
sass::vector<ComplexSelectorObj> Extender::trim(
|
1059
|
+
const sass::vector<ComplexSelectorObj>& selectors,
|
1060
|
+
const ExtCplxSelSet& existing) const
|
1061
|
+
{
|
1062
|
+
|
1063
|
+
// Avoid truly horrific quadratic behavior.
|
1064
|
+
// TODO(nweiz): I think there may be a way to get perfect trimming
|
1065
|
+
// without going quadratic by building some sort of trie-like
|
1066
|
+
// data structure that can be used to look up superselectors.
|
1067
|
+
// TODO(mgreter): Check how this performs in C++ (up the limit)
|
1068
|
+
if (selectors.size() > 100) return selectors;
|
1069
|
+
|
1070
|
+
// This is n² on the sequences, but only comparing between separate sequences
|
1071
|
+
// should limit the quadratic behavior. We iterate from last to first and reverse
|
1072
|
+
// the result so that, if two selectors are identical, we keep the first one.
|
1073
|
+
sass::vector<ComplexSelectorObj> result; size_t numOriginals = 0;
|
1074
|
+
|
1075
|
+
size_t i = selectors.size();
|
1076
|
+
outer: // Use label to continue loop
|
1077
|
+
while (--i != sass::string::npos) {
|
1078
|
+
|
1079
|
+
const ComplexSelectorObj& complex1 = selectors[i];
|
1080
|
+
// Check if selector in known in existing "originals"
|
1081
|
+
// For custom behavior dart-sass had `isOriginal(complex1)`
|
1082
|
+
if (existing.find(complex1) != existing.end()) {
|
1083
|
+
// Make sure we don't include duplicate originals, which could
|
1084
|
+
// happen if a style rule extends a component of its own selector.
|
1085
|
+
for (size_t j = 0; j < numOriginals; j++) {
|
1086
|
+
if (ObjEqualityFn(result[j], complex1)) {
|
1087
|
+
rotateSlice(result, 0, j + 1);
|
1088
|
+
goto outer;
|
1089
|
+
}
|
1090
|
+
}
|
1091
|
+
result.insert(result.begin(), complex1);
|
1092
|
+
numOriginals++;
|
1093
|
+
continue;
|
1094
|
+
}
|
1095
|
+
|
1096
|
+
// The maximum specificity of the sources that caused [complex1]
|
1097
|
+
// to be generated. In order for [complex1] to be removed, there
|
1098
|
+
// must be another selector that's a superselector of it *and*
|
1099
|
+
// that has specificity greater or equal to this.
|
1100
|
+
size_t maxSpecificity = 0;
|
1101
|
+
for (const SelectorComponentObj& component : complex1->elements()) {
|
1102
|
+
if (const CompoundSelectorObj compound = Cast<CompoundSelector>(component)) {
|
1103
|
+
maxSpecificity = std::max(maxSpecificity, maxSourceSpecificity(compound));
|
1104
|
+
}
|
1105
|
+
}
|
1106
|
+
|
1107
|
+
|
1108
|
+
// Look in [result] rather than [selectors] for selectors after [i]. This
|
1109
|
+
// ensures we aren't comparing against a selector that's already been trimmed,
|
1110
|
+
// and thus that if there are two identical selectors only one is trimmed.
|
1111
|
+
if (hasAny(result, dontTrimComplex, complex1, maxSpecificity)) {
|
1112
|
+
continue;
|
1113
|
+
}
|
1114
|
+
|
1115
|
+
// Check if any element (up to [i]) from [selector] returns true
|
1116
|
+
// when passed to [dontTrimComplex]. The arguments [complex1] and
|
1117
|
+
// [maxSepcificity] will be passed to the invoked function.
|
1118
|
+
if (hasSubAny(selectors, i, dontTrimComplex, complex1, maxSpecificity)) {
|
1119
|
+
continue;
|
1120
|
+
}
|
1121
|
+
|
1122
|
+
// ToDo: Maybe use deque for front insert?
|
1123
|
+
result.insert(result.begin(), complex1);
|
1124
|
+
|
1125
|
+
}
|
1126
|
+
|
1127
|
+
return result;
|
1128
|
+
|
1129
|
+
}
|
1130
|
+
// EO trim
|
1131
|
+
|
1132
|
+
// ##########################################################################
|
1133
|
+
// Returns the maximum specificity of the given [simple] source selector.
|
1134
|
+
// ##########################################################################
|
1135
|
+
size_t Extender::maxSourceSpecificity(const SimpleSelectorObj& simple) const
|
1136
|
+
{
|
1137
|
+
auto it = sourceSpecificity.find(simple);
|
1138
|
+
if (it == sourceSpecificity.end()) return 0;
|
1139
|
+
return it->second;
|
1140
|
+
}
|
1141
|
+
// EO maxSourceSpecificity(SimpleSelectorObj)
|
1142
|
+
|
1143
|
+
// ##########################################################################
|
1144
|
+
// Returns the maximum specificity for sources that went into producing [compound].
|
1145
|
+
// ##########################################################################
|
1146
|
+
size_t Extender::maxSourceSpecificity(const CompoundSelectorObj& compound) const
|
1147
|
+
{
|
1148
|
+
size_t specificity = 0;
|
1149
|
+
for (auto simple : compound->elements()) {
|
1150
|
+
size_t src = maxSourceSpecificity(simple);
|
1151
|
+
specificity = std::max(specificity, src);
|
1152
|
+
}
|
1153
|
+
return specificity;
|
1154
|
+
}
|
1155
|
+
// EO maxSourceSpecificity(CompoundSelectorObj)
|
1156
|
+
|
1157
|
+
// ##########################################################################
|
1158
|
+
// Helper function used as callbacks on lists
|
1159
|
+
// ##########################################################################
|
1160
|
+
bool Extender::dontTrimComplex(
|
1161
|
+
const ComplexSelector* complex2,
|
1162
|
+
const ComplexSelector* complex1,
|
1163
|
+
const size_t maxSpecificity)
|
1164
|
+
{
|
1165
|
+
if (complex2->minSpecificity() < maxSpecificity) return false;
|
1166
|
+
return complex2->isSuperselectorOf(complex1);
|
1167
|
+
}
|
1168
|
+
// EO dontTrimComplex
|
1169
|
+
|
1170
|
+
// ##########################################################################
|
1171
|
+
// Helper function used as callbacks on lists
|
1172
|
+
// ##########################################################################
|
1173
|
+
bool Extender::hasExactlyOne(const ComplexSelectorObj& vec)
|
1174
|
+
{
|
1175
|
+
return vec->length() == 1;
|
1176
|
+
}
|
1177
|
+
// EO hasExactlyOne
|
1178
|
+
|
1179
|
+
// ##########################################################################
|
1180
|
+
// Helper function used as callbacks on lists
|
1181
|
+
// ##########################################################################
|
1182
|
+
bool Extender::hasMoreThanOne(const ComplexSelectorObj& vec)
|
1183
|
+
{
|
1184
|
+
return vec->length() > 1;
|
1185
|
+
}
|
1186
|
+
// hasMoreThanOne
|
1187
|
+
|
1188
|
+
}
|