Skip to content

Commit

Permalink
Add last missing selector functions [WIP]
Browse files Browse the repository at this point in the history
Work done by @onedayitwillmake in PR 1261 [WIP]
Will be rebased out once that work has been merged
  • Loading branch information
mgreter committed Jun 5, 2015
1 parent 8f00b20 commit 7411b6b
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 10 deletions.
35 changes: 34 additions & 1 deletion ast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Complex_Selector*> 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) {
Expand All @@ -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<Parent_Selector*>(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<string> Compound_Selector::to_str_vec()
{
To_String to_string;
Expand Down
2 changes: 2 additions & 0 deletions ast.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2090,6 +2090,7 @@ namespace Sass {
};

typedef deque<Complex_Selector*> ComplexSelectorDeque;
typedef Subset_Map<string, pair<Complex_Selector*, Compound_Selector*> > ExtensionSubsetMap;

///////////////////////////////////
// Comma-separated selector groups.
Expand All @@ -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 = "");
Expand Down
7 changes: 5 additions & 2 deletions context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
124 changes: 123 additions & 1 deletion functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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<Selector_List*> parsedSelectors;
for (size_t i = 0, L = arglist->length(); i < L; ++i) {
Expression* exp = dynamic_cast<Expression*>(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<Selector_List*>::iterator itr = parsedSelectors.begin();
Selector_List* result = *itr;
++itr;

for(;itr != parsedSelectors.end(); ++itr) {
Selector_List* child = *itr;
vector<Complex_Selector*> 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<Type_Selector*>(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)
Expand All @@ -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);
Expand All @@ -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)
{
Expand Down
6 changes: 6 additions & 0 deletions functions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
7 changes: 1 addition & 6 deletions node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 7411b6b

Please sign in to comment.