sassc 2.2.1 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }