/src/libsass/src/fn_selectors.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | #include <numeric> |
2 | | |
3 | | #include "parser.hpp" |
4 | | #include "extender.hpp" |
5 | | #include "listize.hpp" |
6 | | #include "fn_utils.hpp" |
7 | | #include "fn_selectors.hpp" |
8 | | |
9 | | namespace Sass { |
10 | | |
11 | | namespace Functions { |
12 | | |
13 | | Signature selector_nest_sig = "selector-nest($selectors...)"; |
14 | | BUILT_IN(selector_nest) |
15 | 0 | { |
16 | 0 | List* arglist = ARG("$selectors", List); |
17 | | |
18 | | // Not enough parameters |
19 | 0 | if (arglist->length() == 0) { |
20 | 0 | error( |
21 | 0 | "$selectors: At least one selector must be passed for `selector-nest'", |
22 | 0 | pstate, traces); |
23 | 0 | } |
24 | | |
25 | | // Parse args into vector of selectors |
26 | 0 | SelectorStack parsedSelectors; |
27 | 0 | for (size_t i = 0, L = arglist->length(); i < L; ++i) { |
28 | 0 | ExpressionObj exp = Cast<Expression>(arglist->value_at_index(i)); |
29 | 0 | if (exp->concrete_type() == Expression::NULL_VAL) { |
30 | 0 | error( |
31 | 0 | "$selectors: null is not a valid selector: it must be a string,\n" |
32 | 0 | "a list of strings, or a list of lists of strings for 'selector-nest'", |
33 | 0 | pstate, traces); |
34 | 0 | } |
35 | 0 | if (String_Constant_Obj str = Cast<String_Constant>(exp)) { |
36 | 0 | str->quote_mark(0); |
37 | 0 | } |
38 | 0 | sass::string exp_src = exp->to_string(ctx.c_options); |
39 | 0 | ItplFile* source = SASS_MEMORY_NEW(ItplFile, exp_src.c_str(), exp->pstate()); |
40 | 0 | SelectorListObj sel = Parser::parse_selector(source, ctx, traces); |
41 | 0 | parsedSelectors.push_back(sel); |
42 | 0 | } |
43 | | |
44 | | // Nothing to do |
45 | 0 | if( parsedSelectors.empty() ) { |
46 | 0 | return SASS_MEMORY_NEW(Null, pstate); |
47 | 0 | } |
48 | | |
49 | | // Set the first element as the `result`, keep |
50 | | // appending to as we go down the parsedSelector vector. |
51 | 0 | SelectorStack::iterator itr = parsedSelectors.begin(); |
52 | 0 | SelectorListObj& result = *itr; |
53 | 0 | ++itr; |
54 | |
|
55 | 0 | for(;itr != parsedSelectors.end(); ++itr) { |
56 | 0 | SelectorListObj& child = *itr; |
57 | 0 | original_stack.push_back(result); |
58 | 0 | SelectorListObj rv = child->resolve_parent_refs(original_stack, traces); |
59 | 0 | result->elements(rv->elements()); |
60 | 0 | original_stack.pop_back(); |
61 | 0 | } |
62 | |
|
63 | 0 | return Cast<Value>(Listize::perform(result)); |
64 | 0 | } |
65 | | |
66 | | Signature selector_append_sig = "selector-append($selectors...)"; |
67 | | BUILT_IN(selector_append) |
68 | 0 | { |
69 | 0 | List* arglist = ARG("$selectors", List); |
70 | | |
71 | | // Not enough parameters |
72 | 0 | if (arglist->empty()) { |
73 | 0 | error( |
74 | 0 | "$selectors: At least one selector must be " |
75 | 0 | "passed for `selector-append'", |
76 | 0 | pstate, traces); |
77 | 0 | } |
78 | | |
79 | | // Parse args into vector of selectors |
80 | 0 | SelectorStack parsedSelectors; |
81 | 0 | parsedSelectors.push_back({}); |
82 | 0 | for (size_t i = 0, L = arglist->length(); i < L; ++i) { |
83 | 0 | Expression* exp = Cast<Expression>(arglist->value_at_index(i)); |
84 | 0 | if (exp->concrete_type() == Expression::NULL_VAL) { |
85 | 0 | error( |
86 | 0 | "$selectors: null is not a valid selector: it must be a string,\n" |
87 | 0 | "a list of strings, or a list of lists of strings for 'selector-append'", |
88 | 0 | pstate, traces); |
89 | 0 | } |
90 | 0 | if (String_Constant* str = Cast<String_Constant>(exp)) { |
91 | 0 | str->quote_mark(0); |
92 | 0 | } |
93 | 0 | sass::string exp_src = exp->to_string(); |
94 | 0 | ItplFile* source = SASS_MEMORY_NEW(ItplFile, exp_src.c_str(), exp->pstate()); |
95 | 0 | SelectorListObj sel = Parser::parse_selector(source, ctx, traces, true); |
96 | |
|
97 | 0 | for (auto& complex : sel->elements()) { |
98 | 0 | if (complex->empty()) { |
99 | 0 | complex->append(SASS_MEMORY_NEW(CompoundSelector, "[append]")); |
100 | 0 | } |
101 | 0 | if (CompoundSelector* comp = Cast<CompoundSelector>(complex->first())) { |
102 | 0 | comp->hasRealParent(true); |
103 | 0 | complex->chroots(true); |
104 | 0 | } |
105 | 0 | } |
106 | |
|
107 | 0 | if (parsedSelectors.size() > 1) { |
108 | |
|
109 | 0 | if (!sel->has_real_parent_ref()) { |
110 | 0 | auto parent = parsedSelectors.back(); |
111 | 0 | for (auto& complex : parent->elements()) { |
112 | 0 | if (CompoundSelector* comp = Cast<CompoundSelector>(complex->first())) { |
113 | 0 | comp->hasRealParent(false); |
114 | 0 | } |
115 | 0 | } |
116 | 0 | error("Can't append \"" + sel->to_string() + "\" to \"" + |
117 | 0 | parent->to_string() + "\" for `selector-append'", |
118 | 0 | pstate, traces); |
119 | 0 | } |
120 | | |
121 | | // Build the resolved stack from the left. It's cheaper to directly |
122 | | // calculate and update each resolved selcted from the left, than to |
123 | | // recursively calculate them from the right side, as we would need |
124 | | // to go through the whole stack depth to build the final results. |
125 | | // E.g. 'a', 'b', 'x, y' => 'a' => 'a b' => 'a b x, a b y' |
126 | | // vs 'a', 'b', 'x, y' => 'x' => 'b x' => 'a b x', 'y' ... |
127 | 0 | parsedSelectors.push_back(sel->resolve_parent_refs(parsedSelectors, traces, true)); |
128 | 0 | } |
129 | 0 | else { |
130 | 0 | parsedSelectors.push_back(sel); |
131 | 0 | } |
132 | 0 | } |
133 | | |
134 | | // Nothing to do |
135 | 0 | if( parsedSelectors.empty() ) { |
136 | 0 | return SASS_MEMORY_NEW(Null, pstate); |
137 | 0 | } |
138 | | |
139 | 0 | return Cast<Value>(Listize::perform(parsedSelectors.back())); |
140 | 0 | } |
141 | | |
142 | | Signature selector_unify_sig = "selector-unify($selector1, $selector2)"; |
143 | | BUILT_IN(selector_unify) |
144 | 0 | { |
145 | 0 | SelectorListObj selector1 = ARGSELS("$selector1"); |
146 | 0 | SelectorListObj selector2 = ARGSELS("$selector2"); |
147 | 0 | SelectorListObj result = selector1->unifyWith(selector2); |
148 | 0 | return Cast<Value>(Listize::perform(result)); |
149 | 0 | } |
150 | | |
151 | | Signature simple_selectors_sig = "simple-selectors($selector)"; |
152 | | BUILT_IN(simple_selectors) |
153 | 0 | { |
154 | 0 | CompoundSelectorObj sel = ARGSEL("$selector"); |
155 | |
|
156 | 0 | List* l = SASS_MEMORY_NEW(List, sel->pstate(), sel->length(), SASS_COMMA); |
157 | |
|
158 | 0 | for (size_t i = 0, L = sel->length(); i < L; ++i) { |
159 | 0 | const SimpleSelectorObj& ss = sel->get(i); |
160 | 0 | sass::string ss_string = ss->to_string() ; |
161 | 0 | l->append(SASS_MEMORY_NEW(String_Quoted, ss->pstate(), ss_string)); |
162 | 0 | } |
163 | |
|
164 | 0 | return l; |
165 | 0 | } |
166 | | |
167 | | Signature selector_extend_sig = "selector-extend($selector, $extendee, $extender)"; |
168 | | BUILT_IN(selector_extend) |
169 | 0 | { |
170 | 0 | SelectorListObj selector = ARGSELS("$selector"); |
171 | 0 | SelectorListObj target = ARGSELS("$extendee"); |
172 | 0 | SelectorListObj source = ARGSELS("$extender"); |
173 | 0 | SelectorListObj result = Extender::extend(selector, source, target, traces); |
174 | 0 | return Cast<Value>(Listize::perform(result)); |
175 | 0 | } |
176 | | |
177 | | Signature selector_replace_sig = "selector-replace($selector, $original, $replacement)"; |
178 | | BUILT_IN(selector_replace) |
179 | 0 | { |
180 | 0 | SelectorListObj selector = ARGSELS("$selector"); |
181 | 0 | SelectorListObj target = ARGSELS("$original"); |
182 | 0 | SelectorListObj source = ARGSELS("$replacement"); |
183 | 0 | SelectorListObj result = Extender::replace(selector, source, target, traces); |
184 | 0 | return Cast<Value>(Listize::perform(result)); |
185 | 0 | } |
186 | | |
187 | | Signature selector_parse_sig = "selector-parse($selector)"; |
188 | | BUILT_IN(selector_parse) |
189 | 0 | { |
190 | 0 | SelectorListObj selector = ARGSELS("$selector"); |
191 | 0 | return Cast<Value>(Listize::perform(selector)); |
192 | 0 | } |
193 | | |
194 | | Signature is_superselector_sig = "is-superselector($super, $sub)"; |
195 | | BUILT_IN(is_superselector) |
196 | 0 | { |
197 | 0 | SelectorListObj sel_sup = ARGSELS("$super"); |
198 | 0 | SelectorListObj sel_sub = ARGSELS("$sub"); |
199 | 0 | bool result = sel_sup->isSuperselectorOf(sel_sub); |
200 | 0 | return SASS_MEMORY_NEW(Boolean, pstate, result); |
201 | 0 | } |
202 | | |
203 | | } |
204 | | |
205 | | } |