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