Skip to content

Commit 1eefb23

Browse files
committed
Merge pull request #1465 from mgreter/refactor/parentize
Add refactoring and fix for `parentize` method
2 parents 0ae11a4 + a8b0a80 commit 1eefb23

File tree

7 files changed

+182
-256
lines changed

7 files changed

+182
-256
lines changed

src/ast.cpp

Lines changed: 142 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -801,120 +801,170 @@ namespace Sass {
801801
return cpy;
802802
}
803803

804+
// append another complex selector at the end
805+
// check if we need to append some headers
806+
// then we need to check for the combinator
807+
// only then we can safely set the new tail
808+
void Complex_Selector::append(Context& ctx, Complex_Selector* ss)
809+
{
810+
811+
Complex_Selector* t = ss->tail();
812+
Combinator c = ss->combinator();
813+
String* r = ss->reference();
814+
Compound_Selector* h = ss->head();
815+
816+
if (ss->has_line_feed()) has_line_feed(true);
817+
if (ss->has_line_break()) has_line_break(true);
818+
819+
// append old headers
820+
if (h && h->length()) {
821+
if (last()->combinator() != ANCESTOR_OF && c != ANCESTOR_OF) {
822+
error("Invalid parent selector", pstate_);
823+
} else {
824+
*last()->head_ += h;
825+
}
826+
} else {
827+
// std::cerr << "has no or empty head\n";
828+
}
829+
830+
if (last()) {
831+
if (last()->combinator() != ANCESTOR_OF && c != ANCESTOR_OF) {
832+
Complex_Selector* inter = new (ctx.mem) Complex_Selector(pstate());
833+
inter->reference(r);
834+
inter->combinator(c);
835+
inter->tail(t);
836+
last()->tail(inter);
837+
} else {
838+
if (last()->combinator() == ANCESTOR_OF) {
839+
last()->combinator(c);
840+
last()->reference(r);
841+
}
842+
last()->tail(t);
843+
}
844+
}
845+
846+
847+
}
848+
804849
Selector_List* Selector_List::parentize(Selector_List* ps, Context& ctx)
805850
{
806851
Selector_List* ss = new (ctx.mem) Selector_List(pstate());
807852
for (size_t pi = 0, pL = ps->length(); pi < pL; ++pi) {
853+
Selector_List* list = new (ctx.mem) Selector_List(pstate());
854+
*list << (*ps)[pi];
808855
for (size_t si = 0, sL = this->length(); si < sL; ++si) {
809-
*ss << (*this)[si]->parentize((*ps)[pi], ctx);
856+
*ss += (*this)[si]->parentize(list, ctx);
810857
}
811858
}
812-
// return selector
813859
return ss;
814860
}
815861

816-
Selector_List* Selector_List::parentize(Complex_Selector* p, Context& ctx)
862+
Selector_List* Complex_Selector::parentize(Selector_List* parents, Context& ctx)
817863
{
818-
Selector_List* ss = new (ctx.mem) Selector_List(pstate());
819-
for (size_t i = 0, L = this->length(); i < L; ++i) {
820-
*ss << (*this)[i]->parentize(p, ctx);
821-
}
822-
// return selector
823-
return ss;
824-
}
825864

826-
Complex_Selector* Complex_Selector::parentize(Context& ctx)
827-
{
828-
// create a new complex selector to return a processed copy
829-
return this;
830-
Complex_Selector* ss = new (ctx.mem) Complex_Selector(this->pstate());
831-
//ss->has_line_feed(this->has_line_feed());
832-
ss->combinator(this->combinator());
833-
if (this->tail()) {
834-
ss->tail(this->tail()->parentize(ctx));
835-
}
836-
if (Compound_Selector* head = this->head()) {
837-
// now add everything expect parent selectors to head
838-
ss->head(new (ctx.mem) Compound_Selector(head->pstate()));
839-
for (size_t i = 0, L = head->length(); i < L; ++i) {
840-
if (!dynamic_cast<Parent_Selector*>((*head)[i])) {
841-
*ss->head() << (*head)[i];
865+
Complex_Selector* tail = this->tail();
866+
Compound_Selector* head = this->head();
867+
868+
// first parentize the tail (which may return an expanded list)
869+
Selector_List* tails = tail ? tail->parentize(parents, ctx) : 0;
870+
871+
if (head && head->length() > 0) {
872+
873+
// we have a parent selector in a simple compound list
874+
// mix parent complex selector into the compound list
875+
if (dynamic_cast<Parent_Selector*>((*head)[0])) {
876+
if (parents && parents->length()) {
877+
Selector_List* retval = new (ctx.mem) Selector_List(pstate());
878+
if (tails && tails->length() > 0) {
879+
for (size_t n = 0, nL = tails->length(); n < nL; ++n) {
880+
for (size_t i = 0, iL = parents->length(); i < iL; ++i) {
881+
Complex_Selector* t = (*tails)[n];
882+
Complex_Selector* parent = (*parents)[i];
883+
Complex_Selector* s = parent->cloneFully(ctx);
884+
Complex_Selector* ss = this->clone(ctx);
885+
ss->tail(t ? t->clone(ctx) : 0);
886+
Compound_Selector* h = head_->clone(ctx);
887+
if (h->length()) h->erase(h->begin());
888+
ss->head(h->length() ? h : 0);
889+
s->append(ctx, ss);
890+
*retval << s;
891+
}
892+
}
893+
}
894+
// have no tails but parents
895+
// loop above is inside out
896+
else {
897+
for (size_t i = 0, iL = parents->length(); i < iL; ++i) {
898+
Complex_Selector* parent = (*parents)[i];
899+
Complex_Selector* s = parent->cloneFully(ctx);
900+
Complex_Selector* ss = this->clone(ctx);
901+
ss->tail(tail ? tail->clone(ctx) : 0);
902+
Compound_Selector* h = head_->clone(ctx);
903+
if (h->length()) h->erase(h->begin());
904+
ss->head(h->length() ? h : 0);
905+
// \/ IMO ruby sass bug \/
906+
ss->has_line_feed(false);
907+
s->append(ctx, ss);
908+
*retval << s;
909+
}
910+
}
911+
return retval;
912+
}
913+
// have no parent but some tails
914+
else {
915+
Selector_List* retval = new (ctx.mem) Selector_List(pstate());
916+
if (tails && tails->length() > 0) {
917+
for (size_t n = 0, nL = tails->length(); n < nL; ++n) {
918+
Complex_Selector* cpy = this->clone(ctx);
919+
cpy->tail((*tails)[n]->cloneFully(ctx));
920+
cpy->head(new (ctx.mem) Compound_Selector(head->pstate()));
921+
for (size_t i = 1, L = this->head()->length(); i < L; ++i)
922+
*cpy->head() << (*this->head())[i];
923+
if (!cpy->head()->length()) cpy->head(0);
924+
*retval << cpy->skip_empty_reference();
925+
}
926+
}
927+
// have no parent and not tails
928+
else {
929+
Complex_Selector* cpy = this->clone(ctx);
930+
cpy->head(new (ctx.mem) Compound_Selector(head->pstate()));
931+
for (size_t i = 1, L = this->head()->length(); i < L; ++i)
932+
*cpy->head() << (*this->head())[i];
933+
if (!cpy->head()->length()) cpy->head(0);
934+
*retval << cpy->skip_empty_reference();
935+
}
936+
return retval;
842937
}
843938
}
844-
// if (ss->head()->empty()) ss->head(0);
845-
}
846-
// return copy
847-
return ss;
848-
}
939+
// no parent selector in head
940+
else {
941+
return this->tails(ctx, tails);
942+
}
849943

850-
Selector_List* Selector_List::parentize(Context& ctx)
851-
{
852-
Selector_List* ss = new (ctx.mem) Selector_List(pstate());
853-
for (size_t i = 0, L = length(); i < L; ++i) {
854-
*ss << (*this)[i]->parentize(ctx);
855944
}
856-
// return selector
857-
return ss;
858-
}
859-
860-
Selector_List* Complex_Selector::parentize(Selector_List* ps, Context& ctx)
861-
{
862-
Selector_List* ss = new (ctx.mem) Selector_List(pstate());
863-
if (ps == 0) { *ss << this->parentize(ctx); return ss; }
864-
for (size_t i = 0, L = ps->length(); i < L; ++i) {
865-
*ss << this->parentize((*ps)[i], ctx);
945+
// has no head
946+
else {
947+
return this->tails(ctx, tails);
866948
}
867-
// return selector
868-
return ss;
949+
950+
// unreachable
951+
return 0;
869952
}
870953

871-
Complex_Selector* Complex_Selector::parentize(Complex_Selector* parent, Context& ctx)
954+
Selector_List* Complex_Selector::tails(Context& ctx, Selector_List* tails)
872955
{
873-
if (!parent) return parentize(ctx);
874-
Complex_Selector* pr = 0;
875-
Compound_Selector* head = this->head();
876-
// create a new complex selector to return a processed copy
877-
Complex_Selector* ss = new (ctx.mem) Complex_Selector(pstate());
878-
// ss->has_line_feed(has_line_feed());
879-
ss->has_line_break(has_line_break());
880-
881-
// Points to last complex selector
882-
// Moved when resolving parent refs
883-
Complex_Selector* cur = ss;
884-
885-
// check if compound selector has exactly one parent reference
886-
// if so we need to connect the parent to the current selector
887-
// then we also need to add the remaining simple selector to the new "parent"
888-
if (head) {
889-
// create a new compound and move originals if needed
890-
// we may add the simple selector to the same selector
891-
// with parent refs we may put them in different places
892-
ss->head(new (ctx.mem) Compound_Selector(head->pstate()));
893-
ss->head()->has_parent_reference(head->has_parent_reference());
894-
ss->head()->has_line_break(head->has_line_break());
895-
// process simple selectors sequence
896-
for (size_t i = 0, L = head->length(); i < L; ++i) {
897-
// we have a parent selector in a simple selector list
898-
// mix parent complex selector into the compound list
899-
if (dynamic_cast<Parent_Selector*>((*head)[i])) {
900-
// clone the parent selector
901-
pr = parent->cloneFully(ctx);
902-
// assign head and tail
903-
cur->head(pr->head());
904-
cur->tail(pr->tail());
905-
// move forward
906-
cur = pr->last();
907-
} else {
908-
// just add simple selector
909-
*cur->head() << (*head)[i];
910-
}
956+
Selector_List* rv = new (ctx.mem) Selector_List(pstate_);
957+
if (tails && tails->length()) {
958+
for (size_t i = 0, iL = tails->length(); i < iL; ++i) {
959+
Complex_Selector* pr = this->clone(ctx);
960+
pr->tail((*tails)[i]);
961+
*rv << pr;
911962
}
912963
}
913-
if (cur->head()) cur->head(cur->head()->length() ? cur->head() : 0);
914-
// parentize and assign trailing complex selector
915-
if (this->tail()) cur->tail(this->tail()->parentize(parent, ctx));
916-
// return selector
917-
return ss;
964+
else {
965+
*rv << this;
966+
}
967+
return rv;
918968
}
919969

920970
// return the last tail that is defined
@@ -1068,11 +1118,6 @@ namespace Sass {
10681118
void Selector_List::adjust_after_pushing(Complex_Selector* c)
10691119
{
10701120
if (c->has_reference()) has_reference(true);
1071-
1072-
#ifdef DEBUG
1073-
To_String to_string;
1074-
this->mCachedSelector(this->perform(&to_string));
1075-
#endif
10761121
}
10771122

10781123
// it's a superselector if every selector of the right side

src/ast.hpp

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,8 @@ namespace Sass {
219219
typename std::vector<T>::iterator begin() { return elements_.begin(); }
220220
typename std::vector<T>::const_iterator end() const { return elements_.end(); }
221221
typename std::vector<T>::const_iterator begin() const { return elements_.begin(); }
222+
typename std::vector<T>::iterator erase(typename std::vector<T>::iterator el) { return elements_.erase(el); }
223+
typename std::vector<T>::const_iterator erase(typename std::vector<T>::const_iterator el) { return elements_.erase(el); }
222224

223225
};
224226
template <typename T>
@@ -2051,6 +2053,7 @@ namespace Sass {
20512053
{ sum += (*this)[i]->specificity(); }
20522054
return sum;
20532055
}
2056+
20542057
bool is_empty_reference()
20552058
{
20562059
return length() == 1 &&
@@ -2095,14 +2098,27 @@ namespace Sass {
20952098
Complex_Selector(ParserState pstate,
20962099
Combinator c = ANCESTOR_OF,
20972100
Compound_Selector* h = 0,
2098-
Complex_Selector* t = 0)
2099-
: Selector(pstate), combinator_(c), head_(h), tail_(t), reference_(0)
2101+
Complex_Selector* t = 0,
2102+
String* r = 0)
2103+
: Selector(pstate), combinator_(c), head_(h), tail_(t), reference_(r)
21002104
{
21012105
if ((h && h->has_reference()) || (t && t->has_reference())) has_reference(true);
21022106
if ((h && h->has_placeholder()) || (t && t->has_placeholder())) has_placeholder(true);
21032107
}
21042108
virtual bool has_parent_ref();
21052109

2110+
Complex_Selector* skip_empty_reference()
2111+
{
2112+
if ((!head_ || !head_->length() || head_->is_empty_reference()) &&
2113+
combinator() == Combinator::ANCESTOR_OF)
2114+
{
2115+
tail_->has_line_feed_ = this->has_line_feed_;
2116+
// tail_->has_line_break_ = this->has_line_break_;
2117+
return tail_ ? tail_->skip_empty_reference() : 0;
2118+
}
2119+
return this;
2120+
}
2121+
21062122
// can still have a tail
21072123
bool is_empty_ancestor() const
21082124
{
@@ -2120,6 +2136,8 @@ namespace Sass {
21202136
// last returns the last real tail
21212137
const Complex_Selector* last() const;
21222138

2139+
Selector_List* tails(Context& ctx, Selector_List* tails);
2140+
21232141
// unconstant accessors
21242142
Complex_Selector* first();
21252143
Complex_Selector* last();
@@ -2129,15 +2147,14 @@ namespace Sass {
21292147
Complex_Selector* innermost() { return last(); };
21302148

21312149
size_t length() const;
2132-
Complex_Selector* parentize(Context& ctx);
21332150
Selector_List* parentize(Selector_List* parents, Context& ctx);
2134-
Complex_Selector* parentize(Complex_Selector* parent, Context& ctx);
21352151
virtual bool is_superselector_of(Compound_Selector* sub, std::string wrapping = "");
21362152
virtual bool is_superselector_of(Complex_Selector* sub, std::string wrapping = "");
21372153
virtual bool is_superselector_of(Selector_List* sub, std::string wrapping = "");
21382154
// virtual Selector_Placeholder* find_placeholder();
21392155
Selector_List* unify_with(Complex_Selector* rhs, Context& ctx);
21402156
Combinator clear_innermost();
2157+
void append(Context&, Complex_Selector*);
21412158
void set_innermost(Complex_Selector*, Combinator);
21422159
virtual unsigned long specificity() const
21432160
{
@@ -2210,9 +2227,6 @@ namespace Sass {
22102227
// Comma-separated selector groups.
22112228
///////////////////////////////////
22122229
class Selector_List : public Selector, public Vectorized<Complex_Selector*> {
2213-
#ifdef DEBUG
2214-
ADD_PROPERTY(std::string, mCachedSelector)
2215-
#endif
22162230
ADD_PROPERTY(std::vector<std::string>, wspace)
22172231
protected:
22182232
void adjust_after_pushing(Complex_Selector* c);
@@ -2224,9 +2238,7 @@ namespace Sass {
22242238
// basically unwraps parsed selectors
22252239
void remove_parent_selectors();
22262240
// virtual Selector_Placeholder* find_placeholder();
2227-
Selector_List* parentize(Context& ctx);
22282241
Selector_List* parentize(Selector_List* parents, Context& ctx);
2229-
Selector_List* parentize(Complex_Selector* parent, Context& ctx);
22302242
virtual bool is_superselector_of(Compound_Selector* sub, std::string wrapping = "");
22312243
virtual bool is_superselector_of(Complex_Selector* sub, std::string wrapping = "");
22322244
virtual bool is_superselector_of(Selector_List* sub, std::string wrapping = "");

0 commit comments

Comments
 (0)