/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 | | } |