Coverage Report

Created: 2025-07-11 06:43

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