1 // sass.hpp must go before all system headers to get the
2 // __EXTENSIONS__ fix on Solaris.
3 #include "sass.hpp"
4 
5 #include <cmath>
6 #include <string>
7 #include <iostream>
8 #include <iomanip>
9 #include <stdint.h>
10 #include <stdint.h>
11 
12 #include "ast.hpp"
13 #include "inspect.hpp"
14 #include "context.hpp"
15 #include "listize.hpp"
16 #include "color_maps.hpp"
17 #include "utf8/checked.h"
18 
19 namespace Sass {
20 
Inspect(const Emitter & emi)21   Inspect::Inspect(const Emitter& emi)
22   : Emitter(emi)
23   { }
~Inspect()24   Inspect::~Inspect() { }
25 
26   // statements
operator ()(Block * block)27   void Inspect::operator()(Block* block)
28   {
29     if (!block->is_root()) {
30       add_open_mapping(block);
31       append_scope_opener();
32     }
33     if (output_style() == NESTED) indentation += block->tabs();
34     for (size_t i = 0, L = block->length(); i < L; ++i) {
35       (*block)[i]->perform(this);
36     }
37     if (output_style() == NESTED) indentation -= block->tabs();
38     if (!block->is_root()) {
39       append_scope_closer();
40       add_close_mapping(block);
41     }
42 
43   }
44 
operator ()(StyleRule * ruleset)45   void Inspect::operator()(StyleRule* ruleset)
46   {
47     if (ruleset->selector()) {
48       ruleset->selector()->perform(this);
49     }
50     if (ruleset->block()) {
51       ruleset->block()->perform(this);
52     }
53   }
54 
operator ()(Keyframe_Rule * rule)55   void Inspect::operator()(Keyframe_Rule* rule)
56   {
57     if (rule->name()) rule->name()->perform(this);
58     if (rule->block()) rule->block()->perform(this);
59   }
60 
operator ()(Bubble * bubble)61   void Inspect::operator()(Bubble* bubble)
62   {
63     append_indentation();
64     append_token("::BUBBLE", bubble);
65     append_scope_opener();
66     bubble->node()->perform(this);
67     append_scope_closer();
68   }
69 
operator ()(MediaRule * rule)70   void Inspect::operator()(MediaRule* rule)
71   {
72     append_indentation();
73     append_token("@media", rule);
74     append_mandatory_space();
75     if (rule->block()) {
76       rule->block()->perform(this);
77     }
78   }
79 
operator ()(CssMediaRule * rule)80   void Inspect::operator()(CssMediaRule* rule)
81   {
82     if (output_style() == NESTED)
83       indentation += rule->tabs();
84     append_indentation();
85     append_token("@media", rule);
86     append_mandatory_space();
87     in_media_block = true;
88     bool joinIt = false;
89     for (auto query : rule->elements()) {
90       if (joinIt) {
91         append_comma_separator();
92         append_optional_space();
93       }
94       operator()(query);
95       joinIt = true;
96     }
97     if (rule->block()) {
98       rule->block()->perform(this);
99     }
100     in_media_block = false;
101     if (output_style() == NESTED)
102       indentation -= rule->tabs();
103   }
104 
operator ()(CssMediaQuery * query)105   void Inspect::operator()(CssMediaQuery* query)
106   {
107     bool joinIt = false;
108     if (!query->modifier().empty()) {
109       append_string(query->modifier());
110       append_mandatory_space();
111     }
112     if (!query->type().empty()) {
113       append_string(query->type());
114       joinIt = true;
115     }
116     for (auto feature : query->features()) {
117       if (joinIt) {
118         append_mandatory_space();
119         append_string("and");
120         append_mandatory_space();
121       }
122       append_string(feature);
123       joinIt = true;
124     }
125   }
126 
operator ()(SupportsRule * feature_block)127   void Inspect::operator()(SupportsRule* feature_block)
128   {
129     append_indentation();
130     append_token("@supports", feature_block);
131     append_mandatory_space();
132     feature_block->condition()->perform(this);
133     feature_block->block()->perform(this);
134   }
135 
operator ()(AtRootRule * at_root_block)136   void Inspect::operator()(AtRootRule* at_root_block)
137   {
138     append_indentation();
139     append_token("@at-root ", at_root_block);
140     append_mandatory_space();
141     if(at_root_block->expression()) at_root_block->expression()->perform(this);
142     if(at_root_block->block()) at_root_block->block()->perform(this);
143   }
144 
operator ()(AtRule * at_rule)145   void Inspect::operator()(AtRule* at_rule)
146   {
147     append_indentation();
148     append_token(at_rule->keyword(), at_rule);
149     if (at_rule->selector()) {
150       append_mandatory_space();
151       bool was_wrapped = in_wrapped;
152       in_wrapped = true;
153       at_rule->selector()->perform(this);
154       in_wrapped = was_wrapped;
155     }
156     if (at_rule->value()) {
157       append_mandatory_space();
158       at_rule->value()->perform(this);
159     }
160     if (at_rule->block()) {
161       at_rule->block()->perform(this);
162     }
163     else {
164       append_delimiter();
165     }
166   }
167 
operator ()(Declaration * dec)168   void Inspect::operator()(Declaration* dec)
169   {
170     if (dec->value()->concrete_type() == Expression::NULL_VAL) return;
171     bool was_decl = in_declaration;
172     in_declaration = true;
173     LOCAL_FLAG(in_custom_property, dec->is_custom_property());
174 
175     if (output_style() == NESTED)
176       indentation += dec->tabs();
177     append_indentation();
178     if (dec->property())
179       dec->property()->perform(this);
180     append_colon_separator();
181 
182     if (dec->value()->concrete_type() == Expression::SELECTOR) {
183       ExpressionObj ls = Listize::perform(dec->value());
184       ls->perform(this);
185     } else {
186       dec->value()->perform(this);
187     }
188 
189     if (dec->is_important()) {
190       append_optional_space();
191       append_string("!important");
192     }
193     append_delimiter();
194     if (output_style() == NESTED)
195       indentation -= dec->tabs();
196     in_declaration = was_decl;
197   }
198 
operator ()(Assignment * assn)199   void Inspect::operator()(Assignment* assn)
200   {
201     append_token(assn->variable(), assn);
202     append_colon_separator();
203     assn->value()->perform(this);
204     if (assn->is_default()) {
205       append_optional_space();
206       append_string("!default");
207     }
208     append_delimiter();
209   }
210 
operator ()(Import * import)211   void Inspect::operator()(Import* import)
212   {
213     if (!import->urls().empty()) {
214       append_token("@import", import);
215       append_mandatory_space();
216 
217       import->urls().front()->perform(this);
218       if (import->urls().size() == 1) {
219         if (import->import_queries()) {
220           append_mandatory_space();
221           import->import_queries()->perform(this);
222         }
223       }
224       append_delimiter();
225       for (size_t i = 1, S = import->urls().size(); i < S; ++i) {
226         append_mandatory_linefeed();
227         append_token("@import", import);
228         append_mandatory_space();
229 
230         import->urls()[i]->perform(this);
231         if (import->urls().size() - 1 == i) {
232           if (import->import_queries()) {
233             append_mandatory_space();
234             import->import_queries()->perform(this);
235           }
236         }
237         append_delimiter();
238       }
239     }
240   }
241 
operator ()(Import_Stub * import)242   void Inspect::operator()(Import_Stub* import)
243   {
244     append_indentation();
245     append_token("@import", import);
246     append_mandatory_space();
247     append_string(import->imp_path());
248     append_delimiter();
249   }
250 
operator ()(WarningRule * warning)251   void Inspect::operator()(WarningRule* warning)
252   {
253     append_indentation();
254     append_token("@warn", warning);
255     append_mandatory_space();
256     warning->message()->perform(this);
257     append_delimiter();
258   }
259 
operator ()(ErrorRule * error)260   void Inspect::operator()(ErrorRule* error)
261   {
262     append_indentation();
263     append_token("@error", error);
264     append_mandatory_space();
265     error->message()->perform(this);
266     append_delimiter();
267   }
268 
operator ()(DebugRule * debug)269   void Inspect::operator()(DebugRule* debug)
270   {
271     append_indentation();
272     append_token("@debug", debug);
273     append_mandatory_space();
274     debug->value()->perform(this);
275     append_delimiter();
276   }
277 
operator ()(Comment * comment)278   void Inspect::operator()(Comment* comment)
279   {
280     in_comment = true;
281     comment->text()->perform(this);
282     in_comment = false;
283   }
284 
operator ()(If * cond)285   void Inspect::operator()(If* cond)
286   {
287     append_indentation();
288     append_token("@if", cond);
289     append_mandatory_space();
290     cond->predicate()->perform(this);
291     cond->block()->perform(this);
292     if (cond->alternative()) {
293       append_optional_linefeed();
294       append_indentation();
295       append_string("else");
296       cond->alternative()->perform(this);
297     }
298   }
299 
operator ()(ForRule * loop)300   void Inspect::operator()(ForRule* loop)
301   {
302     append_indentation();
303     append_token("@for", loop);
304     append_mandatory_space();
305     append_string(loop->variable());
306     append_string(" from ");
307     loop->lower_bound()->perform(this);
308     append_string(loop->is_inclusive() ? " through " : " to ");
309     loop->upper_bound()->perform(this);
310     loop->block()->perform(this);
311   }
312 
operator ()(EachRule * loop)313   void Inspect::operator()(EachRule* loop)
314   {
315     append_indentation();
316     append_token("@each", loop);
317     append_mandatory_space();
318     append_string(loop->variables()[0]);
319     for (size_t i = 1, L = loop->variables().size(); i < L; ++i) {
320       append_comma_separator();
321       append_string(loop->variables()[i]);
322     }
323     append_string(" in ");
324     loop->list()->perform(this);
325     loop->block()->perform(this);
326   }
327 
operator ()(WhileRule * loop)328   void Inspect::operator()(WhileRule* loop)
329   {
330     append_indentation();
331     append_token("@while", loop);
332     append_mandatory_space();
333     loop->predicate()->perform(this);
334     loop->block()->perform(this);
335   }
336 
operator ()(Return * ret)337   void Inspect::operator()(Return* ret)
338   {
339     append_indentation();
340     append_token("@return", ret);
341     append_mandatory_space();
342     ret->value()->perform(this);
343     append_delimiter();
344   }
345 
operator ()(ExtendRule * extend)346   void Inspect::operator()(ExtendRule* extend)
347   {
348     append_indentation();
349     append_token("@extend", extend);
350     append_mandatory_space();
351     extend->selector()->perform(this);
352     append_delimiter();
353   }
354 
operator ()(Definition * def)355   void Inspect::operator()(Definition* def)
356   {
357     append_indentation();
358     if (def->type() == Definition::MIXIN) {
359       append_token("@mixin", def);
360       append_mandatory_space();
361     } else {
362       append_token("@function", def);
363       append_mandatory_space();
364     }
365     append_string(def->name());
366     def->parameters()->perform(this);
367     def->block()->perform(this);
368   }
369 
operator ()(Mixin_Call * call)370   void Inspect::operator()(Mixin_Call* call)
371   {
372     append_indentation();
373     append_token("@include", call);
374     append_mandatory_space();
375     append_string(call->name());
376     if (call->arguments()) {
377       call->arguments()->perform(this);
378     }
379     if (call->block()) {
380       append_optional_space();
381       call->block()->perform(this);
382     }
383     if (!call->block()) append_delimiter();
384   }
385 
operator ()(Content * content)386   void Inspect::operator()(Content* content)
387   {
388     append_indentation();
389     append_token("@content", content);
390     append_delimiter();
391   }
392 
operator ()(Map * map)393   void Inspect::operator()(Map* map)
394   {
395     if (output_style() == TO_SASS && map->empty()) {
396       append_string("()");
397       return;
398     }
399     if (map->empty()) return;
400     if (map->is_invisible()) return;
401     bool items_output = false;
402     append_string("(");
403     for (auto key : map->keys()) {
404       if (items_output) append_comma_separator();
405       key->perform(this);
406       append_colon_separator();
407       LOCAL_FLAG(in_space_array, true);
408       LOCAL_FLAG(in_comma_array, true);
409       map->at(key)->perform(this);
410       items_output = true;
411     }
412     append_string(")");
413   }
414 
lbracket(List * list)415   sass::string Inspect::lbracket(List* list) {
416     return list->is_bracketed() ? "[" : "(";
417   }
418 
rbracket(List * list)419   sass::string Inspect::rbracket(List* list) {
420     return list->is_bracketed() ? "]" : ")";
421   }
422 
operator ()(List * list)423   void Inspect::operator()(List* list)
424   {
425     if (list->empty() && (output_style() == TO_SASS || list->is_bracketed())) {
426       append_string(lbracket(list));
427       append_string(rbracket(list));
428       return;
429     }
430     sass::string sep(list->separator() == SASS_SPACE ? " " : ",");
431     if ((output_style() != COMPRESSED) && sep == ",") sep += " ";
432     else if (in_media_block && sep != " ") sep += " "; // verified
433     if (list->empty()) return;
434     bool items_output = false;
435 
436     bool was_space_array = in_space_array;
437     bool was_comma_array = in_comma_array;
438     // if the list is bracketed, always include the left bracket
439     if (list->is_bracketed()) {
440       append_string(lbracket(list));
441     }
442     // probably ruby sass equivalent of element_needs_parens
443     else if (output_style() == TO_SASS &&
444         list->length() == 1 &&
445         !list->from_selector() &&
446         !Cast<List>(list->at(0)) &&
447         !Cast<SelectorList>(list->at(0))
448     ) {
449       append_string(lbracket(list));
450     }
451     else if (!in_declaration && (list->separator() == SASS_HASH ||
452         (list->separator() == SASS_SPACE && in_space_array) ||
453         (list->separator() == SASS_COMMA && in_comma_array)
454     )) {
455       append_string(lbracket(list));
456     }
457 
458     if (list->separator() == SASS_SPACE) in_space_array = true;
459     else if (list->separator() == SASS_COMMA) in_comma_array = true;
460 
461     for (size_t i = 0, L = list->size(); i < L; ++i) {
462       if (list->separator() == SASS_HASH)
463       { sep[0] = i % 2 ? ':' : ','; }
464       ExpressionObj list_item = list->at(i);
465       if (output_style() != TO_SASS) {
466         if (list_item->is_invisible()) {
467           // this fixes an issue with "" in a list
468           if (!Cast<String_Constant>(list_item)) {
469             continue;
470           }
471         }
472       }
473       if (items_output) {
474         append_string(sep);
475       }
476       if (items_output && sep != " ")
477         append_optional_space();
478       list_item->perform(this);
479       items_output = true;
480     }
481 
482     in_comma_array = was_comma_array;
483     in_space_array = was_space_array;
484 
485     // if the list is bracketed, always include the right bracket
486     if (list->is_bracketed()) {
487       if (list->separator() == SASS_COMMA && list->size() == 1) {
488         append_string(",");
489       }
490       append_string(rbracket(list));
491     }
492     // probably ruby sass equivalent of element_needs_parens
493     else if (output_style() == TO_SASS &&
494         list->length() == 1 &&
495         !list->from_selector() &&
496         !Cast<List>(list->at(0)) &&
497         !Cast<SelectorList>(list->at(0))
498     ) {
499       append_string(",");
500       append_string(rbracket(list));
501     }
502     else if (!in_declaration && (list->separator() == SASS_HASH ||
503         (list->separator() == SASS_SPACE && in_space_array) ||
504         (list->separator() == SASS_COMMA && in_comma_array)
505     )) {
506       append_string(rbracket(list));
507     }
508 
509   }
510 
operator ()(Binary_Expression * expr)511   void Inspect::operator()(Binary_Expression* expr)
512   {
513     expr->left()->perform(this);
514     if ( in_media_block ||
515          (output_style() == INSPECT) || (
516           expr->op().ws_before
517           && (!expr->is_interpolant())
518           && (expr->is_left_interpolant() ||
519               expr->is_right_interpolant())
520 
521     )) append_string(" ");
522     switch (expr->optype()) {
523       case Sass_OP::AND: append_string("&&"); break;
524       case Sass_OP::OR:  append_string("||");  break;
525       case Sass_OP::EQ:  append_string("==");  break;
526       case Sass_OP::NEQ: append_string("!=");  break;
527       case Sass_OP::GT:  append_string(">");   break;
528       case Sass_OP::GTE: append_string(">=");  break;
529       case Sass_OP::LT:  append_string("<");   break;
530       case Sass_OP::LTE: append_string("<=");  break;
531       case Sass_OP::ADD: append_string("+");   break;
532       case Sass_OP::SUB: append_string("-");   break;
533       case Sass_OP::MUL: append_string("*");   break;
534       case Sass_OP::DIV: append_string("/"); break;
535       case Sass_OP::MOD: append_string("%");   break;
536       default: break; // shouldn't get here
537     }
538     if ( in_media_block ||
539          (output_style() == INSPECT) || (
540           expr->op().ws_after
541           && (!expr->is_interpolant())
542           && (expr->is_left_interpolant() ||
543               expr->is_right_interpolant())
544     )) append_string(" ");
545     expr->right()->perform(this);
546   }
547 
operator ()(Unary_Expression * expr)548   void Inspect::operator()(Unary_Expression* expr)
549   {
550     if (expr->optype() == Unary_Expression::PLUS)       append_string("+");
551     else if (expr->optype() == Unary_Expression::SLASH) append_string("/");
552     else                                                append_string("-");
553     expr->operand()->perform(this);
554   }
555 
operator ()(Function_Call * call)556   void Inspect::operator()(Function_Call* call)
557   {
558     append_token(call->name(), call);
559     call->arguments()->perform(this);
560   }
561 
operator ()(Variable * var)562   void Inspect::operator()(Variable* var)
563   {
564     append_token(var->name(), var);
565   }
566 
operator ()(Number * n)567   void Inspect::operator()(Number* n)
568   {
569 
570     // reduce units
571     n->reduce();
572 
573     sass::ostream ss;
574     ss.precision(opt.precision);
575     ss << std::fixed << n->value();
576 
577     sass::string res = ss.str();
578     size_t s = res.length();
579 
580     // delete trailing zeros
581     for(s = s - 1; s > 0; --s)
582     {
583         if(res[s] == '0') {
584           res.erase(s, 1);
585         }
586         else break;
587     }
588 
589     // delete trailing decimal separator
590     if(res[s] == '.') res.erase(s, 1);
591 
592     // some final cosmetics
593     if (res == "0.0") res = "0";
594     else if (res == "") res = "0";
595     else if (res == "-0") res = "0";
596     else if (res == "-0.0") res = "0";
597     else if (opt.output_style == COMPRESSED)
598     {
599       if (n->zero()) {
600         // check if handling negative nr
601         size_t off = res[0] == '-' ? 1 : 0;
602         // remove leading zero from floating point in compressed mode
603         if (res[off] == '0' && res[off+1] == '.') res.erase(off, 1);
604       }
605     }
606 
607     // add unit now
608     res += n->unit();
609 
610     if (opt.output_style == TO_CSS && !n->is_valid_css_unit()) {
611       // traces.push_back(Backtrace(nr->pstate()));
612       throw Exception::InvalidValue({}, *n);
613     }
614 
615     // output the final token
616     append_token(res, n);
617   }
618 
619   // helper function for serializing colors
620   template <size_t range>
cap_channel(double c)621   static double cap_channel(double c) {
622     if      (c > range) return range;
623     else if (c < 0)     return 0;
624     else                return c;
625   }
626 
operator ()(Color_RGBA * c)627   void Inspect::operator()(Color_RGBA* c)
628   {
629     // output the final token
630     sass::ostream ss;
631 
632     // original color name
633     // maybe an unknown token
634     sass::string name = c->disp();
635 
636     // resolved color
637     sass::string res_name = name;
638 
639     double r = Sass::round(cap_channel<0xff>(c->r()), opt.precision);
640     double g = Sass::round(cap_channel<0xff>(c->g()), opt.precision);
641     double b = Sass::round(cap_channel<0xff>(c->b()), opt.precision);
642     double a = cap_channel<1>   (c->a());
643 
644     // get color from given name (if one was given at all)
645     if (name != "" && name_to_color(name)) {
646       const Color_RGBA* n = name_to_color(name);
647       r = Sass::round(cap_channel<0xff>(n->r()), opt.precision);
648       g = Sass::round(cap_channel<0xff>(n->g()), opt.precision);
649       b = Sass::round(cap_channel<0xff>(n->b()), opt.precision);
650       a = cap_channel<1>   (n->a());
651     }
652     // otherwise get the possible resolved color name
653     else {
654       double numval = r * 0x10000 + g * 0x100 + b;
655       if (color_to_name(numval))
656         res_name = color_to_name(numval);
657     }
658 
659     sass::ostream hexlet;
660     // dart sass compressed all colors in regular css always
661     // ruby sass and libsass does it only when not delayed
662     // since color math is going to be removed, this can go too
663     bool compressed = opt.output_style == COMPRESSED;
664     hexlet << '#' << std::setw(1) << std::setfill('0');
665     // create a short color hexlet if there is any need for it
666     if (compressed && is_color_doublet(r, g, b) && a == 1) {
667       hexlet << std::hex << std::setw(1) << (static_cast<unsigned long>(r) >> 4);
668       hexlet << std::hex << std::setw(1) << (static_cast<unsigned long>(g) >> 4);
669       hexlet << std::hex << std::setw(1) << (static_cast<unsigned long>(b) >> 4);
670     } else {
671       hexlet << std::hex << std::setw(2) << static_cast<unsigned long>(r);
672       hexlet << std::hex << std::setw(2) << static_cast<unsigned long>(g);
673       hexlet << std::hex << std::setw(2) << static_cast<unsigned long>(b);
674     }
675 
676     if (compressed && !c->is_delayed()) name = "";
677     if (opt.output_style == INSPECT && a >= 1) {
678       append_token(hexlet.str(), c);
679       return;
680     }
681 
682     // retain the originally specified color definition if unchanged
683     if (name != "") {
684       ss << name;
685     }
686     else if (a >= 1) {
687       if (res_name != "") {
688         if (compressed && hexlet.str().size() < res_name.size()) {
689           ss << hexlet.str();
690         } else {
691           ss << res_name;
692         }
693       }
694       else {
695         ss << hexlet.str();
696       }
697     }
698     else {
699       ss << "rgba(";
700       ss << static_cast<unsigned long>(r) << ",";
701       if (!compressed) ss << " ";
702       ss << static_cast<unsigned long>(g) << ",";
703       if (!compressed) ss << " ";
704       ss << static_cast<unsigned long>(b) << ",";
705       if (!compressed) ss << " ";
706       ss << a << ')';
707     }
708 
709     append_token(ss.str(), c);
710 
711   }
712 
operator ()(Color_HSLA * c)713   void Inspect::operator()(Color_HSLA* c)
714   {
715     Color_RGBA_Obj rgba = c->toRGBA();
716     operator()(rgba);
717   }
718 
operator ()(Boolean * b)719   void Inspect::operator()(Boolean* b)
720   {
721     // output the final token
722     append_token(b->value() ? "true" : "false", b);
723   }
724 
operator ()(String_Schema * ss)725   void Inspect::operator()(String_Schema* ss)
726   {
727     // Evaluation should turn these into String_Constants,
728     // so this method is only for inspection purposes.
729     for (size_t i = 0, L = ss->length(); i < L; ++i) {
730       if ((*ss)[i]->is_interpolant()) append_string("#{");
731       (*ss)[i]->perform(this);
732       if ((*ss)[i]->is_interpolant()) append_string("}");
733     }
734   }
735 
operator ()(String_Constant * s)736   void Inspect::operator()(String_Constant* s)
737   {
738     append_token(s->value(), s);
739   }
740 
operator ()(String_Quoted * s)741   void Inspect::operator()(String_Quoted* s)
742   {
743     if (const char q = s->quote_mark()) {
744       append_token(quote(s->value(), q), s);
745     } else {
746       append_token(s->value(), s);
747     }
748   }
749 
operator ()(Custom_Error * e)750   void Inspect::operator()(Custom_Error* e)
751   {
752     append_token(e->message(), e);
753   }
754 
operator ()(Custom_Warning * w)755   void Inspect::operator()(Custom_Warning* w)
756   {
757     append_token(w->message(), w);
758   }
759 
operator ()(SupportsOperation * so)760   void Inspect::operator()(SupportsOperation* so)
761   {
762 
763     if (so->needs_parens(so->left())) append_string("(");
764     so->left()->perform(this);
765     if (so->needs_parens(so->left())) append_string(")");
766 
767     if (so->operand() == SupportsOperation::AND) {
768       append_mandatory_space();
769       append_token("and", so);
770       append_mandatory_space();
771     } else if (so->operand() == SupportsOperation::OR) {
772       append_mandatory_space();
773       append_token("or", so);
774       append_mandatory_space();
775     }
776 
777     if (so->needs_parens(so->right())) append_string("(");
778     so->right()->perform(this);
779     if (so->needs_parens(so->right())) append_string(")");
780   }
781 
operator ()(SupportsNegation * sn)782   void Inspect::operator()(SupportsNegation* sn)
783   {
784     append_token("not", sn);
785     append_mandatory_space();
786     if (sn->needs_parens(sn->condition())) append_string("(");
787     sn->condition()->perform(this);
788     if (sn->needs_parens(sn->condition())) append_string(")");
789   }
790 
operator ()(SupportsDeclaration * sd)791   void Inspect::operator()(SupportsDeclaration* sd)
792   {
793     append_string("(");
794     sd->feature()->perform(this);
795     append_string(": ");
796     sd->value()->perform(this);
797     append_string(")");
798   }
799 
operator ()(Supports_Interpolation * sd)800   void Inspect::operator()(Supports_Interpolation* sd)
801   {
802     sd->value()->perform(this);
803   }
804 
operator ()(Media_Query * mq)805   void Inspect::operator()(Media_Query* mq)
806   {
807     size_t i = 0;
808     if (mq->media_type()) {
809       if      (mq->is_negated())    append_string("not ");
810       else if (mq->is_restricted()) append_string("only ");
811       mq->media_type()->perform(this);
812     }
813     else {
814       (*mq)[i++]->perform(this);
815     }
816     for (size_t L = mq->length(); i < L; ++i) {
817       append_string(" and ");
818       (*mq)[i]->perform(this);
819     }
820   }
821 
operator ()(Media_Query_Expression * mqe)822   void Inspect::operator()(Media_Query_Expression* mqe)
823   {
824     if (mqe->is_interpolated()) {
825       mqe->feature()->perform(this);
826     }
827     else {
828       append_string("(");
829       mqe->feature()->perform(this);
830       if (mqe->value()) {
831         append_string(": "); // verified
832         mqe->value()->perform(this);
833       }
834       append_string(")");
835     }
836   }
837 
operator ()(At_Root_Query * ae)838   void Inspect::operator()(At_Root_Query* ae)
839   {
840     if (ae->feature()) {
841       append_string("(");
842       ae->feature()->perform(this);
843       if (ae->value()) {
844         append_colon_separator();
845         ae->value()->perform(this);
846       }
847       append_string(")");
848     }
849   }
850 
operator ()(Function * f)851   void Inspect::operator()(Function* f)
852   {
853     append_token("get-function", f);
854     append_string("(");
855     append_string(quote(f->name()));
856     append_string(")");
857   }
858 
operator ()(Null * n)859   void Inspect::operator()(Null* n)
860   {
861     // output the final token
862     append_token("null", n);
863   }
864 
865   // parameters and arguments
operator ()(Parameter * p)866   void Inspect::operator()(Parameter* p)
867   {
868     append_token(p->name(), p);
869     if (p->default_value()) {
870       append_colon_separator();
871       p->default_value()->perform(this);
872     }
873     else if (p->is_rest_parameter()) {
874       append_string("...");
875     }
876   }
877 
operator ()(Parameters * p)878   void Inspect::operator()(Parameters* p)
879   {
880     append_string("(");
881     if (!p->empty()) {
882       (*p)[0]->perform(this);
883       for (size_t i = 1, L = p->length(); i < L; ++i) {
884         append_comma_separator();
885         (*p)[i]->perform(this);
886       }
887     }
888     append_string(")");
889   }
890 
operator ()(Argument * a)891   void Inspect::operator()(Argument* a)
892   {
893     if (!a->name().empty()) {
894       append_token(a->name(), a);
895       append_colon_separator();
896     }
897     if (!a->value()) return;
898     // Special case: argument nulls can be ignored
899     if (a->value()->concrete_type() == Expression::NULL_VAL) {
900       return;
901     }
902     if (a->value()->concrete_type() == Expression::STRING) {
903       String_Constant* s = Cast<String_Constant>(a->value());
904       if (s) s->perform(this);
905     } else {
906       a->value()->perform(this);
907     }
908     if (a->is_rest_argument()) {
909       append_string("...");
910     }
911   }
912 
operator ()(Arguments * a)913   void Inspect::operator()(Arguments* a)
914   {
915     append_string("(");
916     if (!a->empty()) {
917       (*a)[0]->perform(this);
918       for (size_t i = 1, L = a->length(); i < L; ++i) {
919         append_string(", "); // verified
920         // Sass Bug? append_comma_separator();
921         (*a)[i]->perform(this);
922       }
923     }
924     append_string(")");
925   }
926 
operator ()(Selector_Schema * s)927   void Inspect::operator()(Selector_Schema* s)
928   {
929     s->contents()->perform(this);
930   }
931 
operator ()(Parent_Reference * p)932   void Inspect::operator()(Parent_Reference* p)
933   {
934     append_string("&");
935   }
936 
operator ()(PlaceholderSelector * s)937   void Inspect::operator()(PlaceholderSelector* s)
938   {
939     append_token(s->name(), s);
940 
941   }
942 
operator ()(TypeSelector * s)943   void Inspect::operator()(TypeSelector* s)
944   {
945     append_token(s->ns_name(), s);
946   }
947 
operator ()(ClassSelector * s)948   void Inspect::operator()(ClassSelector* s)
949   {
950     append_token(s->ns_name(), s);
951   }
952 
operator ()(IDSelector * s)953   void Inspect::operator()(IDSelector* s)
954   {
955     append_token(s->ns_name(), s);
956   }
957 
operator ()(AttributeSelector * s)958   void Inspect::operator()(AttributeSelector* s)
959   {
960     append_string("[");
961     add_open_mapping(s);
962     append_token(s->ns_name(), s);
963     if (!s->matcher().empty()) {
964       append_string(s->matcher());
965       if (s->value() && *s->value()) {
966         s->value()->perform(this);
967       }
968     }
969     add_close_mapping(s);
970     if (s->modifier() != 0) {
971       append_mandatory_space();
972       append_char(s->modifier());
973     }
974     append_string("]");
975   }
976 
operator ()(PseudoSelector * s)977   void Inspect::operator()(PseudoSelector* s)
978   {
979 
980     if (s->name() != "") {
981       append_string(":");
982       if (s->isSyntacticElement()) {
983         append_string(":");
984       }
985       append_token(s->ns_name(), s);
986       if (s->selector() || s->argument()) {
987         bool was = in_wrapped;
988         in_wrapped = true;
989         append_string("(");
990         if (s->argument()) {
991           s->argument()->perform(this);
992         }
993         if (s->selector() && s->argument()) {
994           append_mandatory_space();
995         }
996         bool was_comma_array = in_comma_array;
997         in_comma_array = false;
998         if (s->selector()) {
999           s->selector()->perform(this);
1000         }
1001         in_comma_array = was_comma_array;
1002         append_string(")");
1003         in_wrapped = was;
1004       }
1005     }
1006   }
1007 
operator ()(SelectorList * g)1008   void Inspect::operator()(SelectorList* g)
1009   {
1010 
1011     if (g->empty()) {
1012       if (output_style() == TO_SASS) {
1013         append_token("()", g);
1014       }
1015       return;
1016     }
1017 
1018 
1019     bool was_comma_array = in_comma_array;
1020     // probably ruby sass equivalent of element_needs_parens
1021     if (output_style() == TO_SASS && g->length() == 1 &&
1022       (!Cast<List>((*g)[0]) &&
1023         !Cast<SelectorList>((*g)[0]))) {
1024       append_string("(");
1025     }
1026     else if (!in_declaration && in_comma_array) {
1027       append_string("(");
1028     }
1029 
1030     if (in_declaration) in_comma_array = true;
1031 
1032     for (size_t i = 0, L = g->length(); i < L; ++i) {
1033 
1034       if (!in_wrapped && i == 0) append_indentation();
1035       if ((*g)[i] == nullptr) continue;
1036       if (g->at(i)->length() == 0) continue;
1037       schedule_mapping(g->at(i)->last());
1038       // add_open_mapping((*g)[i]->last());
1039       (*g)[i]->perform(this);
1040       // add_close_mapping((*g)[i]->last());
1041       if (i < L - 1) {
1042         scheduled_space = 0;
1043         append_comma_separator();
1044       }
1045     }
1046 
1047     in_comma_array = was_comma_array;
1048     // probably ruby sass equivalent of element_needs_parens
1049     if (output_style() == TO_SASS && g->length() == 1 &&
1050       (!Cast<List>((*g)[0]) &&
1051         !Cast<SelectorList>((*g)[0]))) {
1052       append_string(",)");
1053     }
1054     else if (!in_declaration && in_comma_array) {
1055       append_string(")");
1056     }
1057 
1058   }
operator ()(ComplexSelector * sel)1059   void Inspect::operator()(ComplexSelector* sel)
1060   {
1061     if (sel->hasPreLineFeed()) {
1062       append_optional_linefeed();
1063       if (!in_wrapped && output_style() == NESTED) {
1064         append_indentation();
1065       }
1066     }
1067     const SelectorComponent* prev = nullptr;
1068     for (auto& item : sel->elements()) {
1069       if (prev != nullptr) {
1070         if (item->getCombinator() || prev->getCombinator()) {
1071           append_optional_space();
1072         } else {
1073           append_mandatory_space();
1074         }
1075       }
1076       item->perform(this);
1077       prev = item.ptr();
1078     }
1079   }
1080 
operator ()(SelectorComponent * sel)1081   void Inspect::operator()(SelectorComponent* sel)
1082   {
1083     // You should probably never call this method directly
1084     // But in case anyone does, we will do the up-casting
1085     if (auto comp = Cast<CompoundSelector>(sel)) operator()(comp);
1086     if (auto comb = Cast<SelectorCombinator>(sel)) operator()(comb);
1087   }
1088 
operator ()(CompoundSelector * sel)1089   void Inspect::operator()(CompoundSelector* sel)
1090   {
1091     if (sel->hasRealParent()) {
1092       append_string("&");
1093     }
1094     for (auto& item : sel->elements()) {
1095       item->perform(this);
1096     }
1097     // Add the post line break (from ruby sass)
1098     // Dart sass uses another logic for newlines
1099     if (sel->hasPostLineBreak()) {
1100       if (output_style() != COMPACT) {
1101         append_optional_linefeed();
1102       }
1103     }
1104   }
1105 
operator ()(SelectorCombinator * sel)1106   void Inspect::operator()(SelectorCombinator* sel)
1107   {
1108     append_optional_space();
1109     switch (sel->combinator()) {
1110       case SelectorCombinator::Combinator::CHILD: append_string(">"); break;
1111       case SelectorCombinator::Combinator::GENERAL: append_string("~"); break;
1112       case SelectorCombinator::Combinator::ADJACENT: append_string("+"); break;
1113     }
1114     append_optional_space();
1115     // Add the post line break (from ruby sass)
1116     // Dart sass uses another logic for newlines
1117     if (sel->hasPostLineBreak()) {
1118       if (output_style() != COMPACT) {
1119         // append_optional_linefeed();
1120       }
1121     }
1122   }
1123 
1124 }
1125