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 | |
7 | namespace Sass { |
8 | |
9 | // ########################################################################## |
10 | // Returns the contents of a [SelectorList] that matches only |
11 | // elements that are matched by both [complex1] and [complex2]. |
12 | // If no such list can be produced, returns `null`. |
13 | // ########################################################################## |
14 | // ToDo: fine-tune API to avoid unnecessary wrapper allocations |
15 | // ########################################################################## |
16 | sass::vector<sass::vector<SelectorComponentObj>> unifyComplex( |
17 | const sass::vector<sass::vector<SelectorComponentObj>>& complexes) |
18 | { |
19 | |
20 | SASS_ASSERT(!complexes.empty(), "Can't unify empty list" ); |
21 | if (complexes.size() == 1) return complexes; |
22 | |
23 | CompoundSelectorObj unifiedBase = SASS_MEMORY_NEW(CompoundSelector, SourceSpan("[phony]" )); |
24 | for (auto complex : complexes) { |
25 | SelectorComponentObj base = complex.back(); |
26 | if (CompoundSelector * comp = base->getCompound()) { |
27 | if (unifiedBase->empty()) { |
28 | unifiedBase->concat(v: comp); |
29 | } |
30 | else { |
31 | for (SimpleSelectorObj simple : comp->elements()) { |
32 | unifiedBase = simple->unifyWith(unifiedBase); |
33 | if (unifiedBase.isNull()) return {}; |
34 | } |
35 | } |
36 | } |
37 | else { |
38 | return {}; |
39 | } |
40 | } |
41 | |
42 | sass::vector<sass::vector<SelectorComponentObj>> complexesWithoutBases; |
43 | for (size_t i = 0; i < complexes.size(); i += 1) { |
44 | sass::vector<SelectorComponentObj> sel = complexes[i]; |
45 | sel.pop_back(); // remove last item (base) from the list |
46 | complexesWithoutBases.push_back(x: std::move(sel)); |
47 | } |
48 | |
49 | complexesWithoutBases.back().push_back(x: unifiedBase); |
50 | |
51 | return weave(complexes: complexesWithoutBases); |
52 | |
53 | } |
54 | // EO unifyComplex |
55 | |
56 | // ########################################################################## |
57 | // Returns a [CompoundSelector] that matches only elements |
58 | // that are matched by both [compound1] and [compound2]. |
59 | // If no such selector can be produced, returns `null`. |
60 | // ########################################################################## |
61 | CompoundSelector* CompoundSelector::unifyWith(CompoundSelector* rhs) |
62 | { |
63 | if (empty()) return rhs; |
64 | CompoundSelectorObj unified = SASS_MEMORY_COPY(rhs); |
65 | for (const SimpleSelectorObj& sel : elements()) { |
66 | unified = sel->unifyWith(unified); |
67 | if (unified.isNull()) break; |
68 | } |
69 | return unified.detach(); |
70 | } |
71 | // EO CompoundSelector::unifyWith(CompoundSelector*) |
72 | |
73 | // ########################################################################## |
74 | // Returns the compoments of a [CompoundSelector] that matches only elements |
75 | // matched by both this and [compound]. By default, this just returns a copy |
76 | // of [compound] with this selector added to the end, or returns the original |
77 | // array if this selector already exists in it. Returns `null` if unification |
78 | // is impossible—for example, if there are multiple ID selectors. |
79 | // ########################################################################## |
80 | // This is implemented in `selector/simple.dart` as `SimpleSelector::unify` |
81 | // ########################################################################## |
82 | CompoundSelector* SimpleSelector::unifyWith(CompoundSelector* rhs) |
83 | { |
84 | |
85 | if (rhs->length() == 1) { |
86 | if (rhs->get(i: 0)->is_universal()) { |
87 | CompoundSelector* this_compound = SASS_MEMORY_NEW(CompoundSelector, pstate()); |
88 | this_compound->append(SASS_MEMORY_COPY(this)); |
89 | CompoundSelector* unified = rhs->get(i: 0)->unifyWith(rhs: this_compound); |
90 | if (unified == nullptr || unified != this_compound) delete this_compound; |
91 | return unified; |
92 | } |
93 | } |
94 | for (const SimpleSelectorObj& sel : rhs->elements()) { |
95 | if (*this == *sel) { |
96 | return rhs; |
97 | } |
98 | } |
99 | |
100 | CompoundSelectorObj result = SASS_MEMORY_NEW(CompoundSelector, rhs->pstate()); |
101 | |
102 | bool addedThis = false; |
103 | for (auto simple : rhs->elements()) { |
104 | // Make sure pseudo selectors always come last. |
105 | if (!addedThis && simple->getPseudoSelector()) { |
106 | result->append(element: this); |
107 | addedThis = true; |
108 | } |
109 | result->append(element: simple); |
110 | } |
111 | |
112 | if (!addedThis) { |
113 | result->append(element: this); |
114 | } |
115 | return result.detach(); |
116 | |
117 | } |
118 | // EO SimpleSelector::unifyWith(CompoundSelector*) |
119 | |
120 | // ########################################################################## |
121 | // This is implemented in `selector/type.dart` as `PseudoSelector::unify` |
122 | // ########################################################################## |
123 | CompoundSelector* TypeSelector::unifyWith(CompoundSelector* rhs) |
124 | { |
125 | if (rhs->empty()) { |
126 | rhs->append(element: this); |
127 | return rhs; |
128 | } |
129 | TypeSelector* type = Cast<TypeSelector>(ptr: rhs->at(i: 0)); |
130 | if (type != nullptr) { |
131 | SimpleSelector* unified = unifyWith(type); |
132 | if (unified == nullptr) { |
133 | return nullptr; |
134 | } |
135 | rhs->elements()[0] = unified; |
136 | } |
137 | else if (!is_universal() || (has_ns_ && ns_ != "*" )) { |
138 | rhs->insert(position: rhs->begin(), val: this); |
139 | } |
140 | return rhs; |
141 | } |
142 | |
143 | // ########################################################################## |
144 | // This is implemented in `selector/id.dart` as `PseudoSelector::unify` |
145 | // ########################################################################## |
146 | CompoundSelector* IDSelector::unifyWith(CompoundSelector* rhs) |
147 | { |
148 | for (const SimpleSelector* sel : rhs->elements()) { |
149 | if (const IDSelector* id_sel = Cast<IDSelector>(ptr: sel)) { |
150 | if (id_sel->name() != name()) return nullptr; |
151 | } |
152 | } |
153 | return SimpleSelector::unifyWith(rhs); |
154 | } |
155 | |
156 | // ########################################################################## |
157 | // This is implemented in `selector/pseudo.dart` as `PseudoSelector::unify` |
158 | // ########################################################################## |
159 | CompoundSelector* PseudoSelector::unifyWith(CompoundSelector* compound) |
160 | { |
161 | |
162 | if (compound->length() == 1 && compound->first()->is_universal()) { |
163 | // std::cerr << "implement universal pseudo\n"; |
164 | } |
165 | |
166 | for (const SimpleSelectorObj& sel : compound->elements()) { |
167 | if (*this == *sel) { |
168 | return compound; |
169 | } |
170 | } |
171 | |
172 | CompoundSelectorObj result = SASS_MEMORY_NEW(CompoundSelector, compound->pstate()); |
173 | |
174 | bool addedThis = false; |
175 | for (auto simple : compound->elements()) { |
176 | // Make sure pseudo selectors always come last. |
177 | if (PseudoSelectorObj pseudo = simple->getPseudoSelector()) { |
178 | if (pseudo->isElement()) { |
179 | // A given compound selector may only contain one pseudo element. If |
180 | // [compound] has a different one than [this], unification fails. |
181 | if (isElement()) { |
182 | return {}; |
183 | } |
184 | // Otherwise, this is a pseudo selector and |
185 | // should come before pseduo elements. |
186 | result->append(element: this); |
187 | addedThis = true; |
188 | } |
189 | } |
190 | result->append(element: simple); |
191 | } |
192 | |
193 | if (!addedThis) { |
194 | result->append(element: this); |
195 | } |
196 | |
197 | return result.detach(); |
198 | |
199 | } |
200 | // EO PseudoSelector::unifyWith(CompoundSelector* |
201 | |
202 | // ########################################################################## |
203 | // This is implemented in `extend/functions.dart` as `unifyUniversalAndElement` |
204 | // Returns a [SimpleSelector] that matches only elements that are matched by |
205 | // both [selector1] and [selector2], which must both be either [UniversalSelector]s |
206 | // or [TypeSelector]s. If no such selector can be produced, returns `null`. |
207 | // Note: libsass handles universal selector directly within the type selector |
208 | // ########################################################################## |
209 | SimpleSelector* TypeSelector::unifyWith(const SimpleSelector* rhs) |
210 | { |
211 | bool rhs_ns = false; |
212 | if (!(is_ns_eq(r: *rhs) || rhs->is_universal_ns())) { |
213 | if (!is_universal_ns()) { |
214 | return nullptr; |
215 | } |
216 | rhs_ns = true; |
217 | } |
218 | bool rhs_name = false; |
219 | if (!(name_ == rhs->name() || rhs->is_universal())) { |
220 | if (!(is_universal())) { |
221 | return nullptr; |
222 | } |
223 | rhs_name = true; |
224 | } |
225 | if (rhs_ns) { |
226 | ns(ns__: rhs->ns()); |
227 | has_ns(has_ns__: rhs->has_ns()); |
228 | } |
229 | if (rhs_name) name(name__: rhs->name()); |
230 | return this; |
231 | } |
232 | // EO TypeSelector::unifyWith(const SimpleSelector*) |
233 | |
234 | // ########################################################################## |
235 | // Unify two complex selectors. Internally calls `unifyComplex` |
236 | // and then wraps the result in newly create ComplexSelectors. |
237 | // ########################################################################## |
238 | SelectorList* ComplexSelector::unifyWith(ComplexSelector* rhs) |
239 | { |
240 | SelectorListObj list = SASS_MEMORY_NEW(SelectorList, pstate()); |
241 | sass::vector<sass::vector<SelectorComponentObj>> rv = |
242 | unifyComplex(complexes: { elements(), rhs->elements() }); |
243 | for (sass::vector<SelectorComponentObj> items : rv) { |
244 | ComplexSelectorObj sel = SASS_MEMORY_NEW(ComplexSelector, pstate()); |
245 | sel->elements() = std::move(items); |
246 | list->append(element: sel); |
247 | } |
248 | return list.detach(); |
249 | } |
250 | // EO ComplexSelector::unifyWith(ComplexSelector*) |
251 | |
252 | // ########################################################################## |
253 | // only called from the sass function `selector-unify` |
254 | // ########################################################################## |
255 | SelectorList* SelectorList::unifyWith(SelectorList* rhs) |
256 | { |
257 | SelectorList* slist = SASS_MEMORY_NEW(SelectorList, pstate()); |
258 | // Unify all of children with RHS's children, |
259 | // storing the results in `unified_complex_selectors` |
260 | for (ComplexSelectorObj& seq1 : elements()) { |
261 | for (ComplexSelectorObj& seq2 : rhs->elements()) { |
262 | if (SelectorListObj unified = seq1->unifyWith(rhs: seq2)) { |
263 | std::move(first: unified->begin(), last: unified->end(), |
264 | result: std::inserter(x&: slist->elements(), i: slist->end())); |
265 | } |
266 | } |
267 | } |
268 | return slist; |
269 | } |
270 | // EO SelectorList::unifyWith(SelectorList*) |
271 | |
272 | // ########################################################################## |
273 | // ########################################################################## |
274 | |
275 | } |
276 | |