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.
Files changed (147) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -0
  3. data/CHANGELOG.md +18 -0
  4. data/Rakefile +1 -3
  5. data/ext/extconf.rb +13 -5
  6. data/ext/libsass/VERSION +1 -1
  7. data/ext/libsass/include/sass/base.h +2 -1
  8. data/ext/libsass/include/sass/context.h +4 -0
  9. data/ext/libsass/src/MurmurHash2.hpp +91 -0
  10. data/ext/libsass/src/ast.cpp +158 -168
  11. data/ext/libsass/src/ast.hpp +389 -230
  12. data/ext/libsass/src/ast_def_macros.hpp +18 -10
  13. data/ext/libsass/src/ast_fwd_decl.cpp +4 -3
  14. data/ext/libsass/src/ast_fwd_decl.hpp +98 -165
  15. data/ext/libsass/src/ast_helpers.hpp +292 -0
  16. data/ext/libsass/src/ast_sel_cmp.cpp +219 -732
  17. data/ext/libsass/src/ast_sel_super.cpp +539 -0
  18. data/ext/libsass/src/ast_sel_unify.cpp +207 -212
  19. data/ext/libsass/src/ast_sel_weave.cpp +616 -0
  20. data/ext/libsass/src/ast_selectors.cpp +594 -1026
  21. data/ext/libsass/src/ast_selectors.hpp +339 -385
  22. data/ext/libsass/src/ast_supports.cpp +36 -52
  23. data/ext/libsass/src/ast_supports.hpp +29 -29
  24. data/ext/libsass/src/ast_values.cpp +271 -84
  25. data/ext/libsass/src/ast_values.hpp +116 -107
  26. data/ext/libsass/src/backtrace.cpp +9 -9
  27. data/ext/libsass/src/backtrace.hpp +5 -5
  28. data/ext/libsass/src/base64vlq.cpp +2 -2
  29. data/ext/libsass/src/base64vlq.hpp +1 -1
  30. data/ext/libsass/src/bind.cpp +18 -18
  31. data/ext/libsass/src/bind.hpp +1 -1
  32. data/ext/libsass/src/c2ast.cpp +3 -3
  33. data/ext/libsass/src/c2ast.hpp +1 -1
  34. data/ext/libsass/src/cencode.c +4 -6
  35. data/ext/libsass/src/check_nesting.cpp +40 -41
  36. data/ext/libsass/src/check_nesting.hpp +6 -2
  37. data/ext/libsass/src/color_maps.cpp +14 -13
  38. data/ext/libsass/src/color_maps.hpp +1 -9
  39. data/ext/libsass/src/constants.cpp +5 -0
  40. data/ext/libsass/src/constants.hpp +6 -0
  41. data/ext/libsass/src/context.cpp +92 -119
  42. data/ext/libsass/src/context.hpp +41 -53
  43. data/ext/libsass/src/cssize.cpp +66 -149
  44. data/ext/libsass/src/cssize.hpp +17 -23
  45. data/ext/libsass/src/dart_helpers.hpp +199 -0
  46. data/ext/libsass/src/debugger.hpp +451 -295
  47. data/ext/libsass/src/emitter.cpp +15 -16
  48. data/ext/libsass/src/emitter.hpp +10 -12
  49. data/ext/libsass/src/environment.cpp +27 -27
  50. data/ext/libsass/src/environment.hpp +29 -24
  51. data/ext/libsass/src/error_handling.cpp +62 -41
  52. data/ext/libsass/src/error_handling.hpp +61 -51
  53. data/ext/libsass/src/eval.cpp +167 -281
  54. data/ext/libsass/src/eval.hpp +27 -29
  55. data/ext/libsass/src/eval_selectors.cpp +75 -0
  56. data/ext/libsass/src/expand.cpp +275 -222
  57. data/ext/libsass/src/expand.hpp +36 -16
  58. data/ext/libsass/src/extender.cpp +1188 -0
  59. data/ext/libsass/src/extender.hpp +399 -0
  60. data/ext/libsass/src/extension.cpp +43 -0
  61. data/ext/libsass/src/extension.hpp +89 -0
  62. data/ext/libsass/src/file.cpp +81 -72
  63. data/ext/libsass/src/file.hpp +28 -37
  64. data/ext/libsass/src/fn_colors.cpp +20 -18
  65. data/ext/libsass/src/fn_lists.cpp +30 -29
  66. data/ext/libsass/src/fn_maps.cpp +3 -3
  67. data/ext/libsass/src/fn_miscs.cpp +34 -46
  68. data/ext/libsass/src/fn_numbers.cpp +20 -13
  69. data/ext/libsass/src/fn_selectors.cpp +98 -128
  70. data/ext/libsass/src/fn_strings.cpp +47 -33
  71. data/ext/libsass/src/fn_utils.cpp +31 -29
  72. data/ext/libsass/src/fn_utils.hpp +17 -11
  73. data/ext/libsass/src/inspect.cpp +186 -148
  74. data/ext/libsass/src/inspect.hpp +31 -29
  75. data/ext/libsass/src/lexer.cpp +20 -82
  76. data/ext/libsass/src/lexer.hpp +5 -16
  77. data/ext/libsass/src/listize.cpp +23 -37
  78. data/ext/libsass/src/listize.hpp +8 -9
  79. data/ext/libsass/src/mapping.hpp +1 -0
  80. data/ext/libsass/src/memory/allocator.cpp +48 -0
  81. data/ext/libsass/src/memory/allocator.hpp +138 -0
  82. data/ext/libsass/src/memory/config.hpp +20 -0
  83. data/ext/libsass/src/memory/memory_pool.hpp +186 -0
  84. data/ext/libsass/src/memory/{SharedPtr.cpp → shared_ptr.cpp} +2 -2
  85. data/ext/libsass/src/memory/{SharedPtr.hpp → shared_ptr.hpp} +55 -9
  86. data/ext/libsass/src/memory.hpp +12 -0
  87. data/ext/libsass/src/operation.hpp +71 -61
  88. data/ext/libsass/src/operators.cpp +19 -18
  89. data/ext/libsass/src/operators.hpp +11 -11
  90. data/ext/libsass/src/ordered_map.hpp +112 -0
  91. data/ext/libsass/src/output.cpp +45 -64
  92. data/ext/libsass/src/output.hpp +6 -6
  93. data/ext/libsass/src/parser.cpp +512 -700
  94. data/ext/libsass/src/parser.hpp +89 -97
  95. data/ext/libsass/src/parser_selectors.cpp +189 -0
  96. data/ext/libsass/src/permutate.hpp +164 -0
  97. data/ext/libsass/src/plugins.cpp +7 -7
  98. data/ext/libsass/src/plugins.hpp +8 -8
  99. data/ext/libsass/src/position.cpp +7 -26
  100. data/ext/libsass/src/position.hpp +44 -21
  101. data/ext/libsass/src/prelexer.cpp +6 -6
  102. data/ext/libsass/src/remove_placeholders.cpp +55 -56
  103. data/ext/libsass/src/remove_placeholders.hpp +21 -18
  104. data/ext/libsass/src/sass.cpp +16 -15
  105. data/ext/libsass/src/sass.hpp +10 -5
  106. data/ext/libsass/src/sass2scss.cpp +4 -4
  107. data/ext/libsass/src/sass_context.cpp +91 -122
  108. data/ext/libsass/src/sass_context.hpp +2 -2
  109. data/ext/libsass/src/sass_functions.cpp +1 -1
  110. data/ext/libsass/src/sass_values.cpp +8 -11
  111. data/ext/libsass/src/settings.hpp +19 -0
  112. data/ext/libsass/src/source.cpp +69 -0
  113. data/ext/libsass/src/source.hpp +95 -0
  114. data/ext/libsass/src/source_data.hpp +32 -0
  115. data/ext/libsass/src/source_map.cpp +22 -18
  116. data/ext/libsass/src/source_map.hpp +12 -9
  117. data/ext/libsass/src/stylesheet.cpp +22 -0
  118. data/ext/libsass/src/stylesheet.hpp +57 -0
  119. data/ext/libsass/src/to_value.cpp +2 -2
  120. data/ext/libsass/src/to_value.hpp +1 -1
  121. data/ext/libsass/src/units.cpp +24 -22
  122. data/ext/libsass/src/units.hpp +8 -8
  123. data/ext/libsass/src/utf8_string.cpp +9 -10
  124. data/ext/libsass/src/utf8_string.hpp +7 -6
  125. data/ext/libsass/src/util.cpp +48 -50
  126. data/ext/libsass/src/util.hpp +20 -21
  127. data/ext/libsass/src/util_string.cpp +111 -61
  128. data/ext/libsass/src/util_string.hpp +62 -8
  129. data/ext/libsass/src/values.cpp +12 -12
  130. data/lib/sassc/engine.rb +5 -3
  131. data/lib/sassc/functions_handler.rb +8 -8
  132. data/lib/sassc/native.rb +4 -6
  133. data/lib/sassc/script.rb +4 -4
  134. data/lib/sassc/version.rb +1 -1
  135. data/test/functions_test.rb +18 -1
  136. data/test/native_test.rb +4 -4
  137. metadata +29 -15
  138. data/ext/libsass/src/extend.cpp +0 -2132
  139. data/ext/libsass/src/extend.hpp +0 -86
  140. data/ext/libsass/src/node.cpp +0 -322
  141. data/ext/libsass/src/node.hpp +0 -118
  142. data/ext/libsass/src/paths.hpp +0 -71
  143. data/ext/libsass/src/sass_util.cpp +0 -152
  144. data/ext/libsass/src/sass_util.hpp +0 -256
  145. data/ext/libsass/src/subset_map.cpp +0 -58
  146. data/ext/libsass/src/subset_map.hpp +0 -76
  147. 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
+ }