Skip to content

Commit

Permalink
Implement is_superselector sass function
Browse files Browse the repository at this point in the history
Fixes #1091
Fixes #1063
Fixes #823
  • Loading branch information
mgreter committed May 12, 2015
1 parent 28ee088 commit aa5043d
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 6 deletions.
74 changes: 71 additions & 3 deletions ast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,23 @@ namespace Sass {
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)
{ lset.insert((*this)[i]->perform(&to_string)); }
{
Selector* lhs = (*this)[i];
if (Wrapped_Selector* wrapped = dynamic_cast<Wrapped_Selector*>(lhs)) {
if (
wrapped->name() == ":matches(" ||
wrapped->name() == ":-moz-any("
) {
lhs = wrapped->selector();
if (Selector_List* list = dynamic_cast<Selector_List*>(lhs)) {
if (Compound_Selector* comp = dynamic_cast<Compound_Selector*>(rhs)) {
if (list->is_superselector_of(comp)) return true;
}
}
}
}
lset.insert(lhs->perform(&to_string));
}
for (size_t i = 0, L = rhs->length(); i < L; ++i)
{ rset.insert((*rhs)[i]->perform(&to_string)); }
return includes(rset.begin(), rset.end(), lset.begin(), lset.end());
Expand Down Expand Up @@ -326,8 +342,6 @@ namespace Sass {

bool Complex_Selector::is_superselector_of(Compound_Selector* rhs)
{
if (length() != 1)
{ return false; }
return base()->is_superselector_of(rhs);
}

Expand All @@ -352,6 +366,16 @@ namespace Sass {
if (l_len == 1)
{ return lhs->head()->is_superselector_of(rhs->base()); }

// we have to look one tail deeper, since we cary the
// combinator around for it (which is important here)
if (rhs->tail() && lhs->tail() && combinator() != Complex_Selector::ANCESTOR_OF) {
Complex_Selector* lhs_tail = lhs->tail();
Complex_Selector* rhs_tail = rhs->tail();
if (lhs_tail->combinator() != rhs_tail->combinator()) return false;
if (!lhs_tail->head()->is_superselector_of(rhs_tail->head())) return false;
}


bool found = false;
Complex_Selector* marker = rhs;
for (size_t i = 0, L = rhs->length(); i < L; ++i) {
Expand Down Expand Up @@ -493,6 +517,50 @@ namespace Sass {
#endif
}

// 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)
{
// 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;
}
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)
{
// 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;
}
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)
{
// 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;
}
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)
{
// 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;
}
return false;
}

/* not used anymore - remove?
Selector_Placeholder* Selector_List::find_placeholder()
{
Expand Down
12 changes: 9 additions & 3 deletions ast.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1943,7 +1943,9 @@ namespace Sass {
return (*this)[0];
return 0;
}
bool is_superselector_of(Compound_Selector* rhs);
bool is_superselector_of(Compound_Selector* sub);
// bool is_superselector_of(Complex_Selector* sub);
// bool is_superselector_of(Selector_List* sub);
virtual unsigned long specificity()
{
int sum = 0;
Expand Down Expand Up @@ -2000,8 +2002,9 @@ namespace Sass {
Complex_Selector* context(Context&);
Complex_Selector* innermost();
size_t length();
bool is_superselector_of(Compound_Selector*);
bool is_superselector_of(Complex_Selector*);
bool is_superselector_of(Compound_Selector* sub);
bool is_superselector_of(Complex_Selector* sub);
bool is_superselector_of(Selector_List* sub);
// virtual Selector_Placeholder* find_placeholder();
Combinator clear_innermost();
void set_innermost(Complex_Selector*, Combinator);
Expand Down Expand Up @@ -2086,6 +2089,9 @@ namespace Sass {
: Selector(pstate), Vectorized<Complex_Selector*>(s), wspace_(0)
{ }
// 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 unsigned long specificity()
{
unsigned long sum = 0;
Expand Down
2 changes: 2 additions & 0 deletions context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,8 @@ namespace Sass {
// Misc Functions
register_function(ctx, inspect_sig, inspect, env);
register_function(ctx, unique_id_sig, unique_id, env);
// Selector functions
register_function(ctx, is_superselector_sig, is_superselector, env);
}

void register_c_functions(Context& ctx, Env* env, Sass_Function_List descrs)
Expand Down
14 changes: 14 additions & 0 deletions functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1564,6 +1564,20 @@ namespace Sass {
// return v;
}

Signature is_superselector_sig = "is-superselector($super, $sub)";
BUILT_IN(is_superselector)
{
To_String to_string(&ctx, false);
Expression* ex_sup = ARG("$super", Expression);
Expression* ex_sub = ARG("$sub", Expression);
string sup_src = ex_sup->perform(&to_string) + "{";
string sub_src = ex_sub->perform(&to_string) + "{";
Selector_List* sel_sup = Parser::parse_selector(sup_src.c_str(), ctx);
Selector_List* sel_sub = Parser::parse_selector(sub_src.c_str(), ctx);
bool result = sel_sup->is_superselector_of(sel_sub);
return new (ctx.mem) Boolean(pstate, result);
}

Signature unique_id_sig = "unique-id()";
BUILT_IN(unique_id)
{
Expand Down
2 changes: 2 additions & 0 deletions functions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ namespace Sass {
extern Signature keywords_sig;
extern Signature set_nth_sig;
extern Signature unique_id_sig;
extern Signature is_superselector_sig;

BUILT_IN(rgb);
BUILT_IN(rgba_4);
Expand Down Expand Up @@ -175,6 +176,7 @@ namespace Sass {
BUILT_IN(keywords);
BUILT_IN(set_nth);
BUILT_IN(unique_id);
BUILT_IN(is_superselector);

}
}
Expand Down
8 changes: 8 additions & 0 deletions parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ namespace Sass {
return p;
}

Selector_List* Parser::parse_selector(const char* src, Context& ctx, ParserState pstate)
{
Parser p = Parser::from_c_str(src, ctx, pstate);
// ToDo: ruby sass errors on parent references
// ToDo: remap the source-map entries somehow
return p.parse_selector_group();
}

bool Parser::peek_newline(const char* start)
{
return peek_linefeed(start ? start : position);
Expand Down
2 changes: 2 additions & 0 deletions parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ namespace Sass {
static Parser from_c_str(const char* src, Context& ctx, ParserState pstate = ParserState("[CSTRING]"));
static Parser from_c_str(const char* beg, const char* end, Context& ctx, ParserState pstate = ParserState("[CSTRING]"));
static Parser from_token(Token t, Context& ctx, ParserState pstate = ParserState("[TOKEN]"));
// special static parsers to convert strings into certain selectors
static Selector_List* parse_selector(const char* src, Context& ctx, ParserState pstate = ParserState("[SELECTOR]"));

#ifdef __clang__

Expand Down

0 comments on commit aa5043d

Please sign in to comment.