diff --git a/ast.cpp b/ast.cpp index 1ce82a08fb..d497e80b87 100644 --- a/ast.cpp +++ b/ast.cpp @@ -817,7 +817,7 @@ Selector_List* Complex_Selector::unify_with(Complex_Selector* other, Context& ct return false; } -Selector_List* Selector_List::unify_with(Selector_List* rhs, Context& ctx) { + Selector_List* Selector_List::unify_with(Selector_List* rhs, Context& ctx) { vector unified_complex_selectors; // Unify all of children with RHS's children, storing the results in `unified_complex_selectors` for (size_t lhs_i = 0, lhs_L = length(); lhs_i < lhs_L; ++lhs_i) { @@ -842,6 +842,39 @@ Selector_List* Selector_List::unify_with(Selector_List* rhs, Context& ctx) { return final_result; } + void Selector_List::populate_extends(Selector_List* extendee, Context& ctx, ExtensionSubsetMap& extends) { + To_String to_string; + + Selector_List* extender = this; + for (auto complex_sel : extendee->elements()) { + Complex_Selector* c = complex_sel; + + + // Ignore any parent selectors, until we find the first non Selector_Reference head + Compound_Selector* compound_sel = c->head(); + Complex_Selector* pIter = complex_sel; + while (pIter) { + Compound_Selector* pHead = pIter->head(); + if (pHead && dynamic_cast(pHead->elements()[0]) == NULL) { + compound_sel = pHead; + break; + } + + pIter = pIter->tail(); + } + + if (!pIter->head() || pIter->tail()) { + error("nested selectors may not be extended", c->pstate()); + } + + compound_sel->is_optional(extendee->is_optional()); + + for (size_t i = 0, L = extender->length(); i < L; ++i) { + extends.put(compound_sel->to_str_vec(), make_pair((*extender)[i], compound_sel)); + } + } + }; + vector Compound_Selector::to_str_vec() { To_String to_string; diff --git a/ast.hpp b/ast.hpp index 442accd282..0ece738b95 100644 --- a/ast.hpp +++ b/ast.hpp @@ -2090,6 +2090,7 @@ namespace Sass { }; typedef deque ComplexSelectorDeque; + typedef Subset_Map > ExtensionSubsetMap; /////////////////////////////////// // Comma-separated selector groups. @@ -2114,6 +2115,7 @@ namespace Sass { void remove_parent_selectors(); // virtual Selector_Placeholder* find_placeholder(); Selector_List* unify_with(Selector_List*, Context&); + void populate_extends(Selector_List*, Context&, ExtensionSubsetMap&); virtual bool is_superselector_of(Compound_Selector* sub, string wrapping = ""); virtual bool is_superselector_of(Complex_Selector* sub, string wrapping = ""); virtual bool is_superselector_of(Selector_List* sub, string wrapping = ""); diff --git a/context.cpp b/context.cpp index 485f52c889..3d284b9a93 100644 --- a/context.cpp +++ b/context.cpp @@ -557,9 +557,12 @@ namespace Sass { register_function(ctx, mem, inspect_sig, inspect, env); register_function(ctx, mem, unique_id_sig, unique_id, env); // Selector functions - register_function(ctx, mem, is_superselector_sig, is_superselector, env); - register_function(ctx, mem, selector_unify_sig, selector_unify, env); register_function(ctx, mem, selector_nest_sig, selector_nest, env); + register_function(ctx, mem, selector_append_sig, selector_append, env); + register_function(ctx, mem, selector_extend_sig, selector_extend, env); + register_function(ctx, mem, selector_replace_sig, selector_replace, env); + register_function(ctx, mem, selector_unify_sig, selector_unify, env); + register_function(ctx, mem, is_superselector_sig, is_superselector, env); register_function(ctx, mem, simple_selectors_sig, simple_selectors, env); register_function(ctx, mem, selector_parse_sig, selector_parse, env); } diff --git a/functions.cpp b/functions.cpp index a692a1c52b..5a7d65fb17 100644 --- a/functions.cpp +++ b/functions.cpp @@ -3,6 +3,7 @@ #include "context.hpp" #include "backtrace.hpp" #include "parser.hpp" +#include "extend.hpp" #include "constants.hpp" #include "to_string.hpp" #include "inspect.hpp" @@ -1652,6 +1653,91 @@ namespace Sass { return result->perform(&listize); } + Signature selector_append_sig = "selector-append($selectors...)"; + BUILT_IN(selector_append) + { + To_String to_string; + List* arglist = ARG("$selectors", List); + + // Not enough parameters + if( arglist->length() == 0 ) + error("$selectors: At least one selector must be passed", pstate); + + // Parse args into vector of selectors + vector parsedSelectors; + for (size_t i = 0, L = arglist->length(); i < L; ++i) { + Expression* exp = dynamic_cast(arglist->value_at_index(i)); + string exp_src = exp->perform(&to_string) + "{"; + Selector_List* sel = Parser::parse_selector(exp_src.c_str(), ctx, ctx.mem); + parsedSelectors.push_back(sel); + } + + // Nothing to do + if( parsedSelectors.empty() ) { + return new (ctx.mem) Null(pstate); + } + + // Set the first element as the `result`, keep appending to as we go down the parsedSelector vector. + std::vector::iterator itr = parsedSelectors.begin(); + Selector_List* result = *itr; + ++itr; + + for(;itr != parsedSelectors.end(); ++itr) { + Selector_List* child = *itr; + vector newElements; + + // For every COMPLEX_SELECTOR in `result` + // For every COMPLEX_SELECTOR in `child` + // let parentSeqClone equal a copy of result->elements[i] + // let childSeq equal child->elements[j] + // Append all of childSeq head elements into parentSeqClone + // Set the innermost tail of parentSeqClone, to childSeq's tail + // Replace result->elements with newElements + for (size_t i = 0, resultLen = result->length(); i < resultLen; ++i) { + for (size_t j = 0, childLen = child->length(); j < childLen; ++j) { + Complex_Selector* parentSeqClone = (*result)[i]->cloneFully(ctx); + Complex_Selector* childSeq = (*child)[j]; + Complex_Selector* base = childSeq->tail(); + + // Must be a simple sequence + if( childSeq->combinator() != Complex_Selector::Combinator::ANCESTOR_OF ) { + string msg("Can't append `"); + msg += childSeq->perform(&to_string); + msg += "` to `"; + msg += parentSeqClone->perform(&to_string);; + msg += "`"; + error(msg, pstate, backtrace); + } + + // Cannot be a Universal selector + Type_Selector* pType = dynamic_cast(base->head()->first()); + if(pType && pType->name() == "*") { + string msg("Can't append `"); + msg += childSeq->perform(&to_string); + msg += "` to `"; + msg += parentSeqClone->perform(&to_string);; + msg += "`"; + error(msg, pstate, backtrace); + } + + // TODO: Add check for namespace stuff + + // append any selectors in childSeq's head + *(parentSeqClone->innermost()->head()) += (base->head()); + + // Set parentSeqClone new tail + parentSeqClone->innermost()->tail( base->tail() ); + + newElements.push_back(parentSeqClone); + } + } + + result->elements(newElements); + } + + Listize listize(ctx); + return result->perform(&listize); + } Signature selector_unify_sig = "selector-unify($selector1, $selector2)"; BUILT_IN(selector_unify) @@ -1668,7 +1754,7 @@ namespace Sass { return result->perform(&listize); } - Signature simple_selectors_sig = "simple_selectors($selector)"; + Signature simple_selectors_sig = "simple-selectors($selector)"; BUILT_IN(simple_selectors) { Compound_Selector* sel = ARGSEL("$selector", Compound_Selector, p_contextualize); @@ -1686,6 +1772,42 @@ namespace Sass { return l; } + Signature selector_extend_sig = "selector-extend($selector, $extendee, $extender)"; + BUILT_IN(selector_extend) + { + To_String to_string; + + Selector_List* selector = ARGSEL("$selector", Selector_List, p_contextualize); + Selector_List* extendee = ARGSEL("$extendee", Selector_List, p_contextualize); + Selector_List* extender = ARGSEL("$extender", Selector_List, p_contextualize); + + ExtensionSubsetMap subset_map; + extender->populate_extends(extendee, ctx, subset_map); + + bool extendedSomething; + Selector_List* result = Extend::extendSelectorList(selector, ctx, subset_map, false, extendedSomething); + + Listize listize(ctx); + return result->perform(&listize); + } + + Signature selector_replace_sig = "selector-replace($selector, $original, $replacement)"; + BUILT_IN(selector_replace) + { + Selector_List* selector = ARGSEL("$selector", Selector_List, p_contextualize); + Selector_List* original = ARGSEL("$original", Selector_List, p_contextualize); + Selector_List* replacement = ARGSEL("$replacement", Selector_List, p_contextualize); + + ExtensionSubsetMap subset_map; + replacement->populate_extends(original, ctx, subset_map); + + bool extendedSomething; + Selector_List* result = Extend::extendSelectorList(selector, ctx, subset_map, true, extendedSomething); + + Listize listize(ctx); + return result->perform(&listize); + } + Signature selector_parse_sig = "selector-parse($selector)"; BUILT_IN(selector_parse) { diff --git a/functions.hpp b/functions.hpp index 4a3cf41251..96f3cedf1f 100644 --- a/functions.hpp +++ b/functions.hpp @@ -102,6 +102,9 @@ namespace Sass { extern Signature set_nth_sig; extern Signature unique_id_sig; extern Signature selector_nest_sig; + extern Signature selector_append_sig; + extern Signature selector_extend_sig; + extern Signature selector_replace_sig; extern Signature selector_unify_sig; extern Signature is_superselector_sig; extern Signature simple_selectors_sig; @@ -181,6 +184,9 @@ namespace Sass { BUILT_IN(set_nth); BUILT_IN(unique_id); BUILT_IN(selector_nest); + BUILT_IN(selector_append); + BUILT_IN(selector_extend); + BUILT_IN(selector_replace); BUILT_IN(selector_unify); BUILT_IN(is_superselector); BUILT_IN(simple_selectors); diff --git a/node.cpp b/node.cpp index ca7cfaf7c4..64cd95070d 100644 --- a/node.cpp +++ b/node.cpp @@ -288,14 +288,9 @@ namespace Sass { // This is only used in Complex_Selector::unify_with for now, may need modifications to fit other needs Node Node::naiveTrim(Node& seqses, Context& ctx) { + SourcesSet sel_set; Node result = Node::createCollection(); - To_String to_string; - std::set< Complex_Selector*, std::function< bool(Complex_Selector*, Complex_Selector*) > > sel_set([&] ( Complex_Selector* lhs, Complex_Selector* rhs ) { - bool result = lhs->perform(&to_string) < rhs->perform(&to_string); - return result; - } ); - // Add all selectors we don't already have, everything else just add it blindly for (NodeDeque::iterator seqsesIter = seqses.collection()->begin(), seqsesIterEnd = seqses.collection()->end(); seqsesIter != seqsesIterEnd; ++seqsesIter) { Node& seqs1 = *seqsesIter;