diff --git a/ast.cpp b/ast.cpp index 1d078f9a8b..785b6710af 100644 --- a/ast.cpp +++ b/ast.cpp @@ -1,5 +1,6 @@ #include "ast.hpp" #include "context.hpp" +#include "debugger.hpp" #include "to_string.hpp" #include #include @@ -220,18 +221,90 @@ namespace Sass { return Simple_Selector::unify_with(rhs, ctx); } - bool Compound_Selector::is_superselector_of(Compound_Selector* rhs) + bool Wrapped_Selector::is_superselector_of(Wrapped_Selector* sub) + { + if (this->name() != sub->name()) return false; + if (this->name() == ":current") return false; + if (Selector_List* rhs_list = dynamic_cast(sub->selector())) { + if (Selector_List* lhs_list = dynamic_cast(selector())) { + return lhs_list->is_superselector_of(rhs_list); + } + cerr << "INVALID INTERNAL STATE\n"; + } else { + cerr << "INVALID INTERNAL STATE\n"; + } + return false; + } + + bool Simple_Selector::is_superselector_of(Compound_Selector* sub) + { + cerr << "doda\n"; + return false; + } + + bool Pseudo_Selector::is_superselector_of(Compound_Selector* compound) + { + cerr << "is_sup pseudo Compound_Selector\n"; + return false; + } + bool Pseudo_Selector::is_superselector_of(Complex_Selector* complex) + { + cerr << "is_sup pseudo Complex_Selector\n"; + return false; + } + bool Pseudo_Selector::is_superselector_of(Selector_List* list) + { + cerr << "is_sup pseudo Selector_List\n"; + return false; + } + + bool Compound_Selector::is_superselector_of(Selector_List* rhs, string wrapped) + { +//cerr << "DAHJKA\n"; + for (Complex_Selector* item : rhs->elements()) { + if (is_superselector_of(item, wrapped)) return true; + } + return false; + } + + bool Compound_Selector::is_superselector_of(Complex_Selector* rhs, string wrapped) + { + if (rhs->head()) return is_superselector_of(rhs->head(), wrapped); + return false; + } + + bool Compound_Selector::is_superselector_of(Compound_Selector* rhs, string wrapping) { To_String to_string; - Simple_Selector* lbase = base(); + Compound_Selector* lhs = this; + Simple_Selector* lbase = lhs->base(); Simple_Selector* rbase = rhs->base(); // Check if pseudo-elements are the same between the selectors - +/* + if (elements().size() == 1 && rhs->elements().size() == 1 && + dynamic_cast(rhs->elements()[0]) != 0 && + dynamic_cast(this->elements()[0]) != 0) { + //cerr << "is super\n"; + return true; + } +*/ +//debug_ast(lhs, "lhs: "); +//debug_ast(rhs, "rhs: "); +//cerr << elements().size() << endl; +//cerr << rhs->elements().size() << endl; +/* + for (auto item : elements()) { + if (!item->is_superselector_of(rhs)) return false; + } + //cerr << "is super\n"; + return true; +*/ set lpsuedoset, rpsuedoset; for (size_t i = 0, L = length(); i < L; ++i) { + // debug_ast((*this)[i]); if ((*this)[i]->is_pseudo_element()) { string pseudo((*this)[i]->perform(&to_string)); pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving @@ -253,27 +326,103 @@ namespace Sass { // Check the Simple_Selectors set lset, rset; - +size_t i = 0; +size_t L = 0; +size_t n = 0; +// if (length() < rhs->length()) if (!lbase) // no lbase; just see if the left-hand qualifiers are a subset of the right-hand selector { - for (size_t i = 0, L = length(); i < L; ++i) +/* + for (i = 0, L = length(); i < L; ++i) + { + Simple_Selector* lhs = (*this)[i]; +// if (lhs && !lhs->is_superselector_of(rhs)) return false; + } +// return true; +*/ + for (i = 0, L = length(); i < L; ++i) { Selector* lhs = (*this)[i]; + Simple_Selector* rhs2 = rhs->elements().size() > i ? (*rhs)[i] : 0; // very special case for wrapped matches selector if (Wrapped_Selector* wrapped = dynamic_cast(lhs)) { + if (wrapped->name() == ":not") { + if (Selector_List* not_list = dynamic_cast(wrapped->selector())) { + if (!wrapping.empty() && wrapping == wrapped->name()) {; + if (not_list->is_superselector_of(rhs, wrapped->name())) return false; + } else { + if (not_list->is_superselector_of(rhs, wrapped->name())) return false; + } + } else { + cerr << "do not\n"; + } + } if (wrapped->name() == ":matches" || wrapped->name() == ":-moz-any") { + lhs = wrapped->selector(); if (Selector_List* list = dynamic_cast(wrapped->selector())) { if (Compound_Selector* comp = dynamic_cast(rhs)) { - if (list->is_superselector_of(comp)) return true; + if (!wrapping.empty() && wrapping != wrapped->name()) return false; + if (wrapping.empty() || wrapping != wrapped->name()) {; + if (list->is_superselector_of(comp, wrapped->name())) return true; + } } } } + if (Wrapped_Selector* wrapped_r = dynamic_cast(rhs2)) { + if (!wrapping.empty()) { + // cerr << "MORE \n"; + } + if (wrapped->name() == wrapped_r->name()) { + if (wrapped->is_superselector_of(wrapped_r)) { + // lset.insert(lhs->perform(&to_string)); + // rset.insert(wrapped_r->perform(&to_string)); + continue; + rset.insert(lhs->perform(&to_string)); + + }} + } } // match from here on as strings lset.insert(lhs->perform(&to_string)); + // rset.insert((*rhs)[i]->perform(&to_string)); } - for (size_t i = 0, L = rhs->length(); i < L; ++i) - { rset.insert((*rhs)[i]->perform(&to_string)); } +//debug_ast(this); + for (n = 0, L = rhs->length(); n < L; ++n) + { + auto r = (*rhs)[n]; + if (Wrapped_Selector* wrapped = dynamic_cast(r)) { + if (wrapped->name() == ":not") { + if (Selector_List* ls = dynamic_cast(wrapped->selector())) { + ls->remove_parent_selectors(); + if (!wrapping.empty() && wrapping == wrapped->name()) {; + if (is_superselector_of(ls, wrapped->name())) return false; + } else { + if (is_superselector_of(ls, wrapped->name())) return false; + } + } + } + if (wrapped->name() == ":matches" || wrapped->name() == ":-moz-any") { + if (!wrapping.empty()) { + if (wrapping != wrapped->name()) return false; +// cerr << "DID THIS\n"; + } + if (Selector_List* ls = dynamic_cast(wrapped->selector())) { + ls->remove_parent_selectors(); + return (is_superselector_of(ls, wrapped->name())); + // continue; + } else { +// cerr << "ASDADASDASD\n"; + } + } + } + rset.insert(r->perform(&to_string)); + } + + //for (auto l : lset) { cerr << "l: " << l << endl; } + //for (auto r : rset) { cerr << "r: " << r << endl; } + + if (lset.size() == 0) return true; + // return true if rset contains all the elements of lset return includes(rset.begin(), rset.end(), lset.begin(), lset.end()); } else { // there's an lbase @@ -353,12 +502,12 @@ namespace Sass { return *pLeft < *pRight; } - bool Complex_Selector::is_superselector_of(Compound_Selector* rhs) + bool Complex_Selector::is_superselector_of(Compound_Selector* rhs, string wrapping) { - return base()->is_superselector_of(rhs); + return base()->is_superselector_of(rhs, wrapping); } - bool Complex_Selector::is_superselector_of(Complex_Selector* rhs) + bool Complex_Selector::is_superselector_of(Complex_Selector* rhs, string wrapping) { Complex_Selector* lhs = this; To_String to_string; @@ -377,7 +526,7 @@ namespace Sass { { return false; } if (l_len == 1) - { return lhs->head()->is_superselector_of(rhs->base()); } + { return lhs->head()->is_superselector_of(rhs->base(), wrapping); } // we have to look one tail deeper, since we cary the // combinator around for it (which is important here) @@ -394,7 +543,7 @@ namespace Sass { for (size_t i = 0, L = rhs->length(); i < L; ++i) { if (i == L-1) { return false; } - if (lhs->head()->is_superselector_of(marker->head())) + if (lhs->head()->is_superselector_of(marker->head(), wrapping)) { found = true; break; } marker = marker->tail(); } @@ -458,6 +607,49 @@ namespace Sass { return cpy; } + Complex_Selector* Complex_Selector::parentize(Complex_Selector* parent, Context& ctx) + { + Complex_Selector* pr = 0; + Compound_Selector* head = this->head(); + Complex_Selector* trailing = this->tail(); + // create a new complex selector to return a processed copy + Complex_Selector* ss = new (ctx.mem) Complex_Selector(pstate()); + // Points to last complex selector + // Moved when resolving parent refs + Complex_Selector* cur = ss; + + // check if compound selector has exactly one parent reference + // if so we need to connect the parent to the current selector + // then we also need to add the remaining simple selector to the new "parent" + if (head) { + // create a new compound and move originals if needed + // we may add the simple selector to the same selector + // with parent refs we may put them in different places + ss->head(new (ctx.mem) Compound_Selector(head->pstate())); + // process simple selectors sequence + for (size_t i = 0; i < head->size(); ++i) { + // we have a parent selector in a simple selector list + // mix parent complex selector into the compound list + if (dynamic_cast((*head)[i])) { + // clone the parent selector + pr = parent->cloneFully(ctx); + // assign head and tail + cur->head(pr->head()); + cur->tail(pr->tail()); + // move forward + cur = pr->last(); + } else { + // just add simple selector + *cur->head() << (*head)[i]; + } + } + } + // parentize and assign trailing complex selector + if (trailing) cur->tail(trailing->parentize(parent, ctx)); + // return selector + return ss; + } + Complex_Selector* Complex_Selector::innermost() { if (!tail()) return this; @@ -544,41 +736,44 @@ namespace Sass { // it's a superselector if every selector of the right side // list is a superselector of the given left side selector - bool Complex_Selector::is_superselector_of(Selector_List *sub) + bool Complex_Selector::is_superselector_of(Selector_List *sub, string wrapping) { // Check every rhs selector against left hand list for(size_t i = 0, L = sub->length(); i < L; ++i) { - if (!is_superselector_of((*sub)[i])) return false; + if (!is_superselector_of((*sub)[i], wrapping)) return false; } return true; } // it's a superselector if every selector of the right side // list is a superselector of the given left side selector - bool Selector_List::is_superselector_of(Selector_List *sub) + bool Selector_List::is_superselector_of(Selector_List *sub, string wrapping) { + // cerr << "do Selector_List " << wrapping << "\n"; // Check every rhs selector against left hand list for(size_t i = 0, L = sub->length(); i < L; ++i) { - if (!is_superselector_of((*sub)[i])) return false; + if (!is_superselector_of((*sub)[i], wrapping)) return false; } return true; } // it's a superselector if every selector on the right side // is a superselector of any one of the left side selectors - bool Selector_List::is_superselector_of(Compound_Selector *sub) + bool Selector_List::is_superselector_of(Compound_Selector *sub, string wrapping) { + // cerr << "do Compound_Selector " << wrapping << "\n"; // Check every lhs selector against right hand for(size_t i = 0, L = length(); i < L; ++i) { - if ((*this)[i]->is_superselector_of(sub)) return true; + if ((*this)[i]->is_superselector_of(sub, wrapping)) return true; } return false; } // it's a superselector if every selector on the right side // is a superselector of any one of the left side selectors - bool Selector_List::is_superselector_of(Complex_Selector *sub) + bool Selector_List::is_superselector_of(Complex_Selector *sub, string wrapping) { + // cerr << "do Complex_Selector " << wrapping << "\n"; // Check every lhs selector against right hand for(size_t i = 0, L = length(); i < L; ++i) { if ((*this)[i]->is_superselector_of(sub)) return true; diff --git a/ast.hpp b/ast.hpp index 58d007211a..6b786b432c 100644 --- a/ast.hpp +++ b/ast.hpp @@ -183,6 +183,10 @@ namespace Sass { elements_.insert(elements_.begin(), element); return *this; } + size_t size() + { + return elements_.size(); + } vector& elements() { return elements_; } const vector& elements() const { return elements_; } vector& elements(vector& e) { elements_ = e; return elements_; } @@ -1728,6 +1732,10 @@ namespace Sass { virtual bool is_pseudo_element() { return false; } virtual bool is_pseudo_class() { return false; } + virtual bool is_superselector_of(Compound_Selector* sub); + // virtual bool is_superselector_of(Complex_Selector* sub); + // virtual bool is_superselector_of(Selector_List* sub); + bool operator==(const Simple_Selector& rhs) const; inline bool operator!=(const Simple_Selector& rhs) const { return !(*this == rhs); } @@ -1862,6 +1870,10 @@ namespace Sass { && ! is_pseudo_class_element(name_); } + virtual bool is_superselector_of(Compound_Selector* compound); + virtual bool is_superselector_of(Complex_Selector* complex); + virtual bool is_superselector_of(Selector_List* list); + // A pseudo-element is made of two colons (::) followed by the name. // The `::` notation is introduced by the current document in order to // establish a discrimination between pseudo-classes and pseudo-elements. @@ -1895,6 +1907,7 @@ namespace Sass { Wrapped_Selector(ParserState pstate, string n, Selector* sel) : Simple_Selector(pstate), name_(n), selector_(sel) { } + virtual bool is_superselector_of(Wrapped_Selector* sub); // Selectors inside the negation pseudo-class are counted like any // other, but the negation itself does not count as a pseudo-class. virtual unsigned long specificity() @@ -1929,7 +1942,6 @@ namespace Sass { Vectorized(s), has_parent_reference_(false) { } - Compound_Selector* unify_with(Compound_Selector* rhs, Context& ctx); // virtual Selector_Placeholder* find_placeholder(); virtual bool has_parent_ref(); @@ -1943,9 +1955,9 @@ namespace Sass { return (*this)[0]; return 0; } - bool is_superselector_of(Compound_Selector* sub); - // bool is_superselector_of(Complex_Selector* sub); - // bool is_superselector_of(Selector_List* sub); + virtual bool is_superselector_of(Compound_Selector* sub, string wrapped = ""); + virtual bool is_superselector_of(Complex_Selector* sub, string wrapped = ""); + virtual bool is_superselector_of(Selector_List* sub, string wrapped = ""); virtual unsigned long specificity() { int sum = 0; @@ -1998,13 +2010,16 @@ namespace Sass { if ((h && h->has_placeholder()) || (t && t->has_placeholder())) has_placeholder(true); } virtual bool has_parent_ref(); + Complex_Selector* parentize(Complex_Selector* parent, Context& ctx); Compound_Selector* base(); Complex_Selector* context(Context&); Complex_Selector* innermost(); + Complex_Selector* last() { return innermost(); }; + // Complex_Selector* first() { return this || tail(); }; size_t length(); - bool is_superselector_of(Compound_Selector* sub); - bool is_superselector_of(Complex_Selector* sub); - bool is_superselector_of(Selector_List* sub); + 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 = ""); // virtual Selector_Placeholder* find_placeholder(); Combinator clear_innermost(); void set_innermost(Complex_Selector*, Combinator); @@ -2077,7 +2092,11 @@ namespace Sass { /////////////////////////////////// // Comma-separated selector groups. /////////////////////////////////// - class Selector_List : public Selector, public Vectorized { + class Selector_List + : public Selector, + public Vectorized + + { #ifdef DEBUG ADD_PROPERTY(string, mCachedSelector); #endif @@ -2092,9 +2111,9 @@ namespace Sass { // basically unwraps parsed selectors void remove_parent_selectors(); // virtual Selector_Placeholder* find_placeholder(); - bool is_superselector_of(Compound_Selector* sub); - bool is_superselector_of(Complex_Selector* sub); - bool is_superselector_of(Selector_List* sub); + 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 = ""); virtual unsigned long specificity() { unsigned long sum = 0; diff --git a/context.cpp b/context.cpp index 4dc1d3db59..e7be3ca50d 100644 --- a/context.cpp +++ b/context.cpp @@ -558,6 +558,8 @@ namespace Sass { 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); } void register_c_functions(Context& ctx, Env* env, Sass_Function_List descrs) diff --git a/functions.cpp b/functions.cpp index 3c1e9d83b5..8ca412ed65 100644 --- a/functions.cpp +++ b/functions.cpp @@ -9,6 +9,7 @@ #include "eval.hpp" #include "util.hpp" #include "expand.hpp" +#include "parentize.hpp" #include "utf8_string.hpp" #include "utf8.h" @@ -23,11 +24,14 @@ #include #include + #ifdef __MINGW32__ #include "windows.h" #include "wincrypt.h" #endif +#include "debugger.hpp" + #define ARG(argname, argtype) get_arg(argname, env, sig, pstate, backtrace) #define ARGR(argname, argtype, lo, hi) get_arg_r(argname, env, sig, pstate, lo, hi, backtrace) #define ARGM(argname, argtype, ctx) get_arg_m(argname, env, sig, pstate, backtrace, ctx) @@ -1560,6 +1564,79 @@ namespace Sass { } // return v; } + Signature selector_nest_sig = "selector-nest($selectors...)"; + BUILT_IN(selector_nest) + { + + To_String to_string(&ctx, false); + 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; + + // debug_ast(result); + Listize listize(ctx); + + for(;itr != parsedSelectors.end(); ++itr) { + + Selector_List* child = *itr; + vector exploded; + + // For every COMPLEX_SELECTOR in `child` + // For every COMPLEX_SELECTOR in `result` + // let parentSeqClone equal a copy of result->elements[i] + // let childSeq equal child->elements[j] + // Set childSeq as the new innermost tail of parentSeqClone + // Add parentSeqClone to the newElements + // 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* parent = (*result)[i]->cloneFully(ctx); + exploded.push_back((*child)[j]->parentize(parent, ctx)); + } + } + + result->elements(exploded); + } + + return result->perform(&listize); + } + + + Signature selector_unify_sig = "selector-unify($selector1, $selector2)"; + BUILT_IN(selector_unify) + { + To_String to_string(&ctx, false); + Expression* sel1 = ARG("$selector1", Expression); + Expression* sel2 = ARG("$selector2", Expression); + string sup_src = sel1->perform(&to_string) + "{"; + 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); + Listize listize(ctx); + return selector2->perform(&listize); + } Signature is_superselector_sig = "is-superselector($super, $sub)"; BUILT_IN(is_superselector) diff --git a/functions.hpp b/functions.hpp index c0307f6ed4..88896aaefd 100644 --- a/functions.hpp +++ b/functions.hpp @@ -101,6 +101,8 @@ namespace Sass { extern Signature keywords_sig; extern Signature set_nth_sig; extern Signature unique_id_sig; + extern Signature selector_nest_sig; + extern Signature selector_unify_sig; extern Signature is_superselector_sig; BUILT_IN(rgb); @@ -176,6 +178,8 @@ namespace Sass { BUILT_IN(keywords); BUILT_IN(set_nth); BUILT_IN(unique_id); + BUILT_IN(selector_nest); + BUILT_IN(selector_unify); BUILT_IN(is_superselector); }