10 #include "inspect.hpp"
11 #include "context.hpp"
12 #include "listize.hpp"
13 #include "color_maps.hpp"
14 #include "utf8/checked.h"
18 Inspect::Inspect(Emitter emi)
21 Inspect::~Inspect() { }
24 void Inspect::operator()(Block_Ptr block)
26 if (!block->is_root()) {
27 add_open_mapping(block);
28 append_scope_opener();
30 if (output_style() == NESTED) indentation += block->tabs();
31 for (size_t i = 0, L = block->length(); i < L; ++i) {
32 (*block)[i]->perform(this);
34 if (output_style() == NESTED) indentation -= block->tabs();
35 if (!block->is_root()) {
36 append_scope_closer();
37 add_close_mapping(block);
42 void Inspect::operator()(Ruleset_Ptr ruleset)
44 if (ruleset->selector()) {
45 ruleset->selector()->perform(this);
47 if (ruleset->block()) {
48 ruleset->block()->perform(this);
52 void Inspect::operator()(Keyframe_Rule_Ptr rule)
54 if (rule->name()) rule->name()->perform(this);
55 if (rule->block()) rule->block()->perform(this);
58 void Inspect::operator()(Bubble_Ptr bubble)
61 append_token("::BUBBLE", bubble);
62 append_scope_opener();
63 bubble->node()->perform(this);
64 append_scope_closer();
67 void Inspect::operator()(Media_Block_Ptr media_block)
70 append_token("@media", media_block);
71 append_mandatory_space();
72 in_media_block = true;
73 media_block->media_queries()->perform(this);
74 in_media_block = false;
75 media_block->block()->perform(this);
78 void Inspect::operator()(Supports_Block_Ptr feature_block)
81 append_token("@supports", feature_block);
82 append_mandatory_space();
83 feature_block->condition()->perform(this);
84 feature_block->block()->perform(this);
87 void Inspect::operator()(At_Root_Block_Ptr at_root_block)
90 append_token("@at-root ", at_root_block);
91 append_mandatory_space();
92 if(at_root_block->expression()) at_root_block->expression()->perform(this);
93 at_root_block->block()->perform(this);
96 void Inspect::operator()(Directive_Ptr at_rule)
99 append_token(at_rule->keyword(), at_rule);
100 if (at_rule->selector()) {
101 append_mandatory_space();
102 bool was_wrapped = in_wrapped;
104 at_rule->selector()->perform(this);
105 in_wrapped = was_wrapped;
107 if (at_rule->value()) {
108 append_mandatory_space();
109 at_rule->value()->perform(this);
111 if (at_rule->block()) {
112 at_rule->block()->perform(this);
119 void Inspect::operator()(Declaration_Ptr dec)
121 if (dec->value()->concrete_type() == Expression::NULL_VAL) return;
122 bool was_decl = in_declaration;
123 in_declaration = true;
124 if (output_style() == NESTED)
125 indentation += dec->tabs();
126 append_indentation();
128 dec->property()->perform(this);
129 append_colon_separator();
131 if (dec->value()->concrete_type() == Expression::SELECTOR) {
133 Expression_Obj ls = dec->value()->perform(&listize);
136 dec->value()->perform(this);
139 if (dec->is_important()) {
140 append_optional_space();
141 append_string("!important");
144 if (output_style() == NESTED)
145 indentation -= dec->tabs();
146 in_declaration = was_decl;
149 void Inspect::operator()(Assignment_Ptr assn)
151 append_token(assn->variable(), assn);
152 append_colon_separator();
153 assn->value()->perform(this);
154 if (assn->is_default()) {
155 append_optional_space();
156 append_string("!default");
161 void Inspect::operator()(Import_Ptr import)
163 if (!import->urls().empty()) {
164 append_token("@import", import);
165 append_mandatory_space();
167 import->urls().front()->perform(this);
168 if (import->urls().size() == 1) {
169 if (import->import_queries()) {
170 append_mandatory_space();
171 import->import_queries()->perform(this);
175 for (size_t i = 1, S = import->urls().size(); i < S; ++i) {
176 append_mandatory_linefeed();
177 append_token("@import", import);
178 append_mandatory_space();
180 import->urls()[i]->perform(this);
181 if (import->urls().size() - 1 == i) {
182 if (import->import_queries()) {
183 append_mandatory_space();
184 import->import_queries()->perform(this);
192 void Inspect::operator()(Import_Stub_Ptr import)
194 append_indentation();
195 append_token("@import", import);
196 append_mandatory_space();
197 append_string(import->imp_path());
201 void Inspect::operator()(Warning_Ptr warning)
203 append_indentation();
204 append_token("@warn", warning);
205 append_mandatory_space();
206 warning->message()->perform(this);
210 void Inspect::operator()(Error_Ptr error)
212 append_indentation();
213 append_token("@error", error);
214 append_mandatory_space();
215 error->message()->perform(this);
219 void Inspect::operator()(Debug_Ptr debug)
221 append_indentation();
222 append_token("@debug", debug);
223 append_mandatory_space();
224 debug->value()->perform(this);
228 void Inspect::operator()(Comment_Ptr comment)
231 comment->text()->perform(this);
235 void Inspect::operator()(If_Ptr cond)
237 append_indentation();
238 append_token("@if", cond);
239 append_mandatory_space();
240 cond->predicate()->perform(this);
241 cond->block()->perform(this);
242 if (cond->alternative()) {
243 append_optional_linefeed();
244 append_indentation();
245 append_string("else");
246 cond->alternative()->perform(this);
250 void Inspect::operator()(For_Ptr loop)
252 append_indentation();
253 append_token("@for", loop);
254 append_mandatory_space();
255 append_string(loop->variable());
256 append_string(" from ");
257 loop->lower_bound()->perform(this);
258 append_string(loop->is_inclusive() ? " through " : " to ");
259 loop->upper_bound()->perform(this);
260 loop->block()->perform(this);
263 void Inspect::operator()(Each_Ptr loop)
265 append_indentation();
266 append_token("@each", loop);
267 append_mandatory_space();
268 append_string(loop->variables()[0]);
269 for (size_t i = 1, L = loop->variables().size(); i < L; ++i) {
270 append_comma_separator();
271 append_string(loop->variables()[i]);
273 append_string(" in ");
274 loop->list()->perform(this);
275 loop->block()->perform(this);
278 void Inspect::operator()(While_Ptr loop)
280 append_indentation();
281 append_token("@while", loop);
282 append_mandatory_space();
283 loop->predicate()->perform(this);
284 loop->block()->perform(this);
287 void Inspect::operator()(Return_Ptr ret)
289 append_indentation();
290 append_token("@return", ret);
291 append_mandatory_space();
292 ret->value()->perform(this);
296 void Inspect::operator()(Extension_Ptr extend)
298 append_indentation();
299 append_token("@extend", extend);
300 append_mandatory_space();
301 extend->selector()->perform(this);
305 void Inspect::operator()(Definition_Ptr def)
307 append_indentation();
308 if (def->type() == Definition::MIXIN) {
309 append_token("@mixin", def);
310 append_mandatory_space();
312 append_token("@function", def);
313 append_mandatory_space();
315 append_string(def->name());
316 def->parameters()->perform(this);
317 def->block()->perform(this);
320 void Inspect::operator()(Mixin_Call_Ptr call)
322 append_indentation();
323 append_token("@include", call);
324 append_mandatory_space();
325 append_string(call->name());
326 if (call->arguments()) {
327 call->arguments()->perform(this);
330 append_optional_space();
331 call->block()->perform(this);
333 if (!call->block()) append_delimiter();
336 void Inspect::operator()(Content_Ptr content)
338 append_indentation();
339 append_token("@content", content);
343 void Inspect::operator()(Map_Ptr map)
345 if (output_style() == TO_SASS && map->empty()) {
349 if (map->empty()) return;
350 if (map->is_invisible()) return;
351 bool items_output = false;
353 for (auto key : map->keys()) {
354 if (items_output) append_comma_separator();
356 append_colon_separator();
357 map->at(key)->perform(this);
363 std::string Inspect::lbracket(List_Ptr list) {
364 return list->is_bracketed() ? "[" : "(";
367 std::string Inspect::rbracket(List_Ptr list) {
368 return list->is_bracketed() ? "]" : ")";
371 void Inspect::operator()(List_Ptr list)
373 if (list->empty() && (output_style() == TO_SASS || list->is_bracketed())) {
374 append_string(lbracket(list));
375 append_string(rbracket(list));
378 std::string sep(list->separator() == SASS_SPACE ? " " : ",");
379 if ((output_style() != COMPRESSED) && sep == ",") sep += " ";
380 else if (in_media_block && sep != " ") sep += " "; // verified
381 if (list->empty()) return;
382 bool items_output = false;
384 bool was_space_array = in_space_array;
385 bool was_comma_array = in_comma_array;
386 // if the list is bracketed, always include the left bracket
387 if (list->is_bracketed()) {
388 append_string(lbracket(list));
390 // probably ruby sass eqivalent of element_needs_parens
391 else if (output_style() == TO_SASS &&
392 list->length() == 1 &&
393 !list->from_selector() &&
394 !Cast<List>(list->at(0)) &&
395 !Cast<Selector_List>(list->at(0))
397 append_string(lbracket(list));
399 else if (!in_declaration && (list->separator() == SASS_HASH ||
400 (list->separator() == SASS_SPACE && in_space_array) ||
401 (list->separator() == SASS_COMMA && in_comma_array)
403 append_string(lbracket(list));
406 if (list->separator() == SASS_SPACE) in_space_array = true;
407 else if (list->separator() == SASS_COMMA) in_comma_array = true;
409 for (size_t i = 0, L = list->size(); i < L; ++i) {
410 if (list->separator() == SASS_HASH)
411 { sep[0] = i % 2 ? ':' : ','; }
412 Expression_Obj list_item = list->at(i);
413 if (output_style() != TO_SASS) {
414 if (list_item->is_invisible()) {
415 // this fixes an issue with "" in a list
416 if (!Cast<String_Constant>(list_item)) {
424 if (items_output && sep != " ")
425 append_optional_space();
426 list_item->perform(this);
430 in_comma_array = was_comma_array;
431 in_space_array = was_space_array;
433 // if the list is bracketed, always include the right bracket
434 if (list->is_bracketed()) {
435 if (list->separator() == SASS_COMMA && list->size() == 1) {
438 append_string(rbracket(list));
440 // probably ruby sass eqivalent of element_needs_parens
441 else if (output_style() == TO_SASS &&
442 list->length() == 1 &&
443 !list->from_selector() &&
444 !Cast<List>(list->at(0)) &&
445 !Cast<Selector_List>(list->at(0))
448 append_string(rbracket(list));
450 else if (!in_declaration && (list->separator() == SASS_HASH ||
451 (list->separator() == SASS_SPACE && in_space_array) ||
452 (list->separator() == SASS_COMMA && in_comma_array)
454 append_string(rbracket(list));
459 void Inspect::operator()(Binary_Expression_Ptr expr)
461 expr->left()->perform(this);
462 if ( in_media_block ||
463 (output_style() == INSPECT) || (
465 && (!expr->is_interpolant())
466 && (expr->is_left_interpolant() ||
467 expr->is_right_interpolant())
469 )) append_string(" ");
470 switch (expr->optype()) {
471 case Sass_OP::AND: append_string("&&"); break;
472 case Sass_OP::OR: append_string("||"); break;
473 case Sass_OP::EQ: append_string("=="); break;
474 case Sass_OP::NEQ: append_string("!="); break;
475 case Sass_OP::GT: append_string(">"); break;
476 case Sass_OP::GTE: append_string(">="); break;
477 case Sass_OP::LT: append_string("<"); break;
478 case Sass_OP::LTE: append_string("<="); break;
479 case Sass_OP::ADD: append_string("+"); break;
480 case Sass_OP::SUB: append_string("-"); break;
481 case Sass_OP::MUL: append_string("*"); break;
482 case Sass_OP::DIV: append_string("/"); break;
483 case Sass_OP::MOD: append_string("%"); break;
484 default: break; // shouldn't get here
486 if ( in_media_block ||
487 (output_style() == INSPECT) || (
489 && (!expr->is_interpolant())
490 && (expr->is_left_interpolant() ||
491 expr->is_right_interpolant())
492 )) append_string(" ");
493 expr->right()->perform(this);
496 void Inspect::operator()(Unary_Expression_Ptr expr)
498 if (expr->optype() == Unary_Expression::PLUS) append_string("+");
499 else append_string("-");
500 expr->operand()->perform(this);
503 void Inspect::operator()(Function_Call_Ptr call)
505 append_token(call->name(), call);
506 call->arguments()->perform(this);
509 void Inspect::operator()(Function_Call_Schema_Ptr call)
511 call->name()->perform(this);
512 call->arguments()->perform(this);
515 void Inspect::operator()(Variable_Ptr var)
517 append_token(var->name(), var);
520 void Inspect::operator()(Textual_Ptr txt)
522 append_token(txt->value(), txt);
525 void Inspect::operator()(Number_Ptr n)
530 // check if the fractional part of the value equals to zero
531 // neat trick from http://stackoverflow.com/a/1521682/1550314
532 // double int_part; bool is_int = modf(value, &int_part) == 0.0;
534 // this all cannot be done with one run only, since fixed
535 // output differs from normal output and regular output
536 // can contain scientific notation which we do not want!
539 std::stringstream ss;
543 // check if we got scientific notation in result
544 if (ss.str().find_first_of("e") != std::string::npos) {
545 ss.clear(); ss.str(std::string());
546 ss.precision(std::max(12, opt.precision));
547 ss << std::fixed << n->value();
550 std::string tmp = ss.str();
551 size_t pos_point = tmp.find_first_of(".,");
552 size_t pos_fract = tmp.find_last_not_of("0");
553 bool is_int = pos_point == pos_fract ||
554 pos_point == std::string::npos;
556 // reset stream for another run
557 ss.clear(); ss.str(std::string());
559 // take a shortcut for integers
563 ss << std::fixed << n->value();
564 res = std::string(ss.str());
569 // do we have have too much precision?
570 if (pos_fract < opt.precision + pos_point)
571 { ss.precision((int)(pos_fract - pos_point)); }
572 else { ss.precision(opt.precision); }
574 ss << std::fixed << n->value();
575 res = std::string(ss.str());
576 // maybe we truncated up to decimal point
577 size_t pos = res.find_last_not_of("0");
578 // handle case where we have a "0"
579 if (pos == std::string::npos) {
582 bool at_dec_point = res[pos] == '.' ||
584 // don't leave a blank point
585 if (at_dec_point) ++ pos;
586 res.resize (pos + 1);
590 // some final cosmetics
591 if (res == "0.0") res = "0";
592 else if (res == "") res = "0";
593 else if (res == "-0") res = "0";
594 else if (res == "-0.0") res = "0";
595 else if (opt.output_style == COMPRESSED)
597 // check if handling negative nr
598 size_t off = res[0] == '-' ? 1 : 0;
599 // remove leading zero from floating point in compressed mode
600 if (n->zero() && res[off] == '0' && res[off+1] == '.') res.erase(off, 1);
606 // output the final token
607 append_token(res, n);
610 // helper function for serializing colors
611 template <size_t range>
612 static double cap_channel(double c) {
613 if (c > range) return range;
614 else if (c < 0) return 0;
618 void Inspect::operator()(Color_Ptr c)
620 // output the final token
621 std::stringstream ss;
623 // original color name
624 // maybe an unknown token
625 std::string name = c->disp();
628 std::string res_name = name;
630 double r = Sass::round(cap_channel<0xff>(c->r()), opt.precision);
631 double g = Sass::round(cap_channel<0xff>(c->g()), opt.precision);
632 double b = Sass::round(cap_channel<0xff>(c->b()), opt.precision);
633 double a = cap_channel<1> (c->a());
635 // get color from given name (if one was given at all)
636 if (name != "" && name_to_color(name)) {
637 Color_Ptr_Const n = name_to_color(name);
638 r = Sass::round(cap_channel<0xff>(n->r()), opt.precision);
639 g = Sass::round(cap_channel<0xff>(n->g()), opt.precision);
640 b = Sass::round(cap_channel<0xff>(n->b()), opt.precision);
641 a = cap_channel<1> (n->a());
643 // otherwise get the possible resolved color name
645 double numval = r * 0x10000 + g * 0x100 + b;
646 if (color_to_name(numval))
647 res_name = color_to_name(numval);
650 std::stringstream hexlet;
651 bool compressed = opt.output_style == COMPRESSED;
652 hexlet << '#' << std::setw(1) << std::setfill('0');
653 // create a short color hexlet if there is any need for it
654 if (compressed && is_color_doublet(r, g, b) && a == 1) {
655 hexlet << std::hex << std::setw(1) << (static_cast<unsigned long>(r) >> 4);
656 hexlet << std::hex << std::setw(1) << (static_cast<unsigned long>(g) >> 4);
657 hexlet << std::hex << std::setw(1) << (static_cast<unsigned long>(b) >> 4);
659 hexlet << std::hex << std::setw(2) << static_cast<unsigned long>(r);
660 hexlet << std::hex << std::setw(2) << static_cast<unsigned long>(g);
661 hexlet << std::hex << std::setw(2) << static_cast<unsigned long>(b);
664 if (compressed && !c->is_delayed()) name = "";
665 if (opt.output_style == INSPECT && a >= 1) {
666 append_token(hexlet.str(), c);
670 // retain the originally specified color definition if unchanged
674 else if (r == 0 && g == 0 && b == 0 && a == 0) {
678 if (res_name != "") {
679 if (compressed && hexlet.str().size() < res_name.size()) {
691 ss << static_cast<unsigned long>(r) << ",";
692 if (!compressed) ss << " ";
693 ss << static_cast<unsigned long>(g) << ",";
694 if (!compressed) ss << " ";
695 ss << static_cast<unsigned long>(b) << ",";
696 if (!compressed) ss << " ";
700 append_token(ss.str(), c);
704 void Inspect::operator()(Boolean_Ptr b)
706 // output the final token
707 append_token(b->value() ? "true" : "false", b);
710 void Inspect::operator()(String_Schema_Ptr ss)
712 // Evaluation should turn these into String_Constants,
713 // so this method is only for inspection purposes.
714 for (size_t i = 0, L = ss->length(); i < L; ++i) {
715 if ((*ss)[i]->is_interpolant()) append_string("#{");
716 (*ss)[i]->perform(this);
717 if ((*ss)[i]->is_interpolant()) append_string("}");
721 void Inspect::operator()(String_Constant_Ptr s)
723 append_token(s->value(), s);
726 void Inspect::operator()(String_Quoted_Ptr s)
728 if (const char q = s->quote_mark()) {
729 append_token(quote(s->value(), q), s);
731 append_token(s->value(), s);
735 void Inspect::operator()(Custom_Error_Ptr e)
737 append_token(e->message(), e);
740 void Inspect::operator()(Custom_Warning_Ptr w)
742 append_token(w->message(), w);
745 void Inspect::operator()(Supports_Operator_Ptr so)
748 if (so->needs_parens(so->left())) append_string("(");
749 so->left()->perform(this);
750 if (so->needs_parens(so->left())) append_string(")");
752 if (so->operand() == Supports_Operator::AND) {
753 append_mandatory_space();
754 append_token("and", so);
755 append_mandatory_space();
756 } else if (so->operand() == Supports_Operator::OR) {
757 append_mandatory_space();
758 append_token("or", so);
759 append_mandatory_space();
762 if (so->needs_parens(so->right())) append_string("(");
763 so->right()->perform(this);
764 if (so->needs_parens(so->right())) append_string(")");
767 void Inspect::operator()(Supports_Negation_Ptr sn)
769 append_token("not", sn);
770 append_mandatory_space();
771 if (sn->needs_parens(sn->condition())) append_string("(");
772 sn->condition()->perform(this);
773 if (sn->needs_parens(sn->condition())) append_string(")");
776 void Inspect::operator()(Supports_Declaration_Ptr sd)
779 sd->feature()->perform(this);
781 sd->value()->perform(this);
785 void Inspect::operator()(Supports_Interpolation_Ptr sd)
787 sd->value()->perform(this);
790 void Inspect::operator()(Media_Query_Ptr mq)
793 if (mq->media_type()) {
794 if (mq->is_negated()) append_string("not ");
795 else if (mq->is_restricted()) append_string("only ");
796 mq->media_type()->perform(this);
799 (*mq)[i++]->perform(this);
801 for (size_t L = mq->length(); i < L; ++i) {
802 append_string(" and ");
803 (*mq)[i]->perform(this);
807 void Inspect::operator()(Media_Query_Expression_Ptr mqe)
809 if (mqe->is_interpolated()) {
810 mqe->feature()->perform(this);
814 mqe->feature()->perform(this);
816 append_string(": "); // verified
817 mqe->value()->perform(this);
823 void Inspect::operator()(At_Root_Query_Ptr ae)
826 ae->feature()->perform(this);
828 append_colon_separator();
829 ae->value()->perform(this);
834 void Inspect::operator()(Null_Ptr n)
836 // output the final token
837 append_token("null", n);
840 // parameters and arguments
841 void Inspect::operator()(Parameter_Ptr p)
843 append_token(p->name(), p);
844 if (p->default_value()) {
845 append_colon_separator();
846 p->default_value()->perform(this);
848 else if (p->is_rest_parameter()) {
849 append_string("...");
853 void Inspect::operator()(Parameters_Ptr p)
857 (*p)[0]->perform(this);
858 for (size_t i = 1, L = p->length(); i < L; ++i) {
859 append_comma_separator();
860 (*p)[i]->perform(this);
866 void Inspect::operator()(Argument_Ptr a)
868 if (!a->name().empty()) {
869 append_token(a->name(), a);
870 append_colon_separator();
872 if (!a->value()) return;
873 // Special case: argument nulls can be ignored
874 if (a->value()->concrete_type() == Expression::NULL_VAL) {
877 if (a->value()->concrete_type() == Expression::STRING) {
878 String_Constant_Ptr s = Cast<String_Constant>(a->value());
879 if (s) s->perform(this);
881 a->value()->perform(this);
883 if (a->is_rest_argument()) {
884 append_string("...");
888 void Inspect::operator()(Arguments_Ptr a)
892 (*a)[0]->perform(this);
893 for (size_t i = 1, L = a->length(); i < L; ++i) {
894 append_string(", "); // verified
895 // Sass Bug? append_comma_separator();
896 (*a)[i]->perform(this);
902 void Inspect::operator()(Selector_Schema_Ptr s)
904 s->contents()->perform(this);
907 void Inspect::operator()(Parent_Selector_Ptr p)
909 if (p->is_real_parent_ref()) append_string("&");
912 void Inspect::operator()(Placeholder_Selector_Ptr s)
914 append_token(s->name(), s);
915 if (s->has_line_break()) append_optional_linefeed();
916 if (s->has_line_break()) append_indentation();
920 void Inspect::operator()(Element_Selector_Ptr s)
922 append_token(s->ns_name(), s);
925 void Inspect::operator()(Class_Selector_Ptr s)
927 append_token(s->ns_name(), s);
928 if (s->has_line_break()) append_optional_linefeed();
929 if (s->has_line_break()) append_indentation();
932 void Inspect::operator()(Id_Selector_Ptr s)
934 append_token(s->ns_name(), s);
935 if (s->has_line_break()) append_optional_linefeed();
936 if (s->has_line_break()) append_indentation();
939 void Inspect::operator()(Attribute_Selector_Ptr s)
943 append_token(s->ns_name(), s);
944 if (!s->matcher().empty()) {
945 append_string(s->matcher());
946 if (s->value() && *s->value()) {
947 s->value()->perform(this);
950 add_close_mapping(s);
954 void Inspect::operator()(Pseudo_Selector_Ptr s)
956 append_token(s->ns_name(), s);
957 if (s->expression()) {
959 s->expression()->perform(this);
964 void Inspect::operator()(Wrapped_Selector_Ptr s)
966 bool was = in_wrapped;
968 append_token(s->name(), s);
970 bool was_comma_array = in_comma_array;
971 in_comma_array = false;
972 s->selector()->perform(this);
973 in_comma_array = was_comma_array;
978 void Inspect::operator()(Compound_Selector_Ptr s)
980 for (size_t i = 0, L = s->length(); i < L; ++i) {
981 (*s)[i]->perform(this);
983 if (s->has_line_break()) {
984 if (output_style() != COMPACT) {
985 append_optional_linefeed();
990 void Inspect::operator()(Complex_Selector_Ptr c)
992 Compound_Selector_Obj head = c->head();
993 Complex_Selector_Obj tail = c->tail();
994 Complex_Selector::Combinator comb = c->combinator();
996 if (comb == Complex_Selector::ANCESTOR_OF && (!head || head->empty())) {
997 if (tail) tail->perform(this);
1001 if (c->has_line_feed()) {
1002 if (!(c->has_parent_ref())) {
1003 append_optional_linefeed();
1004 append_indentation();
1008 if (head && head->length() != 0) head->perform(this);
1009 bool is_empty = !head || head->length() == 0 || head->is_empty_reference();
1010 bool is_tail = head && !head->is_empty_reference() && tail;
1011 if (output_style() == COMPRESSED && comb != Complex_Selector::ANCESTOR_OF) scheduled_space = 0;
1014 case Complex_Selector::ANCESTOR_OF:
1015 if (is_tail) append_mandatory_space();
1017 case Complex_Selector::PARENT_OF:
1018 append_optional_space();
1020 append_optional_space();
1022 case Complex_Selector::ADJACENT_TO:
1023 append_optional_space();
1025 append_optional_space();
1027 case Complex_Selector::REFERENCE:
1028 append_mandatory_space();
1030 c->reference()->perform(this);
1032 append_mandatory_space();
1034 case Complex_Selector::PRECEDES:
1035 if (is_empty) append_optional_space();
1036 else append_mandatory_space();
1038 if (tail) append_mandatory_space();
1039 else append_optional_space();
1042 if (tail && comb != Complex_Selector::ANCESTOR_OF) {
1043 if (c->has_line_break()) append_optional_linefeed();
1045 if (tail) tail->perform(this);
1046 if (!tail && c->has_line_break()) {
1047 if (output_style() == COMPACT) {
1048 append_mandatory_space();
1053 void Inspect::operator()(Selector_List_Ptr g)
1057 if (output_style() == TO_SASS) {
1058 append_token("()", g);
1064 bool was_comma_array = in_comma_array;
1065 // probably ruby sass eqivalent of element_needs_parens
1066 if (output_style() == TO_SASS && g->length() == 1 &&
1067 (!Cast<List>((*g)[0]) &&
1068 !Cast<Selector_List>((*g)[0]))) {
1071 else if (!in_declaration && in_comma_array) {
1075 if (in_declaration) in_comma_array = true;
1077 for (size_t i = 0, L = g->length(); i < L; ++i) {
1078 if (!in_wrapped && i == 0) append_indentation();
1079 if ((*g)[i] == 0) continue;
1080 schedule_mapping(g->at(i)->last());
1081 // add_open_mapping((*g)[i]->last());
1082 (*g)[i]->perform(this);
1083 // add_close_mapping((*g)[i]->last());
1085 scheduled_space = 0;
1086 append_comma_separator();
1090 in_comma_array = was_comma_array;
1091 // probably ruby sass eqivalent of element_needs_parens
1092 if (output_style() == TO_SASS && g->length() == 1 &&
1093 (!Cast<List>((*g)[0]) &&
1094 !Cast<Selector_List>((*g)[0]))) {
1095 append_string(",)");
1097 else if (!in_declaration && in_comma_array) {
1103 void Inspect::fallback_impl(AST_Node_Ptr n)