/src/libsass/src/ast_sel_weave.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // sass.hpp must go before all system headers to get the |
2 | | // __EXTENSIONS__ fix on Solaris. |
3 | | #include "sass.hpp" |
4 | | |
5 | | #include "ast.hpp" |
6 | | #include "permutate.hpp" |
7 | | #include "dart_helpers.hpp" |
8 | | |
9 | | namespace Sass { |
10 | | |
11 | | // ########################################################################## |
12 | | // Returns whether or not [compound] contains a `::root` selector. |
13 | | // ########################################################################## |
14 | | bool hasRoot(const CompoundSelector* compound) |
15 | 0 | { |
16 | | // Libsass does not yet know the root selector |
17 | 0 | return false; |
18 | 0 | } |
19 | | // EO hasRoot |
20 | | |
21 | | // ########################################################################## |
22 | | // Returns whether a [CompoundSelector] may contain only |
23 | | // one simple selector of the same type as [simple]. |
24 | | // ########################################################################## |
25 | | bool isUnique(const SimpleSelector* simple) |
26 | 0 | { |
27 | 0 | if (Cast<IDSelector>(simple)) return true; |
28 | 0 | if (const PseudoSelector * pseudo = Cast<PseudoSelector>(simple)) { |
29 | 0 | if (pseudo->is_pseudo_element()) return true; |
30 | 0 | } |
31 | 0 | return false; |
32 | 0 | } |
33 | | // EO isUnique |
34 | | |
35 | | // ########################################################################## |
36 | | // Returns whether [complex1] and [complex2] need to be unified to |
37 | | // produce a valid combined selector. This is necessary when both |
38 | | // selectors contain the same unique simple selector, such as an ID. |
39 | | // ########################################################################## |
40 | | bool mustUnify( |
41 | | const sass::vector<SelectorComponentObj>& complex1, |
42 | | const sass::vector<SelectorComponentObj>& complex2) |
43 | 0 | { |
44 | |
|
45 | 0 | sass::vector<const SimpleSelector*> uniqueSelectors1; |
46 | 0 | for (const SelectorComponent* component : complex1) { |
47 | 0 | if (const CompoundSelector * compound = component->getCompound()) { |
48 | 0 | for (const SimpleSelector* sel : compound->elements()) { |
49 | 0 | if (isUnique(sel)) { |
50 | 0 | uniqueSelectors1.push_back(sel); |
51 | 0 | } |
52 | 0 | } |
53 | 0 | } |
54 | 0 | } |
55 | 0 | if (uniqueSelectors1.empty()) return false; |
56 | | |
57 | | // ToDo: unsure if this is correct |
58 | 0 | for (const SelectorComponent* component : complex2) { |
59 | 0 | if (const CompoundSelector * compound = component->getCompound()) { |
60 | 0 | for (const SimpleSelector* sel : compound->elements()) { |
61 | 0 | if (isUnique(sel)) { |
62 | 0 | for (auto check : uniqueSelectors1) { |
63 | 0 | if (*check == *sel) return true; |
64 | 0 | } |
65 | 0 | } |
66 | 0 | } |
67 | 0 | } |
68 | 0 | } |
69 | | |
70 | 0 | return false; |
71 | |
|
72 | 0 | } |
73 | | // EO isUnique |
74 | | |
75 | | // ########################################################################## |
76 | | // Helper function used by `weaveParents` |
77 | | // ########################################################################## |
78 | | bool cmpGroups( |
79 | | const sass::vector<SelectorComponentObj>& group1, |
80 | | const sass::vector<SelectorComponentObj>& group2, |
81 | | sass::vector<SelectorComponentObj>& select) |
82 | 0 | { |
83 | |
|
84 | 0 | if (group1.size() == group2.size() && std::equal(group1.begin(), group1.end(), group2.begin(), PtrObjEqualityFn<SelectorComponent>)) { |
85 | 0 | select = group1; |
86 | 0 | return true; |
87 | 0 | } |
88 | | |
89 | 0 | if (!Cast<CompoundSelector>(group1.front())) { |
90 | 0 | select = {}; |
91 | 0 | return false; |
92 | 0 | } |
93 | 0 | if (!Cast<CompoundSelector>(group2.front())) { |
94 | 0 | select = {}; |
95 | 0 | return false; |
96 | 0 | } |
97 | | |
98 | 0 | if (complexIsParentSuperselector(group1, group2)) { |
99 | 0 | select = group2; |
100 | 0 | return true; |
101 | 0 | } |
102 | 0 | if (complexIsParentSuperselector(group2, group1)) { |
103 | 0 | select = group1; |
104 | 0 | return true; |
105 | 0 | } |
106 | | |
107 | 0 | if (!mustUnify(group1, group2)) { |
108 | 0 | select = {}; |
109 | 0 | return false; |
110 | 0 | } |
111 | | |
112 | 0 | sass::vector<sass::vector<SelectorComponentObj>> unified |
113 | 0 | = unifyComplex({ group1, group2 }); |
114 | 0 | if (unified.empty()) return false; |
115 | 0 | if (unified.size() > 1) return false; |
116 | 0 | select = unified.front(); |
117 | 0 | return true; |
118 | 0 | } |
119 | | // EO cmpGroups |
120 | | |
121 | | // ########################################################################## |
122 | | // Helper function used by `weaveParents` |
123 | | // ########################################################################## |
124 | | template <class T> |
125 | 0 | bool checkForEmptyChild(const T& item) { |
126 | 0 | return item.empty(); |
127 | 0 | } |
128 | | // EO checkForEmptyChild |
129 | | |
130 | | // ########################################################################## |
131 | | // Helper function used by `weaveParents` |
132 | | // ########################################################################## |
133 | | bool cmpChunkForEmptySequence( |
134 | | const sass::vector<sass::vector<SelectorComponentObj>>& seq, |
135 | | const sass::vector<SelectorComponentObj>& group) |
136 | 0 | { |
137 | 0 | return seq.empty(); |
138 | 0 | } |
139 | | // EO cmpChunkForEmptySequence |
140 | | |
141 | | // ########################################################################## |
142 | | // Helper function used by `weaveParents` |
143 | | // ########################################################################## |
144 | | bool cmpChunkForParentSuperselector( |
145 | | const sass::vector<sass::vector<SelectorComponentObj>>& seq, |
146 | | const sass::vector<SelectorComponentObj>& group) |
147 | 0 | { |
148 | 0 | return seq.empty() || complexIsParentSuperselector(seq.front(), group); |
149 | 0 | } |
150 | | // EO cmpChunkForParentSuperselector |
151 | | |
152 | | // ########################################################################## |
153 | | // Returns all orderings of initial subseqeuences of [queue1] and [queue2]. |
154 | | // The [done] callback is used to determine the extent of the initial |
155 | | // subsequences. It's called with each queue until it returns `true`. |
156 | | // Destructively removes the initial subsequences of [queue1] and [queue2]. |
157 | | // For example, given `(A B C | D E)` and `(1 2 | 3 4 5)` (with `|` denoting |
158 | | // the boundary of the initial subsequence), this would return `[(A B C 1 2), |
159 | | // (1 2 A B C)]`. The queues would then contain `(D E)` and `(3 4 5)`. |
160 | | // ########################################################################## |
161 | | template <class T> |
162 | | sass::vector<sass::vector<T>> getChunks( |
163 | | sass::vector<T>& queue1, sass::vector<T>& queue2, |
164 | | const T& group, bool(*done)(const sass::vector<T>&, const T&) |
165 | 0 | ) { |
166 | |
|
167 | 0 | sass::vector<T> chunk1; |
168 | 0 | while (!done(queue1, group)) { |
169 | 0 | chunk1.push_back(queue1.front()); |
170 | 0 | queue1.erase(queue1.begin()); |
171 | 0 | } |
172 | |
|
173 | 0 | sass::vector<T> chunk2; |
174 | 0 | while (!done(queue2, group)) { |
175 | 0 | chunk2.push_back(queue2.front()); |
176 | 0 | queue2.erase(queue2.begin()); |
177 | 0 | } |
178 | |
|
179 | 0 | if (chunk1.empty() && chunk2.empty()) return {}; |
180 | 0 | else if (chunk1.empty()) return { chunk2 }; |
181 | 0 | else if (chunk2.empty()) return { chunk1 }; |
182 | | |
183 | 0 | sass::vector<T> choice1(chunk1), choice2(chunk2); |
184 | 0 | std::move(std::begin(chunk2), std::end(chunk2), |
185 | 0 | std::inserter(choice1, std::end(choice1))); |
186 | 0 | std::move(std::begin(chunk1), std::end(chunk1), |
187 | 0 | std::inserter(choice2, std::end(choice2))); |
188 | 0 | return { choice1, choice2 }; |
189 | 0 | } |
190 | | // EO getChunks |
191 | | |
192 | | // ########################################################################## |
193 | | // If the first element of [queue] has a `::root` |
194 | | // selector, removes and returns that element. |
195 | | // ########################################################################## |
196 | 0 | CompoundSelectorObj getFirstIfRoot(sass::vector<SelectorComponentObj>& queue) { |
197 | 0 | if (queue.empty()) return {}; |
198 | 0 | SelectorComponent* first = queue.front(); |
199 | 0 | if (CompoundSelector* sel = Cast<CompoundSelector>(first)) { |
200 | 0 | if (!hasRoot(sel)) return {}; |
201 | 0 | queue.erase(queue.begin()); |
202 | 0 | return sel; |
203 | 0 | } |
204 | 0 | return {}; |
205 | 0 | } |
206 | | // EO getFirstIfRoot |
207 | | |
208 | | // ########################################################################## |
209 | | // Returns [complex], grouped into sub-lists such that no sub-list |
210 | | // contains two adjacent [ComplexSelector]s. For example, |
211 | | // `(A B > C D + E ~ > G)` is grouped into `[(A) (B > C) (D + E ~ > G)]`. |
212 | | // ########################################################################## |
213 | | sass::vector<sass::vector<SelectorComponentObj>> groupSelectors( |
214 | | const sass::vector<SelectorComponentObj>& components) |
215 | 0 | { |
216 | 0 | bool lastWasCompound = false; |
217 | 0 | sass::vector<SelectorComponentObj> group; |
218 | 0 | sass::vector<sass::vector<SelectorComponentObj>> groups; |
219 | 0 | for (size_t i = 0; i < components.size(); i += 1) { |
220 | 0 | if (CompoundSelector* compound = components[i]->getCompound()) { |
221 | 0 | if (lastWasCompound) { |
222 | 0 | groups.push_back(group); |
223 | 0 | group.clear(); |
224 | 0 | } |
225 | 0 | group.push_back(compound); |
226 | 0 | lastWasCompound = true; |
227 | 0 | } |
228 | 0 | else if (SelectorCombinator* combinator = components[i]->getCombinator()) { |
229 | 0 | group.push_back(combinator); |
230 | 0 | lastWasCompound = false; |
231 | 0 | } |
232 | 0 | } |
233 | 0 | if (!group.empty()) { |
234 | 0 | groups.push_back(group); |
235 | 0 | } |
236 | 0 | return groups; |
237 | 0 | } |
238 | | // EO groupSelectors |
239 | | |
240 | | // ########################################################################## |
241 | | // Extracts leading [Combinator]s from [components1] and [components2] |
242 | | // and merges them together into a single list of combinators. |
243 | | // If there are no combinators to be merged, returns an empty list. |
244 | | // If the combinators can't be merged, returns `null`. |
245 | | // ########################################################################## |
246 | | bool mergeInitialCombinators( |
247 | | sass::vector<SelectorComponentObj>& components1, |
248 | | sass::vector<SelectorComponentObj>& components2, |
249 | | sass::vector<SelectorComponentObj>& result) |
250 | 0 | { |
251 | |
|
252 | 0 | sass::vector<SelectorComponentObj> combinators1; |
253 | 0 | while (!components1.empty() && Cast<SelectorCombinator>(components1.front())) { |
254 | 0 | SelectorCombinatorObj front = Cast<SelectorCombinator>(components1.front()); |
255 | 0 | components1.erase(components1.begin()); |
256 | 0 | combinators1.push_back(front); |
257 | 0 | } |
258 | |
|
259 | 0 | sass::vector<SelectorComponentObj> combinators2; |
260 | 0 | while (!components2.empty() && Cast<SelectorCombinator>(components2.front())) { |
261 | 0 | SelectorCombinatorObj front = Cast<SelectorCombinator>(components2.front()); |
262 | 0 | components2.erase(components2.begin()); |
263 | 0 | combinators2.push_back(front); |
264 | 0 | } |
265 | | |
266 | | // If neither sequence of combinators is a subsequence |
267 | | // of the other, they cannot be merged successfully. |
268 | 0 | sass::vector<SelectorComponentObj> LCS = lcs<SelectorComponentObj>(combinators1, combinators2); |
269 | |
|
270 | 0 | if (ListEquality(LCS, combinators1, PtrObjEqualityFn<SelectorComponent>)) { |
271 | 0 | result = combinators2; |
272 | 0 | return true; |
273 | 0 | } |
274 | 0 | if (ListEquality(LCS, combinators2, PtrObjEqualityFn<SelectorComponent>)) { |
275 | 0 | result = combinators1; |
276 | 0 | return true; |
277 | 0 | } |
278 | | |
279 | 0 | return false; |
280 | |
|
281 | 0 | } |
282 | | // EO mergeInitialCombinators |
283 | | |
284 | | // ########################################################################## |
285 | | // Extracts trailing [Combinator]s, and the selectors to which they apply, |
286 | | // from [components1] and [components2] and merges them together into a |
287 | | // single list. If there are no combinators to be merged, returns an |
288 | | // empty list. If the sequences can't be merged, returns `null`. |
289 | | // ########################################################################## |
290 | | bool mergeFinalCombinators( |
291 | | sass::vector<SelectorComponentObj>& components1, |
292 | | sass::vector<SelectorComponentObj>& components2, |
293 | | sass::vector<sass::vector<sass::vector<SelectorComponentObj>>>& result) |
294 | 0 | { |
295 | |
|
296 | 0 | if (components1.empty() || !Cast<SelectorCombinator>(components1.back())) { |
297 | 0 | if (components2.empty() || !Cast<SelectorCombinator>(components2.back())) { |
298 | 0 | return true; |
299 | 0 | } |
300 | 0 | } |
301 | | |
302 | 0 | sass::vector<SelectorComponentObj> combinators1; |
303 | 0 | while (!components1.empty() && Cast<SelectorCombinator>(components1.back())) { |
304 | 0 | SelectorCombinatorObj back = Cast<SelectorCombinator>(components1.back()); |
305 | 0 | components1.erase(components1.end() - 1); |
306 | 0 | combinators1.push_back(back); |
307 | 0 | } |
308 | |
|
309 | 0 | sass::vector<SelectorComponentObj> combinators2; |
310 | 0 | while (!components2.empty() && Cast<SelectorCombinator>(components2.back())) { |
311 | 0 | SelectorCombinatorObj back = Cast<SelectorCombinator>(components2.back()); |
312 | 0 | components2.erase(components2.end() - 1); |
313 | 0 | combinators2.push_back(back); |
314 | 0 | } |
315 | | |
316 | | // reverse now as we used push_back (faster than new alloc) |
317 | 0 | std::reverse(combinators1.begin(), combinators1.end()); |
318 | 0 | std::reverse(combinators2.begin(), combinators2.end()); |
319 | |
|
320 | 0 | if (combinators1.size() > 1 || combinators2.size() > 1) { |
321 | | // If there are multiple combinators, something hacky's going on. If one |
322 | | // is a supersequence of the other, use that, otherwise give up. |
323 | 0 | auto LCS = lcs<SelectorComponentObj>(combinators1, combinators2); |
324 | 0 | if (ListEquality(LCS, combinators1, PtrObjEqualityFn<SelectorComponent>)) { |
325 | 0 | result.push_back({ combinators2 }); |
326 | 0 | } |
327 | 0 | else if (ListEquality(LCS, combinators2, PtrObjEqualityFn<SelectorComponent>)) { |
328 | 0 | result.push_back({ combinators1 }); |
329 | 0 | } |
330 | 0 | else { |
331 | 0 | return false; |
332 | 0 | } |
333 | 0 | return true; |
334 | 0 | } |
335 | | |
336 | | // This code looks complicated, but it's actually just a bunch of special |
337 | | // cases for interactions between different combinators. |
338 | 0 | SelectorCombinatorObj combinator1, combinator2; |
339 | 0 | if (!combinators1.empty()) combinator1 = combinators1.back(); |
340 | 0 | if (!combinators2.empty()) combinator2 = combinators2.back(); |
341 | |
|
342 | 0 | if (!combinator1.isNull() && !combinator2.isNull()) { |
343 | |
|
344 | 0 | CompoundSelector* compound1 = Cast<CompoundSelector>(components1.back()); |
345 | 0 | CompoundSelector* compound2 = Cast<CompoundSelector>(components2.back()); |
346 | |
|
347 | 0 | components1.pop_back(); |
348 | 0 | components2.pop_back(); |
349 | |
|
350 | 0 | if (combinator1->isGeneralCombinator() && combinator2->isGeneralCombinator()) { |
351 | |
|
352 | 0 | if (compound1->isSuperselectorOf(compound2)) { |
353 | 0 | result.push_back({ { compound2, combinator2 } }); |
354 | 0 | } |
355 | 0 | else if (compound2->isSuperselectorOf(compound1)) { |
356 | 0 | result.push_back({ { compound1, combinator1 } }); |
357 | 0 | } |
358 | 0 | else { |
359 | 0 | sass::vector<sass::vector<SelectorComponentObj>> choices; |
360 | 0 | choices.push_back({ compound1, combinator1, compound2, combinator2 }); |
361 | 0 | choices.push_back({ compound2, combinator2, compound1, combinator1 }); |
362 | 0 | if (CompoundSelector* unified = compound1->unifyWith(compound2)) { |
363 | 0 | choices.push_back({ unified, combinator1 }); |
364 | 0 | } |
365 | 0 | result.push_back(choices); |
366 | 0 | } |
367 | 0 | } |
368 | 0 | else if ((combinator1->isGeneralCombinator() && combinator2->isAdjacentCombinator()) || |
369 | 0 | (combinator1->isAdjacentCombinator() && combinator2->isGeneralCombinator())) { |
370 | |
|
371 | 0 | CompoundSelector* followingSiblingSelector = combinator1->isGeneralCombinator() ? compound1 : compound2; |
372 | 0 | CompoundSelector* nextSiblingSelector = combinator1->isGeneralCombinator() ? compound2 : compound1; |
373 | 0 | SelectorCombinator* followingSiblingCombinator = combinator1->isGeneralCombinator() ? combinator1 : combinator2; |
374 | 0 | SelectorCombinator* nextSiblingCombinator = combinator1->isGeneralCombinator() ? combinator2 : combinator1; |
375 | |
|
376 | 0 | if (followingSiblingSelector->isSuperselectorOf(nextSiblingSelector)) { |
377 | 0 | result.push_back({ { nextSiblingSelector, nextSiblingCombinator } }); |
378 | 0 | } |
379 | 0 | else { |
380 | 0 | CompoundSelectorObj unified = compound1->unifyWith(compound2); |
381 | 0 | sass::vector<sass::vector<SelectorComponentObj>> items; |
382 | | |
383 | 0 | if (!unified.isNull()) { |
384 | 0 | items.push_back({ |
385 | 0 | unified, nextSiblingCombinator |
386 | 0 | }); |
387 | 0 | } |
388 | |
|
389 | 0 | items.insert(items.begin(), { |
390 | 0 | followingSiblingSelector, |
391 | 0 | followingSiblingCombinator, |
392 | 0 | nextSiblingSelector, |
393 | 0 | nextSiblingCombinator, |
394 | 0 | }); |
395 | |
|
396 | 0 | result.push_back(items); |
397 | 0 | } |
398 | |
|
399 | 0 | } |
400 | 0 | else if (combinator1->isChildCombinator() && (combinator2->isAdjacentCombinator() || combinator2->isGeneralCombinator())) { |
401 | 0 | result.push_back({ { compound2, combinator2 } }); |
402 | 0 | components1.push_back(compound1); |
403 | 0 | components1.push_back(combinator1); |
404 | 0 | } |
405 | 0 | else if (combinator2->isChildCombinator() && (combinator1->isAdjacentCombinator() || combinator1->isGeneralCombinator())) { |
406 | 0 | result.push_back({ { compound1, combinator1 } }); |
407 | 0 | components2.push_back(compound2); |
408 | 0 | components2.push_back(combinator2); |
409 | 0 | } |
410 | 0 | else if (*combinator1 == *combinator2) { |
411 | 0 | CompoundSelectorObj unified = compound1->unifyWith(compound2); |
412 | 0 | if (unified.isNull()) return false; |
413 | 0 | result.push_back({ { unified, combinator1 } }); |
414 | 0 | } |
415 | 0 | else { |
416 | 0 | return false; |
417 | 0 | } |
418 | | |
419 | 0 | return mergeFinalCombinators(components1, components2, result); |
420 | |
|
421 | 0 | } |
422 | 0 | else if (!combinator1.isNull()) { |
423 | |
|
424 | 0 | if (combinator1->isChildCombinator() && !components2.empty()) { |
425 | 0 | const CompoundSelector* back1 = Cast<CompoundSelector>(components1.back()); |
426 | 0 | const CompoundSelector* back2 = Cast<CompoundSelector>(components2.back()); |
427 | 0 | if (back1 && back2 && back2->isSuperselectorOf(back1)) { |
428 | 0 | components2.pop_back(); |
429 | 0 | } |
430 | 0 | } |
431 | |
|
432 | 0 | result.push_back({ { components1.back(), combinator1 } }); |
433 | |
|
434 | 0 | components1.pop_back(); |
435 | |
|
436 | 0 | return mergeFinalCombinators(components1, components2, result); |
437 | |
|
438 | 0 | } |
439 | | |
440 | 0 | if (combinator2->isChildCombinator() && !components1.empty()) { |
441 | 0 | const CompoundSelector* back1 = Cast<CompoundSelector>(components1.back()); |
442 | 0 | const CompoundSelector* back2 = Cast<CompoundSelector>(components2.back()); |
443 | 0 | if (back1 && back2 && back1->isSuperselectorOf(back2)) { |
444 | 0 | components1.pop_back(); |
445 | 0 | } |
446 | 0 | } |
447 | |
|
448 | 0 | result.push_back({ { components2.back(), combinator2 } }); |
449 | |
|
450 | 0 | components2.pop_back(); |
451 | |
|
452 | 0 | return mergeFinalCombinators(components1, components2, result); |
453 | |
|
454 | 0 | } |
455 | | // EO mergeFinalCombinators |
456 | | |
457 | | // ########################################################################## |
458 | | // Expands "parenthesized selectors" in [complexes]. That is, if |
459 | | // we have `.A .B {@extend .C}` and `.D .C {...}`, this conceptually |
460 | | // expands into `.D .C, .D (.A .B)`, and this function translates |
461 | | // `.D (.A .B)` into `.D .A .B, .A .D .B`. For thoroughness, `.A.D .B` |
462 | | // would also be required, but including merged selectors results in |
463 | | // exponential output for very little gain. The selector `.D (.A .B)` |
464 | | // is represented as the list `[[.D], [.A, .B]]`. |
465 | | // ########################################################################## |
466 | | sass::vector<sass::vector<SelectorComponentObj>> weave( |
467 | 0 | const sass::vector<sass::vector<SelectorComponentObj>>& complexes) { |
468 | |
|
469 | 0 | sass::vector<sass::vector<SelectorComponentObj>> prefixes; |
470 | |
|
471 | 0 | prefixes.push_back(complexes.at(0)); |
472 | |
|
473 | 0 | for (size_t i = 1; i < complexes.size(); i += 1) { |
474 | |
|
475 | 0 | if (complexes[i].empty()) { |
476 | 0 | continue; |
477 | 0 | } |
478 | 0 | const sass::vector<SelectorComponentObj>& complex = complexes[i]; |
479 | 0 | SelectorComponent* target = complex.back(); |
480 | 0 | if (complex.size() == 1) { |
481 | 0 | for (auto& prefix : prefixes) { |
482 | 0 | prefix.push_back(target); |
483 | 0 | } |
484 | 0 | continue; |
485 | 0 | } |
486 | | |
487 | 0 | sass::vector<SelectorComponentObj> parents(complex); |
488 | |
|
489 | 0 | parents.pop_back(); |
490 | |
|
491 | 0 | sass::vector<sass::vector<SelectorComponentObj>> newPrefixes; |
492 | 0 | for (sass::vector<SelectorComponentObj> prefix : prefixes) { |
493 | 0 | sass::vector<sass::vector<SelectorComponentObj>> |
494 | 0 | parentPrefixes = weaveParents(prefix, parents); |
495 | 0 | if (parentPrefixes.empty()) continue; |
496 | 0 | for (auto& parentPrefix : parentPrefixes) { |
497 | 0 | parentPrefix.push_back(target); |
498 | 0 | newPrefixes.push_back(parentPrefix); |
499 | 0 | } |
500 | 0 | } |
501 | 0 | prefixes = newPrefixes; |
502 | |
|
503 | 0 | } |
504 | 0 | return prefixes; |
505 | |
|
506 | 0 | } |
507 | | // EO weave |
508 | | |
509 | | // ########################################################################## |
510 | | // Interweaves [parents1] and [parents2] as parents of the same target |
511 | | // selector. Returns all possible orderings of the selectors in the |
512 | | // inputs (including using unification) that maintain the relative |
513 | | // ordering of the input. For example, given `.foo .bar` and `.baz .bang`, |
514 | | // this would return `.foo .bar .baz .bang`, `.foo .bar.baz .bang`, |
515 | | // `.foo .baz .bar .bang`, `.foo .baz .bar.bang`, `.foo .baz .bang .bar`, |
516 | | // and so on until `.baz .bang .foo .bar`. Semantically, for selectors A |
517 | | // and B, this returns all selectors `AB_i` such that the union over all i |
518 | | // of elements matched by `AB_i X` is identical to the intersection of all |
519 | | // elements matched by `A X` and all elements matched by `B X`. Some `AB_i` |
520 | | // are elided to reduce the size of the output. |
521 | | // ########################################################################## |
522 | | sass::vector<sass::vector<SelectorComponentObj>> weaveParents( |
523 | | sass::vector<SelectorComponentObj> queue1, |
524 | | sass::vector<SelectorComponentObj> queue2) |
525 | 0 | { |
526 | |
|
527 | 0 | sass::vector<SelectorComponentObj> leads; |
528 | 0 | sass::vector<sass::vector<sass::vector<SelectorComponentObj>>> trails; |
529 | 0 | if (!mergeInitialCombinators(queue1, queue2, leads)) return {}; |
530 | 0 | if (!mergeFinalCombinators(queue1, queue2, trails)) return {}; |
531 | | // list comes out in reverse order for performance |
532 | 0 | std::reverse(trails.begin(), trails.end()); |
533 | | |
534 | | // Make sure there's at most one `:root` in the output. |
535 | | // Note: does not yet do anything in libsass (no root selector) |
536 | 0 | CompoundSelectorObj root1 = getFirstIfRoot(queue1); |
537 | 0 | CompoundSelectorObj root2 = getFirstIfRoot(queue2); |
538 | |
|
539 | 0 | if (!root1.isNull() && !root2.isNull()) { |
540 | 0 | CompoundSelectorObj root = root1->unifyWith(root2); |
541 | 0 | if (root.isNull()) return {}; // null |
542 | 0 | queue1.insert(queue1.begin(), root); |
543 | 0 | queue2.insert(queue2.begin(), root); |
544 | 0 | } |
545 | 0 | else if (!root1.isNull()) { |
546 | 0 | queue2.insert(queue2.begin(), root1); |
547 | 0 | } |
548 | 0 | else if (!root2.isNull()) { |
549 | 0 | queue1.insert(queue1.begin(), root2); |
550 | 0 | } |
551 | | |
552 | | // group into sub-lists so no sub-list contains two adjacent ComplexSelectors. |
553 | 0 | sass::vector<sass::vector<SelectorComponentObj>> groups1 = groupSelectors(queue1); |
554 | 0 | sass::vector<sass::vector<SelectorComponentObj>> groups2 = groupSelectors(queue2); |
555 | | |
556 | | // The main array to store our choices that will be permutated |
557 | 0 | sass::vector<sass::vector<sass::vector<SelectorComponentObj>>> choices; |
558 | | |
559 | | // append initial combinators |
560 | 0 | choices.push_back({ leads }); |
561 | |
|
562 | 0 | sass::vector<sass::vector<SelectorComponentObj>> LCS = |
563 | 0 | lcs<sass::vector<SelectorComponentObj>>(groups1, groups2, cmpGroups); |
564 | |
|
565 | 0 | for (auto group : LCS) { |
566 | | |
567 | | // Create junks from groups1 and groups2 |
568 | 0 | sass::vector<sass::vector<sass::vector<SelectorComponentObj>>> |
569 | 0 | chunks = getChunks<sass::vector<SelectorComponentObj>>( |
570 | 0 | groups1, groups2, group, cmpChunkForParentSuperselector); |
571 | | |
572 | | // Create expanded array by flattening chunks2 inner |
573 | 0 | sass::vector<sass::vector<SelectorComponentObj>> |
574 | 0 | expanded = flattenInner(chunks); |
575 | | |
576 | | // Prepare data structures |
577 | 0 | choices.push_back(expanded); |
578 | 0 | choices.push_back({ group }); |
579 | 0 | if (!groups1.empty()) { |
580 | 0 | groups1.erase(groups1.begin()); |
581 | 0 | } |
582 | 0 | if (!groups2.empty()) { |
583 | 0 | groups2.erase(groups2.begin()); |
584 | 0 | } |
585 | |
|
586 | 0 | } |
587 | | |
588 | | // Create junks from groups1 and groups2 |
589 | 0 | sass::vector<sass::vector<sass::vector<SelectorComponentObj>>> |
590 | 0 | chunks = getChunks<sass::vector<SelectorComponentObj>>( |
591 | 0 | groups1, groups2, {}, cmpChunkForEmptySequence); |
592 | | |
593 | | // Append chunks with inner arrays flattened |
594 | 0 | choices.emplace_back(flattenInner(chunks)); |
595 | | |
596 | | // append all trailing selectors to choices |
597 | 0 | std::move(std::begin(trails), std::end(trails), |
598 | 0 | std::inserter(choices, std::end(choices))); |
599 | | |
600 | | // move all non empty items to the front, then erase the trailing ones |
601 | 0 | choices.erase(std::remove_if(choices.begin(), choices.end(), checkForEmptyChild |
602 | 0 | <sass::vector<sass::vector<SelectorComponentObj>>>), choices.end()); |
603 | | |
604 | | // permutate all possible paths through selectors |
605 | 0 | sass::vector<sass::vector<SelectorComponentObj>> |
606 | 0 | results = flattenInner(permutate(choices)); |
607 | |
|
608 | 0 | return results; |
609 | |
|
610 | 0 | } |
611 | | // EO weaveParents |
612 | | |
613 | | // ########################################################################## |
614 | | // ########################################################################## |
615 | | |
616 | | } |