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

source code of gtk/subprojects/libsass/src/fn_selectors.cpp