sassc4 2.4.0

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