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