Coverage Report

Created: 2023-11-26 06:41

/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
}