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
19namespace Sass {
20
21 Inspect::Inspect(const Emitter& emi)
22 : Emitter(emi)
23 { }
24 Inspect::~Inspect() { }
25
26 // statements
27 void Inspect::operator()(Block* block)
28 {
29 if (!block->is_root()) {
30 add_open_mapping(node: 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(op: this);
36 }
37 if (output_style() == NESTED) indentation -= block->tabs();
38 if (!block->is_root()) {
39 append_scope_closer();
40 add_close_mapping(node: block);
41 }
42
43 }
44
45 void Inspect::operator()(StyleRule* ruleset)
46 {
47 if (ruleset->selector()) {
48 ruleset->selector()->perform(op: this);
49 }
50 if (ruleset->block()) {
51 ruleset->block()->perform(op: this);
52 }
53 }
54
55 void Inspect::operator()(Keyframe_Rule* rule)
56 {
57 if (rule->name()) rule->name()->perform(op: this);
58 if (rule->block()) rule->block()->perform(op: this);
59 }
60
61 void Inspect::operator()(Bubble* bubble)
62 {
63 append_indentation();
64 append_token(text: "::BUBBLE", node: bubble);
65 append_scope_opener();
66 bubble->node()->perform(op: this);
67 append_scope_closer();
68 }
69
70 void Inspect::operator()(MediaRule* rule)
71 {
72 append_indentation();
73 append_token(text: "@media", node: rule);
74 append_mandatory_space();
75 if (rule->block()) {
76 rule->block()->perform(op: this);
77 }
78 }
79
80 void Inspect::operator()(CssMediaRule* rule)
81 {
82 if (output_style() == NESTED)
83 indentation += rule->tabs();
84 append_indentation();
85 append_token(text: "@media", node: 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(op: this);
99 }
100 in_media_block = false;
101 if (output_style() == NESTED)
102 indentation -= rule->tabs();
103 }
104
105 void Inspect::operator()(CssMediaQuery* query)
106 {
107 bool joinIt = false;
108 if (!query->modifier().empty()) {
109 append_string(text: query->modifier());
110 append_mandatory_space();
111 }
112 if (!query->type().empty()) {
113 append_string(text: query->type());
114 joinIt = true;
115 }
116 for (auto feature : query->features()) {
117 if (joinIt) {
118 append_mandatory_space();
119 append_string(text: "and");
120 append_mandatory_space();
121 }
122 append_string(text: feature);
123 joinIt = true;
124 }
125 }
126
127 void Inspect::operator()(SupportsRule* feature_block)
128 {
129 append_indentation();
130 append_token(text: "@supports", node: feature_block);
131 append_mandatory_space();
132 feature_block->condition()->perform(op: this);
133 feature_block->block()->perform(op: this);
134 }
135
136 void Inspect::operator()(AtRootRule* at_root_block)
137 {
138 append_indentation();
139 append_token(text: "@at-root ", node: at_root_block);
140 append_mandatory_space();
141 if(at_root_block->expression()) at_root_block->expression()->perform(op: this);
142 if(at_root_block->block()) at_root_block->block()->perform(op: this);
143 }
144
145 void Inspect::operator()(AtRule* at_rule)
146 {
147 append_indentation();
148 append_token(text: at_rule->keyword(), node: 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(op: this);
154 in_wrapped = was_wrapped;
155 }
156 if (at_rule->value()) {
157 append_mandatory_space();
158 at_rule->value()->perform(op: this);
159 }
160 if (at_rule->block()) {
161 at_rule->block()->perform(op: this);
162 }
163 else {
164 append_delimiter();
165 }
166 }
167
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(op: this);
180 append_colon_separator();
181
182 if (dec->value()->concrete_type() == Expression::SELECTOR) {
183 ExpressionObj ls = Listize::perform(node: dec->value());
184 ls->perform(op: this);
185 } else {
186 dec->value()->perform(op: this);
187 }
188
189 if (dec->is_important()) {
190 append_optional_space();
191 append_string(text: "!important");
192 }
193 append_delimiter();
194 if (output_style() == NESTED)
195 indentation -= dec->tabs();
196 in_declaration = was_decl;
197 }
198
199 void Inspect::operator()(Assignment* assn)
200 {
201 append_token(text: assn->variable(), node: assn);
202 append_colon_separator();
203 assn->value()->perform(op: this);
204 if (assn->is_default()) {
205 append_optional_space();
206 append_string(text: "!default");
207 }
208 append_delimiter();
209 }
210
211 void Inspect::operator()(Import* import)
212 {
213 if (!import->urls().empty()) {
214 append_token(text: "@import", node: import);
215 append_mandatory_space();
216
217 import->urls().front()->perform(op: this);
218 if (import->urls().size() == 1) {
219 if (import->import_queries()) {
220 append_mandatory_space();
221 import->import_queries()->perform(op: 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(text: "@import", node: import);
228 append_mandatory_space();
229
230 import->urls()[i]->perform(op: this);
231 if (import->urls().size() - 1 == i) {
232 if (import->import_queries()) {
233 append_mandatory_space();
234 import->import_queries()->perform(op: this);
235 }
236 }
237 append_delimiter();
238 }
239 }
240 }
241
242 void Inspect::operator()(Import_Stub* import)
243 {
244 append_indentation();
245 append_token(text: "@import", node: import);
246 append_mandatory_space();
247 append_string(text: import->imp_path());
248 append_delimiter();
249 }
250
251 void Inspect::operator()(WarningRule* warning)
252 {
253 append_indentation();
254 append_token(text: "@warn", node: warning);
255 append_mandatory_space();
256 warning->message()->perform(op: this);
257 append_delimiter();
258 }
259
260 void Inspect::operator()(ErrorRule* error)
261 {
262 append_indentation();
263 append_token(text: "@error", node: error);
264 append_mandatory_space();
265 error->message()->perform(op: this);
266 append_delimiter();
267 }
268
269 void Inspect::operator()(DebugRule* debug)
270 {
271 append_indentation();
272 append_token(text: "@debug", node: debug);
273 append_mandatory_space();
274 debug->value()->perform(op: this);
275 append_delimiter();
276 }
277
278 void Inspect::operator()(Comment* comment)
279 {
280 in_comment = true;
281 comment->text()->perform(op: this);
282 in_comment = false;
283 }
284
285 void Inspect::operator()(If* cond)
286 {
287 append_indentation();
288 append_token(text: "@if", node: cond);
289 append_mandatory_space();
290 cond->predicate()->perform(op: this);
291 cond->block()->perform(op: this);
292 if (cond->alternative()) {
293 append_optional_linefeed();
294 append_indentation();
295 append_string(text: "else");
296 cond->alternative()->perform(op: this);
297 }
298 }
299
300 void Inspect::operator()(ForRule* loop)
301 {
302 append_indentation();
303 append_token(text: "@for", node: loop);
304 append_mandatory_space();
305 append_string(text: loop->variable());
306 append_string(text: " from ");
307 loop->lower_bound()->perform(op: this);
308 append_string(text: loop->is_inclusive() ? " through " : " to ");
309 loop->upper_bound()->perform(op: this);
310 loop->block()->perform(op: this);
311 }
312
313 void Inspect::operator()(EachRule* loop)
314 {
315 append_indentation();
316 append_token(text: "@each", node: loop);
317 append_mandatory_space();
318 append_string(text: loop->variables()[0]);
319 for (size_t i = 1, L = loop->variables().size(); i < L; ++i) {
320 append_comma_separator();
321 append_string(text: loop->variables()[i]);
322 }
323 append_string(text: " in ");
324 loop->list()->perform(op: this);
325 loop->block()->perform(op: this);
326 }
327
328 void Inspect::operator()(WhileRule* loop)
329 {
330 append_indentation();
331 append_token(text: "@while", node: loop);
332 append_mandatory_space();
333 loop->predicate()->perform(op: this);
334 loop->block()->perform(op: this);
335 }
336
337 void Inspect::operator()(Return* ret)
338 {
339 append_indentation();
340 append_token(text: "@return", node: ret);
341 append_mandatory_space();
342 ret->value()->perform(op: this);
343 append_delimiter();
344 }
345
346 void Inspect::operator()(ExtendRule* extend)
347 {
348 append_indentation();
349 append_token(text: "@extend", node: extend);
350 append_mandatory_space();
351 extend->selector()->perform(op: this);
352 append_delimiter();
353 }
354
355 void Inspect::operator()(Definition* def)
356 {
357 append_indentation();
358 if (def->type() == Definition::MIXIN) {
359 append_token(text: "@mixin", node: def);
360 append_mandatory_space();
361 } else {
362 append_token(text: "@function", node: def);
363 append_mandatory_space();
364 }
365 append_string(text: def->name());
366 def->parameters()->perform(op: this);
367 def->block()->perform(op: this);
368 }
369
370 void Inspect::operator()(Mixin_Call* call)
371 {
372 append_indentation();
373 append_token(text: "@include", node: call);
374 append_mandatory_space();
375 append_string(text: call->name());
376 if (call->arguments()) {
377 call->arguments()->perform(op: this);
378 }
379 if (call->block()) {
380 append_optional_space();
381 call->block()->perform(op: this);
382 }
383 if (!call->block()) append_delimiter();
384 }
385
386 void Inspect::operator()(Content* content)
387 {
388 append_indentation();
389 append_token(text: "@content", node: content);
390 append_delimiter();
391 }
392
393 void Inspect::operator()(Map* map)
394 {
395 if (output_style() == TO_SASS && map->empty()) {
396 append_string(text: "()");
397 return;
398 }
399 if (map->empty()) return;
400 if (map->is_invisible()) return;
401 bool items_output = false;
402 append_string(text: "(");
403 for (auto key : map->keys()) {
404 if (items_output) append_comma_separator();
405 key->perform(op: this);
406 append_colon_separator();
407 LOCAL_FLAG(in_space_array, true);
408 LOCAL_FLAG(in_comma_array, true);
409 map->at(k: key)->perform(op: this);
410 items_output = true;
411 }
412 append_string(text: ")");
413 }
414
415 sass::string Inspect::lbracket(List* list) {
416 return list->is_bracketed() ? "[" : "(";
417 }
418
419 sass::string Inspect::rbracket(List* list) {
420 return list->is_bracketed() ? "]" : ")";
421 }
422
423 void Inspect::operator()(List* list)
424 {
425 if (list->empty() && (output_style() == TO_SASS || list->is_bracketed())) {
426 append_string(text: lbracket(list));
427 append_string(text: 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(text: lbracket(list));
441 }
442 // probably ruby sass eqivalent of element_needs_parens
443 else if (output_style() == TO_SASS &&
444 list->length() == 1 &&
445 !list->from_selector() &&
446 !Cast<List>(ptr: list->at(i: 0)) &&
447 !Cast<SelectorList>(ptr: list->at(i: 0))
448 ) {
449 append_string(text: 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(text: 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>(ptr: list_item)) {
469 continue;
470 }
471 }
472 }
473 if (items_output) {
474 append_string(text: sep);
475 }
476 if (items_output && sep != " ")
477 append_optional_space();
478 list_item->perform(op: 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(text: ",");
489 }
490 append_string(text: rbracket(list));
491 }
492 // probably ruby sass eqivalent of element_needs_parens
493 else if (output_style() == TO_SASS &&
494 list->length() == 1 &&
495 !list->from_selector() &&
496 !Cast<List>(ptr: list->at(i: 0)) &&
497 !Cast<SelectorList>(ptr: list->at(i: 0))
498 ) {
499 append_string(text: ",");
500 append_string(text: 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(text: rbracket(list));
507 }
508
509 }
510
511 void Inspect::operator()(Binary_Expression* expr)
512 {
513 expr->left()->perform(op: 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(text: " ");
522 switch (expr->optype()) {
523 case Sass_OP::AND: append_string(text: "&&"); break;
524 case Sass_OP::OR: append_string(text: "||"); break;
525 case Sass_OP::EQ: append_string(text: "=="); break;
526 case Sass_OP::NEQ: append_string(text: "!="); break;
527 case Sass_OP::GT: append_string(text: ">"); break;
528 case Sass_OP::GTE: append_string(text: ">="); break;
529 case Sass_OP::LT: append_string(text: "<"); break;
530 case Sass_OP::LTE: append_string(text: "<="); break;
531 case Sass_OP::ADD: append_string(text: "+"); break;
532 case Sass_OP::SUB: append_string(text: "-"); break;
533 case Sass_OP::MUL: append_string(text: "*"); break;
534 case Sass_OP::DIV: append_string(text: "/"); break;
535 case Sass_OP::MOD: append_string(text: "%"); 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(text: " ");
545 expr->right()->perform(op: this);
546 }
547
548 void Inspect::operator()(Unary_Expression* expr)
549 {
550 if (expr->optype() == Unary_Expression::PLUS) append_string(text: "+");
551 else if (expr->optype() == Unary_Expression::SLASH) append_string(text: "/");
552 else append_string(text: "-");
553 expr->operand()->perform(op: this);
554 }
555
556 void Inspect::operator()(Function_Call* call)
557 {
558 append_token(text: call->name(), node: call);
559 call->arguments()->perform(op: this);
560 }
561
562 void Inspect::operator()(Variable* var)
563 {
564 append_token(text: var->name(), node: var);
565 }
566
567 void Inspect::operator()(Number* n)
568 {
569
570 // reduce units
571 n->reduce();
572
573 sass::ostream ss;
574 ss.precision(prec: 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(pos: s, n: 1);
585 }
586 else break;
587 }
588
589 // delete trailing decimal separator
590 if(res[s] == '.') res.erase(pos: s, n: 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(pos: off, n: 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(text: res, node: n);
617 }
618
619 // helper function for serializing colors
620 template <size_t range>
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
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(val: cap_channel<0xff>(c: c->r()), precision: opt.precision);
640 double g = Sass::round(val: cap_channel<0xff>(c: c->g()), precision: opt.precision);
641 double b = Sass::round(val: cap_channel<0xff>(c: c->b()), precision: opt.precision);
642 double a = cap_channel<1> (c: 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(val: cap_channel<0xff>(c: n->r()), precision: opt.precision);
648 g = Sass::round(val: cap_channel<0xff>(c: n->g()), precision: opt.precision);
649 b = Sass::round(val: cap_channel<0xff>(c: n->b()), precision: opt.precision);
650 a = cap_channel<1> (c: 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(text: hexlet.str(), node: 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(text: ss.str(), node: c);
710
711 }
712
713 void Inspect::operator()(Color_HSLA* c)
714 {
715 Color_RGBA_Obj rgba = c->toRGBA();
716 operator()(c: rgba);
717 }
718
719 void Inspect::operator()(Boolean* b)
720 {
721 // output the final token
722 append_token(text: b->value() ? "true" : "false", node: b);
723 }
724
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(text: "#{");
731 (*ss)[i]->perform(op: this);
732 if ((*ss)[i]->is_interpolant()) append_string(text: "}");
733 }
734 }
735
736 void Inspect::operator()(String_Constant* s)
737 {
738 append_token(text: s->value(), node: s);
739 }
740
741 void Inspect::operator()(String_Quoted* s)
742 {
743 if (const char q = s->quote_mark()) {
744 append_token(text: quote(s->value(), q), node: s);
745 } else {
746 append_token(text: s->value(), node: s);
747 }
748 }
749
750 void Inspect::operator()(Custom_Error* e)
751 {
752 append_token(text: e->message(), node: e);
753 }
754
755 void Inspect::operator()(Custom_Warning* w)
756 {
757 append_token(text: w->message(), node: w);
758 }
759
760 void Inspect::operator()(SupportsOperation* so)
761 {
762
763 if (so->needs_parens(cond: so->left())) append_string(text: "(");
764 so->left()->perform(op: this);
765 if (so->needs_parens(cond: so->left())) append_string(text: ")");
766
767 if (so->operand() == SupportsOperation::AND) {
768 append_mandatory_space();
769 append_token(text: "and", node: so);
770 append_mandatory_space();
771 } else if (so->operand() == SupportsOperation::OR) {
772 append_mandatory_space();
773 append_token(text: "or", node: so);
774 append_mandatory_space();
775 }
776
777 if (so->needs_parens(cond: so->right())) append_string(text: "(");
778 so->right()->perform(op: this);
779 if (so->needs_parens(cond: so->right())) append_string(text: ")");
780 }
781
782 void Inspect::operator()(SupportsNegation* sn)
783 {
784 append_token(text: "not", node: sn);
785 append_mandatory_space();
786 if (sn->needs_parens(cond: sn->condition())) append_string(text: "(");
787 sn->condition()->perform(op: this);
788 if (sn->needs_parens(cond: sn->condition())) append_string(text: ")");
789 }
790
791 void Inspect::operator()(SupportsDeclaration* sd)
792 {
793 append_string(text: "(");
794 sd->feature()->perform(op: this);
795 append_string(text: ": ");
796 sd->value()->perform(op: this);
797 append_string(text: ")");
798 }
799
800 void Inspect::operator()(Supports_Interpolation* sd)
801 {
802 sd->value()->perform(op: this);
803 }
804
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(text: "not ");
810 else if (mq->is_restricted()) append_string(text: "only ");
811 mq->media_type()->perform(op: this);
812 }
813 else {
814 (*mq)[i++]->perform(op: this);
815 }
816 for (size_t L = mq->length(); i < L; ++i) {
817 append_string(text: " and ");
818 (*mq)[i]->perform(op: this);
819 }
820 }
821
822 void Inspect::operator()(Media_Query_Expression* mqe)
823 {
824 if (mqe->is_interpolated()) {
825 mqe->feature()->perform(op: this);
826 }
827 else {
828 append_string(text: "(");
829 mqe->feature()->perform(op: this);
830 if (mqe->value()) {
831 append_string(text: ": "); // verified
832 mqe->value()->perform(op: this);
833 }
834 append_string(text: ")");
835 }
836 }
837
838 void Inspect::operator()(At_Root_Query* ae)
839 {
840 if (ae->feature()) {
841 append_string(text: "(");
842 ae->feature()->perform(op: this);
843 if (ae->value()) {
844 append_colon_separator();
845 ae->value()->perform(op: this);
846 }
847 append_string(text: ")");
848 }
849 }
850
851 void Inspect::operator()(Function* f)
852 {
853 append_token(text: "get-function", node: f);
854 append_string(text: "(");
855 append_string(text: quote(f->name()));
856 append_string(text: ")");
857 }
858
859 void Inspect::operator()(Null* n)
860 {
861 // output the final token
862 append_token(text: "null", node: n);
863 }
864
865 // parameters and arguments
866 void Inspect::operator()(Parameter* p)
867 {
868 append_token(text: p->name(), node: p);
869 if (p->default_value()) {
870 append_colon_separator();
871 p->default_value()->perform(op: this);
872 }
873 else if (p->is_rest_parameter()) {
874 append_string(text: "...");
875 }
876 }
877
878 void Inspect::operator()(Parameters* p)
879 {
880 append_string(text: "(");
881 if (!p->empty()) {
882 (*p)[0]->perform(op: this);
883 for (size_t i = 1, L = p->length(); i < L; ++i) {
884 append_comma_separator();
885 (*p)[i]->perform(op: this);
886 }
887 }
888 append_string(text: ")");
889 }
890
891 void Inspect::operator()(Argument* a)
892 {
893 if (!a->name().empty()) {
894 append_token(text: a->name(), node: 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>(ptr: a->value());
904 if (s) s->perform(op: this);
905 } else {
906 a->value()->perform(op: this);
907 }
908 if (a->is_rest_argument()) {
909 append_string(text: "...");
910 }
911 }
912
913 void Inspect::operator()(Arguments* a)
914 {
915 append_string(text: "(");
916 if (!a->empty()) {
917 (*a)[0]->perform(op: this);
918 for (size_t i = 1, L = a->length(); i < L; ++i) {
919 append_string(text: ", "); // verified
920 // Sass Bug? append_comma_separator();
921 (*a)[i]->perform(op: this);
922 }
923 }
924 append_string(text: ")");
925 }
926
927 void Inspect::operator()(Selector_Schema* s)
928 {
929 s->contents()->perform(op: this);
930 }
931
932 void Inspect::operator()(Parent_Reference* p)
933 {
934 append_string(text: "&");
935 }
936
937 void Inspect::operator()(PlaceholderSelector* s)
938 {
939 append_token(text: s->name(), node: s);
940
941 }
942
943 void Inspect::operator()(TypeSelector* s)
944 {
945 append_token(text: s->ns_name(), node: s);
946 }
947
948 void Inspect::operator()(ClassSelector* s)
949 {
950 append_token(text: s->ns_name(), node: s);
951 }
952
953 void Inspect::operator()(IDSelector* s)
954 {
955 append_token(text: s->ns_name(), node: s);
956 }
957
958 void Inspect::operator()(AttributeSelector* s)
959 {
960 append_string(text: "[");
961 add_open_mapping(node: s);
962 append_token(text: s->ns_name(), node: s);
963 if (!s->matcher().empty()) {
964 append_string(text: s->matcher());
965 if (s->value() && *s->value()) {
966 s->value()->perform(op: this);
967 }
968 }
969 add_close_mapping(node: s);
970 if (s->modifier() != 0) {
971 append_mandatory_space();
972 append_char(chr: s->modifier());
973 }
974 append_string(text: "]");
975 }
976
977 void Inspect::operator()(PseudoSelector* s)
978 {
979
980 if (s->name() != "") {
981 append_string(text: ":");
982 if (s->isSyntacticElement()) {
983 append_string(text: ":");
984 }
985 append_token(text: s->ns_name(), node: s);
986 if (s->selector() || s->argument()) {
987 bool was = in_wrapped;
988 in_wrapped = true;
989 append_string(text: "(");
990 if (s->argument()) {
991 s->argument()->perform(op: 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(op: this);
1000 }
1001 in_comma_array = was_comma_array;
1002 append_string(text: ")");
1003 in_wrapped = was;
1004 }
1005 }
1006 }
1007
1008 void Inspect::operator()(SelectorList* g)
1009 {
1010
1011 if (g->empty()) {
1012 if (output_style() == TO_SASS) {
1013 append_token(text: "()", node: g);
1014 }
1015 return;
1016 }
1017
1018
1019 bool was_comma_array = in_comma_array;
1020 // probably ruby sass eqivalent of element_needs_parens
1021 if (output_style() == TO_SASS && g->length() == 1 &&
1022 (!Cast<List>(ptr: (*g)[0]) &&
1023 !Cast<SelectorList>(ptr: (*g)[0]))) {
1024 append_string(text: "(");
1025 }
1026 else if (!in_declaration && in_comma_array) {
1027 append_string(text: "(");
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(node: g->at(i)->last());
1038 // add_open_mapping((*g)[i]->last());
1039 (*g)[i]->perform(op: 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 eqivalent of element_needs_parens
1049 if (output_style() == TO_SASS && g->length() == 1 &&
1050 (!Cast<List>(ptr: (*g)[0]) &&
1051 !Cast<SelectorList>(ptr: (*g)[0]))) {
1052 append_string(text: ",)");
1053 }
1054 else if (!in_declaration && in_comma_array) {
1055 append_string(text: ")");
1056 }
1057
1058 }
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(op: this);
1077 prev = item.ptr();
1078 }
1079 }
1080
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 upcasting
1085 if (auto comp = Cast<CompoundSelector>(ptr: sel)) operator()(comp);
1086 if (auto comb = Cast<SelectorCombinator>(ptr: sel)) operator()(comb);
1087 }
1088
1089 void Inspect::operator()(CompoundSelector* sel)
1090 {
1091 if (sel->hasRealParent()) {
1092 append_string(text: "&");
1093 }
1094 sel->sortChildren();
1095 for (auto& item : sel->elements()) {
1096 item->perform(op: this);
1097 }
1098 // Add the post line break (from ruby sass)
1099 // Dart sass uses another logic for newlines
1100 if (sel->hasPostLineBreak()) {
1101 if (output_style() != COMPACT) {
1102 append_optional_linefeed();
1103 }
1104 }
1105 }
1106
1107 void Inspect::operator()(SelectorCombinator* sel)
1108 {
1109 append_optional_space();
1110 switch (sel->combinator()) {
1111 case SelectorCombinator::Combinator::CHILD: append_string(text: ">"); break;
1112 case SelectorCombinator::Combinator::GENERAL: append_string(text: "~"); break;
1113 case SelectorCombinator::Combinator::ADJACENT: append_string(text: "+"); break;
1114 }
1115 append_optional_space();
1116 // Add the post line break (from ruby sass)
1117 // Dart sass uses another logic for newlines
1118 if (sel->hasPostLineBreak()) {
1119 if (output_style() != COMPACT) {
1120 // append_optional_linefeed();
1121 }
1122 }
1123 }
1124
1125}
1126

source code of gtk/subprojects/libsass/src/inspect.cpp