#include <numeric>
#include "parser.hpp"
#include "extender.hpp"
#include "listize.hpp"
#include "fn_utils.hpp"
#include "fn_selectors.hpp"
namespace Sass {
namespace Functions {
Signature selector_nest_sig = "selector-nest($selectors...)";
BUILT_IN(selector_nest)
{
List* arglist = ARG("$selectors", List);
// Not enough parameters
if (arglist->length() == 0) {
error(
"$selectors: At least one selector must be passed for `selector-nest'",
pstate, traces);
}
// Parse args into vector of selectors
SelectorStack parsedSelectors;
for (size_t i = 0, L = arglist->length(); i < L; ++i) {
ExpressionObj exp = Cast<Expression>(arglist->value_at_index(i));
if (exp->concrete_type() == Expression::NULL_VAL) {
error(
"$selectors: null is not a valid selector: it must be a string,\n"
"a list of strings, or a list of lists of strings for 'selector-nest'",
pstate, traces);
}
if (String_Constant_Obj str = Cast<String_Constant>(exp)) {
str->quote_mark(0);
}
sass::string exp_src = exp->to_string(ctx.c_options);
ItplFile* source = SASS_MEMORY_NEW(ItplFile, exp_src.c_str(), exp->pstate());
SelectorListObj sel = Parser::parse_selector(source, ctx, traces);
parsedSelectors.push_back(sel);
}
// Nothing to do
if( parsedSelectors.empty() ) {
return SASS_MEMORY_NEW(Null, pstate);
}
// Set the first element as the `result`, keep
// appending to as we go down the parsedSelector vector.
SelectorStack::iterator itr = parsedSelectors.begin();
SelectorListObj& result = *itr;
++itr;
for(;itr != parsedSelectors.end(); ++itr) {
SelectorListObj& child = *itr;
original_stack.push_back(result);
SelectorListObj rv = child->resolve_parent_refs(original_stack, traces);
result->elements(rv->elements());
original_stack.pop_back();
}
return Cast<Value>(Listize::perform(result));
}
Signature selector_append_sig = "selector-append($selectors...)";
BUILT_IN(selector_append)
{
List* arglist = ARG("$selectors", List);
// Not enough parameters
if (arglist->empty()) {
error(
"$selectors: At least one selector must be "
"passed for `selector-append'",
pstate, traces);
}
// Parse args into vector of selectors
SelectorStack parsedSelectors;
parsedSelectors.push_back({});
for (size_t i = 0, L = arglist->length(); i < L; ++i) {
Expression* exp = Cast<Expression>(arglist->value_at_index(i));
if (exp->concrete_type() == Expression::NULL_VAL) {
error(
"$selectors: null is not a valid selector: it must be a string,\n"
"a list of strings, or a list of lists of strings for 'selector-append'",
pstate, traces);
}
if (String_Constant* str = Cast<String_Constant>(exp)) {
str->quote_mark(0);
}
sass::string exp_src = exp->to_string();
ItplFile* source = SASS_MEMORY_NEW(ItplFile, exp_src.c_str(), exp->pstate());
SelectorListObj sel = Parser::parse_selector(source, ctx, traces, true);
for (auto& complex : sel->elements()) {
if (complex->empty()) {
complex->append(SASS_MEMORY_NEW(CompoundSelector, "[append]"));
}
if (CompoundSelector* comp = Cast<CompoundSelector>(complex->first())) {
comp->hasRealParent(true);
complex->chroots(true);
}
}
if (parsedSelectors.size() > 1) {
if (!sel->has_real_parent_ref()) {
auto parent = parsedSelectors.back();
for (auto& complex : parent->elements()) {
if (CompoundSelector* comp = Cast<CompoundSelector>(complex->first())) {
comp->hasRealParent(false);
}
}
error("Can't append \"" + sel->to_string() + "\" to \"" +
parent->to_string() + "\" for `selector-append'",
pstate, traces);
}
// Build the resolved stack from the left. It's cheaper to directly
// calculate and update each resolved selcted from the left, than to
// recursively calculate them from the right side, as we would need
// to go through the whole stack depth to build the final results.
// E.g. 'a', 'b', 'x, y' => 'a' => 'a b' => 'a b x, a b y'
// vs 'a', 'b', 'x, y' => 'x' => 'b x' => 'a b x', 'y' ...
parsedSelectors.push_back(sel->resolve_parent_refs(parsedSelectors, traces, true));
}
else {
parsedSelectors.push_back(sel);
}
}
// Nothing to do
if( parsedSelectors.empty() ) {
return SASS_MEMORY_NEW(Null, pstate);
}
return Cast<Value>(Listize::perform(parsedSelectors.back()));
}
Signature selector_unify_sig = "selector-unify($selector1, $selector2)";
BUILT_IN(selector_unify)
{
SelectorListObj selector1 = ARGSELS("$selector1");
SelectorListObj selector2 = ARGSELS("$selector2");
SelectorListObj result = selector1->unifyWith(selector2);
return Cast<Value>(Listize::perform(result));
}
Signature simple_selectors_sig = "simple-selectors($selector)";
BUILT_IN(simple_selectors)
{
CompoundSelectorObj sel = ARGSEL("$selector");
List* l = SASS_MEMORY_NEW(List, sel->pstate(), sel->length(), SASS_COMMA);
for (size_t i = 0, L = sel->length(); i < L; ++i) {
const SimpleSelectorObj& ss = sel->get(i);
sass::string ss_string = ss->to_string() ;
l->append(SASS_MEMORY_NEW(String_Quoted, ss->pstate(), ss_string));
}
return l;
}
Signature selector_extend_sig = "selector-extend($selector, $extendee, $extender)";
BUILT_IN(selector_extend)
{
SelectorListObj selector = ARGSELS("$selector");
SelectorListObj target = ARGSELS("$extendee");
SelectorListObj source = ARGSELS("$extender");
SelectorListObj result = Extender::extend(selector, source, target, traces);
return Cast<Value>(Listize::perform(result));
}
Signature selector_replace_sig = "selector-replace($selector, $original, $replacement)";
BUILT_IN(selector_replace)
{
SelectorListObj selector = ARGSELS("$selector");
SelectorListObj target = ARGSELS("$original");
SelectorListObj source = ARGSELS("$replacement");
SelectorListObj result = Extender::replace(selector, source, target, traces);
return Cast<Value>(Listize::perform(result));
}
Signature selector_parse_sig = "selector-parse($selector)";
BUILT_IN(selector_parse)
{
SelectorListObj selector = ARGSELS("$selector");
return Cast<Value>(Listize::perform(selector));
}
Signature is_superselector_sig = "is-superselector($super, $sub)";
BUILT_IN(is_superselector)
{
SelectorListObj sel_sup = ARGSELS("$super");
SelectorListObj sel_sub = ARGSELS("$sub");
bool result = sel_sup->isSuperselectorOf(sel_sub);
return SASS_MEMORY_NEW(Boolean, pstate, result);
}
}
}