Skip to content

Commit aa5043d

Browse files
committed
Implement is_superselector sass function
Fixes #1091 Fixes #1063 Fixes #823
1 parent 28ee088 commit aa5043d

File tree

7 files changed

+108
-6
lines changed

7 files changed

+108
-6
lines changed

ast.cpp

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,23 @@ namespace Sass {
242242
if (!lbase) // no lbase; just see if the left-hand qualifiers are a subset of the right-hand selector
243243
{
244244
for (size_t i = 0, L = length(); i < L; ++i)
245-
{ lset.insert((*this)[i]->perform(&to_string)); }
245+
{
246+
Selector* lhs = (*this)[i];
247+
if (Wrapped_Selector* wrapped = dynamic_cast<Wrapped_Selector*>(lhs)) {
248+
if (
249+
wrapped->name() == ":matches(" ||
250+
wrapped->name() == ":-moz-any("
251+
) {
252+
lhs = wrapped->selector();
253+
if (Selector_List* list = dynamic_cast<Selector_List*>(lhs)) {
254+
if (Compound_Selector* comp = dynamic_cast<Compound_Selector*>(rhs)) {
255+
if (list->is_superselector_of(comp)) return true;
256+
}
257+
}
258+
}
259+
}
260+
lset.insert(lhs->perform(&to_string));
261+
}
246262
for (size_t i = 0, L = rhs->length(); i < L; ++i)
247263
{ rset.insert((*rhs)[i]->perform(&to_string)); }
248264
return includes(rset.begin(), rset.end(), lset.begin(), lset.end());
@@ -326,8 +342,6 @@ namespace Sass {
326342

327343
bool Complex_Selector::is_superselector_of(Compound_Selector* rhs)
328344
{
329-
if (length() != 1)
330-
{ return false; }
331345
return base()->is_superselector_of(rhs);
332346
}
333347

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

369+
// we have to look one tail deeper, since we cary the
370+
// combinator around for it (which is important here)
371+
if (rhs->tail() && lhs->tail() && combinator() != Complex_Selector::ANCESTOR_OF) {
372+
Complex_Selector* lhs_tail = lhs->tail();
373+
Complex_Selector* rhs_tail = rhs->tail();
374+
if (lhs_tail->combinator() != rhs_tail->combinator()) return false;
375+
if (!lhs_tail->head()->is_superselector_of(rhs_tail->head())) return false;
376+
}
377+
378+
355379
bool found = false;
356380
Complex_Selector* marker = rhs;
357381
for (size_t i = 0, L = rhs->length(); i < L; ++i) {
@@ -493,6 +517,50 @@ namespace Sass {
493517
#endif
494518
}
495519

520+
// it's a superselector if every selector of the right side
521+
// list is a superselector of the given left side selector
522+
bool Complex_Selector::is_superselector_of(Selector_List *sub)
523+
{
524+
// Check every rhs selector against left hand list
525+
for(size_t i = 0, L = sub->length(); i < L; ++i) {
526+
if (!is_superselector_of((*sub)[i])) return false;
527+
}
528+
return true;
529+
}
530+
531+
// it's a superselector if every selector of the right side
532+
// list is a superselector of the given left side selector
533+
bool Selector_List::is_superselector_of(Selector_List *sub)
534+
{
535+
// Check every rhs selector against left hand list
536+
for(size_t i = 0, L = sub->length(); i < L; ++i) {
537+
if (!is_superselector_of((*sub)[i])) return false;
538+
}
539+
return true;
540+
}
541+
542+
// it's a superselector if every selector on the right side
543+
// is a superselector of any one of the left side selectors
544+
bool Selector_List::is_superselector_of(Compound_Selector *sub)
545+
{
546+
// Check every lhs selector against right hand
547+
for(size_t i = 0, L = length(); i < L; ++i) {
548+
if ((*this)[i]->is_superselector_of(sub)) return true;
549+
}
550+
return false;
551+
}
552+
553+
// it's a superselector if every selector on the right side
554+
// is a superselector of any one of the left side selectors
555+
bool Selector_List::is_superselector_of(Complex_Selector *sub)
556+
{
557+
// Check every lhs selector against right hand
558+
for(size_t i = 0, L = length(); i < L; ++i) {
559+
if ((*this)[i]->is_superselector_of(sub)) return true;
560+
}
561+
return false;
562+
}
563+
496564
/* not used anymore - remove?
497565
Selector_Placeholder* Selector_List::find_placeholder()
498566
{

ast.hpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1943,7 +1943,9 @@ namespace Sass {
19431943
return (*this)[0];
19441944
return 0;
19451945
}
1946-
bool is_superselector_of(Compound_Selector* rhs);
1946+
bool is_superselector_of(Compound_Selector* sub);
1947+
// bool is_superselector_of(Complex_Selector* sub);
1948+
// bool is_superselector_of(Selector_List* sub);
19471949
virtual unsigned long specificity()
19481950
{
19491951
int sum = 0;
@@ -2000,8 +2002,9 @@ namespace Sass {
20002002
Complex_Selector* context(Context&);
20012003
Complex_Selector* innermost();
20022004
size_t length();
2003-
bool is_superselector_of(Compound_Selector*);
2004-
bool is_superselector_of(Complex_Selector*);
2005+
bool is_superselector_of(Compound_Selector* sub);
2006+
bool is_superselector_of(Complex_Selector* sub);
2007+
bool is_superselector_of(Selector_List* sub);
20052008
// virtual Selector_Placeholder* find_placeholder();
20062009
Combinator clear_innermost();
20072010
void set_innermost(Complex_Selector*, Combinator);
@@ -2086,6 +2089,9 @@ namespace Sass {
20862089
: Selector(pstate), Vectorized<Complex_Selector*>(s), wspace_(0)
20872090
{ }
20882091
// virtual Selector_Placeholder* find_placeholder();
2092+
bool is_superselector_of(Compound_Selector* sub);
2093+
bool is_superselector_of(Complex_Selector* sub);
2094+
bool is_superselector_of(Selector_List* sub);
20892095
virtual unsigned long specificity()
20902096
{
20912097
unsigned long sum = 0;

context.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,8 @@ namespace Sass {
539539
// Misc Functions
540540
register_function(ctx, inspect_sig, inspect, env);
541541
register_function(ctx, unique_id_sig, unique_id, env);
542+
// Selector functions
543+
register_function(ctx, is_superselector_sig, is_superselector, env);
542544
}
543545

544546
void register_c_functions(Context& ctx, Env* env, Sass_Function_List descrs)

functions.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1564,6 +1564,20 @@ namespace Sass {
15641564
// return v;
15651565
}
15661566

1567+
Signature is_superselector_sig = "is-superselector($super, $sub)";
1568+
BUILT_IN(is_superselector)
1569+
{
1570+
To_String to_string(&ctx, false);
1571+
Expression* ex_sup = ARG("$super", Expression);
1572+
Expression* ex_sub = ARG("$sub", Expression);
1573+
string sup_src = ex_sup->perform(&to_string) + "{";
1574+
string sub_src = ex_sub->perform(&to_string) + "{";
1575+
Selector_List* sel_sup = Parser::parse_selector(sup_src.c_str(), ctx);
1576+
Selector_List* sel_sub = Parser::parse_selector(sub_src.c_str(), ctx);
1577+
bool result = sel_sup->is_superselector_of(sel_sub);
1578+
return new (ctx.mem) Boolean(pstate, result);
1579+
}
1580+
15671581
Signature unique_id_sig = "unique-id()";
15681582
BUILT_IN(unique_id)
15691583
{

functions.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ namespace Sass {
101101
extern Signature keywords_sig;
102102
extern Signature set_nth_sig;
103103
extern Signature unique_id_sig;
104+
extern Signature is_superselector_sig;
104105

105106
BUILT_IN(rgb);
106107
BUILT_IN(rgba_4);
@@ -175,6 +176,7 @@ namespace Sass {
175176
BUILT_IN(keywords);
176177
BUILT_IN(set_nth);
177178
BUILT_IN(unique_id);
179+
BUILT_IN(is_superselector);
178180

179181
}
180182
}

parser.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ namespace Sass {
4040
return p;
4141
}
4242

43+
Selector_List* Parser::parse_selector(const char* src, Context& ctx, ParserState pstate)
44+
{
45+
Parser p = Parser::from_c_str(src, ctx, pstate);
46+
// ToDo: ruby sass errors on parent references
47+
// ToDo: remap the source-map entries somehow
48+
return p.parse_selector_group();
49+
}
50+
4351
bool Parser::peek_newline(const char* start)
4452
{
4553
return peek_linefeed(start ? start : position);

parser.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ namespace Sass {
5757
static Parser from_c_str(const char* src, Context& ctx, ParserState pstate = ParserState("[CSTRING]"));
5858
static Parser from_c_str(const char* beg, const char* end, Context& ctx, ParserState pstate = ParserState("[CSTRING]"));
5959
static Parser from_token(Token t, Context& ctx, ParserState pstate = ParserState("[TOKEN]"));
60+
// special static parsers to convert strings into certain selectors
61+
static Selector_List* parse_selector(const char* src, Context& ctx, ParserState pstate = ParserState("[SELECTOR]"));
6062

6163
#ifdef __clang__
6264

0 commit comments

Comments
 (0)