diff --git a/ast.cpp b/ast.cpp index 785b6710af..1ce82a08fb 100644 --- a/ast.cpp +++ b/ast.cpp @@ -1,5 +1,7 @@ #include "ast.hpp" #include "context.hpp" +#include "node.hpp" +#include "extend.hpp" #include "debugger.hpp" #include "to_string.hpp" #include @@ -444,6 +446,40 @@ size_t n = 0; return false; } +Selector_List* Complex_Selector::unify_with(Complex_Selector* other, Context& ctx) { + To_String to_string; + Compound_Selector* thisBase = base(); + Compound_Selector* rhsBase = other->base(); + + if( thisBase == 0 || rhsBase == 0 ) return 0; + + // Not sure about this check, but closest way I could check to see if this is a ruby 'SimpleSequence' equivalent + if( tail()->combinator() != Combinator::ANCESTOR_OF || other->tail()->combinator() != Combinator::ANCESTOR_OF ) return 0; + + Compound_Selector* unified = rhsBase->unify_with(thisBase, ctx); + if( unified == 0 ) return 0; + + Node lhsNode = complexSelectorToNode(this, ctx); + Node rhsNode = complexSelectorToNode(other, ctx); + + // Create a temp Complex_Selector, turn it into a Node, and combine it with the existing RHS node + Complex_Selector* fakeComplexSelector = new (ctx.mem) Complex_Selector(ParserState("[NODE]"), Complex_Selector::ANCESTOR_OF, unified, NULL); + Node unifiedNode = complexSelectorToNode(fakeComplexSelector, ctx); + rhsNode.plus(unifiedNode); + + Node node = Extend::subweave(lhsNode, rhsNode, ctx); + + Selector_List* result = new (ctx.mem) Selector_List(pstate()); + for (NodeDeque::iterator iter = node.collection()->begin(), iterEnd = node.collection()->end(); iter != iterEnd; iter++) { + Node childNode = *iter; + childNode = Node::naiveTrim(childNode, ctx); + + Complex_Selector* childNodeAsComplexSelector = nodeToComplexSelector(childNode, ctx); + if( childNodeAsComplexSelector ) { (*result) << childNodeAsComplexSelector; } + } + return result->length() ? result : 0; + } + bool Compound_Selector::operator==(const Compound_Selector& rhs) const { To_String to_string; @@ -781,6 +817,31 @@ size_t n = 0; return false; } +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) { + Complex_Selector* seq1 = (*this)[lhs_i]; + for(size_t rhs_i = 0, rhs_L = rhs->length(); rhs_i < rhs_L; ++rhs_i) { + Complex_Selector* seq2 = (*rhs)[rhs_i]; + + Selector_List* result = seq1->unify_with(seq2, ctx); + if( result ) { + for(size_t i = 0, L = result->length(); i < L; ++i) { + unified_complex_selectors.push_back( (*result)[i] ); + } + } + } + } + + // Creates the final Selector_List by combining all the complex selectors + Selector_List* final_result = new (ctx.mem) Selector_List(pstate()); + for (auto itr = unified_complex_selectors.begin(); itr != unified_complex_selectors.end(); ++itr) { + *final_result << *itr; + } + return final_result; + } + vector Compound_Selector::to_str_vec() { To_String to_string; diff --git a/ast.hpp b/ast.hpp index 6b786b432c..e67d7a7e40 100644 --- a/ast.hpp +++ b/ast.hpp @@ -2021,6 +2021,7 @@ namespace Sass { virtual bool is_superselector_of(Complex_Selector* sub, string wrapping = ""); virtual bool is_superselector_of(Selector_List* sub, string wrapping = ""); // virtual Selector_Placeholder* find_placeholder(); + Selector_List* unify_with(Complex_Selector* rhs, Context& ctx); Combinator clear_innermost(); void set_innermost(Complex_Selector*, Combinator); virtual unsigned long specificity() const @@ -2111,6 +2112,7 @@ namespace Sass { // basically unwraps parsed selectors void remove_parent_selectors(); // virtual Selector_Placeholder* find_placeholder(); + Selector_List* unify_with(Selector_List*, Context&); 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/extend.cpp b/extend.cpp index b348ec6e00..af646bce22 100644 --- a/extend.cpp +++ b/extend.cpp @@ -1150,7 +1150,7 @@ namespace Sass { result end */ - static Node subweave(Node& one, Node& two, Context& ctx) { + Node Extend::subweave(Node& one, Node& two, Context& ctx) { // Check for the simple cases if (one.collection()->size() == 0) { Node out = Node::createCollection(); @@ -1441,7 +1441,7 @@ namespace Sass { for (NodeDeque::iterator beforesIter = befores.collection()->begin(), beforesEndIter = befores.collection()->end(); beforesIter != beforesEndIter; beforesIter++) { Node& before = *beforesIter; - Node sub = subweave(before, current, ctx); + Node sub = Extend::subweave(before, current, ctx); DEBUG_PRINTLN(WEAVE, "SUB: " << sub) @@ -1883,7 +1883,7 @@ if (pSels->has_line_feed()) pNewSelector->has_line_feed(true);; /* This is the equivalent of ruby's CommaSequence.do_extend. */ - static Selector_List* extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subsetMap, bool& extendedSomething) { + Selector_List* Extend::extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subsetMap, bool isReplace, bool& extendedSomething) { To_String to_string(&ctx); @@ -1923,7 +1923,9 @@ if (pSels->has_line_feed()) pNewSelector->has_line_feed(true);; } } - for (NodeDeque::iterator iterator = extendedSelectors.collection()->begin(), iteratorEnd = extendedSelectors.collection()->end(); iterator != iteratorEnd; ++iterator) { + for (NodeDeque::iterator iterator = extendedSelectors.collection()->begin(), iteratorBegin = extendedSelectors.collection()->begin(), iteratorEnd = extendedSelectors.collection()->end(); iterator != iteratorEnd; ++iterator) { + // When it is a replace, skip the first one, unless there is only one + if(isReplace && iterator == iteratorBegin && extendedSelectors.collection()->size() > 1 ) continue; Node& childNode = *iterator; *pNewSelectors << nodeToComplexSelector(childNode, ctx); } @@ -1980,7 +1982,7 @@ if (pSels->has_line_feed()) pNewSelector->has_line_feed(true);; } bool extendedSomething = false; - Selector_List* pNewSelectorList = extendSelectorList(static_cast(pObject->selector()), ctx, subsetMap, extendedSomething); + Selector_List* pNewSelectorList = Extend::extendSelectorList(static_cast(pObject->selector()), ctx, subsetMap, false, extendedSomething); if (extendedSomething && pNewSelectorList) { DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND ORIGINAL SELECTORS: " << static_cast(pObject->selector())->perform(&to_string)) diff --git a/extend.hpp b/extend.hpp index 6158b98280..23f3db8a02 100644 --- a/extend.hpp +++ b/extend.hpp @@ -14,6 +14,7 @@ namespace Sass { using namespace std; class Context; + class Node; typedef Subset_Map > ExtensionSubsetMap; @@ -24,7 +25,10 @@ namespace Sass { void fallback_impl(AST_Node* n) { }; + public: + static Node subweave(Node& one, Node& two, Context& ctx); + static Selector_List* extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subsetMap, bool isReplace, bool& extendedSomething); Extend(Context&, ExtensionSubsetMap&); virtual ~Extend() { } diff --git a/functions.cpp b/functions.cpp index ce4b90b625..f71dc03e19 100644 --- a/functions.cpp +++ b/functions.cpp @@ -1632,9 +1632,9 @@ namespace Sass { string sub_src = sel2->perform(&to_string) + "{"; Selector_List* selector1 = Parser::parse_selector(sup_src.c_str(), ctx, ctx.mem); Selector_List* selector2 = Parser::parse_selector(sub_src.c_str(), ctx, ctx.mem); -// Selector_List* result = selector1->unify_with(selector2, ctx); + Selector_List* result = selector1->unify_with(selector2, ctx); Listize listize(ctx); - return selector2->perform(&listize); + return result->perform(&listize); } Signature is_superselector_sig = "is-superselector($super, $sub)"; diff --git a/node.cpp b/node.cpp index b41bcb6b2e..ca7cfaf7c4 100644 --- a/node.cpp +++ b/node.cpp @@ -284,5 +284,32 @@ namespace Sass { return pFirst; } + // A very naive trim function, which removes duplicates in a node + // 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) { + + 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; + if( seqs1.isSelector() ) { + auto found = sel_set.find( seqs1.selector() ); + if( found == sel_set.end() ) { + sel_set.insert(seqs1.selector()); + result.collection()->push_back(seqs1); + } + } else { + result.collection()->push_back(seqs1); + } + } + return result; + } } diff --git a/node.hpp b/node.hpp index 6aa1156819..faaa95eea7 100644 --- a/node.hpp +++ b/node.hpp @@ -69,6 +69,7 @@ namespace Sass { static Node createCollection(const NodeDeque& values); static Node createNil(); + static Node naiveTrim(Node& seqses, Context& ctx); Node clone(Context& ctx) const;