void Inspect::operator()(Declaration* dec) { if (dec->value()->concrete_type() == Expression::NULL_VAL) return; bool was_decl = in_declaration; in_declaration = true; if (output_style() == NESTED) indentation += dec->tabs(); append_indentation(); dec->property()->perform(this); append_colon_separator(); if (dec->value()->concrete_type() == Expression::SELECTOR) { Listize listize(*ctx); dec->value()->perform(&listize)->perform(this); } else { dec->value()->perform(this); } if (dec->is_important()) { append_optional_space(); append_string("!important"); } append_delimiter(); if (output_style() == NESTED) indentation -= dec->tabs(); in_declaration = was_decl; }
void Output::operator()(Media_Block* m) { if (m->is_invisible()) return; List* q = m->media_queries(); Block* b = m->block(); // Filter out media blocks that aren't printable (process its children though) if (!Util::isPrintable(m, output_style())) { for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; if (dynamic_cast<Has_Block*>(stm)) { stm->perform(this); } } return; } if (output_style() == NESTED) indentation += m->tabs(); append_indentation(); append_token("@media", m); append_mandatory_space(); in_media_block = true; q->perform(this); in_media_block = false; append_scope_opener(); for (size_t i = 0, L = b->length(); i < L; ++i) { if ((*b)[i]) (*b)[i]->perform(this); if (i < L - 1) append_special_linefeed(); } if (output_style() == NESTED) indentation -= m->tabs(); append_scope_closer(); }
void Output::operator()(Supports_Block* f) { if (f->is_invisible()) return; Supports_Condition* c = f->condition(); Block* b = f->block(); // Filter out feature blocks that aren't printable (process its children though) if (!Util::isPrintable(f, output_style())) { for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; if (dynamic_cast<Has_Block*>(stm)) { stm->perform(this); } } return; } if (output_style() == NESTED) indentation += f->tabs(); append_indentation(); append_token("@supports", f); append_mandatory_space(); c->perform(this); append_scope_opener(); if (b->has_non_hoistable()) { // JMA - hoisted, output the non-hoistable in a nested block, followed by the hoistable append_scope_opener(); for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; if (!stm->is_hoistable()) { stm->perform(this); } } append_scope_closer(); for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; if (stm->is_hoistable()) { stm->perform(this); } } } else { // JMA - not hoisted, just output in order for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; stm->perform(this); if (i < L - 1) append_special_linefeed(); } } if (output_style() == NESTED) indentation -= f->tabs(); append_scope_closer(); }
void Inspect::operator()(Complex_Selector* c) { Compound_Selector* head = c->head(); Complex_Selector* tail = c->tail(); Complex_Selector::Combinator comb = c->combinator(); if (c->has_line_feed()) { if (!(c->has_parent_ref())) { append_optional_linefeed(); append_indentation(); } } if (head && head->length() != 0) head->perform(this); bool is_empty = !head || head->length() == 0 || head->is_empty_reference(); bool is_tail = head && !head->is_empty_reference() && tail; if (output_style() == COMPRESSED && comb != Complex_Selector::ANCESTOR_OF) scheduled_space = 0; switch (comb) { case Complex_Selector::ANCESTOR_OF: if (is_tail) append_mandatory_space(); break; case Complex_Selector::PARENT_OF: append_optional_space(); append_string(">"); append_optional_space(); break; case Complex_Selector::ADJACENT_TO: append_optional_space(); append_string("+"); append_optional_space(); break; case Complex_Selector::REFERENCE: append_mandatory_space(); append_string("/"); c->reference()->perform(this); append_string("/"); append_mandatory_space(); break; case Complex_Selector::PRECEDES: if (is_empty) append_optional_space(); else append_mandatory_space(); append_string("~"); if (tail) append_mandatory_space(); else append_optional_space(); break; } if (tail && comb != Complex_Selector::ANCESTOR_OF) { if (c->has_line_break()) append_optional_linefeed(); } if (tail) tail->perform(this); if (!tail && c->has_line_break()) { if (output_style() == COMPACT) { append_mandatory_space(); } } }
void Emitter::append_indentation() { if (output_style() == COMPRESSED) return; if (output_style() == COMPACT) return; if (in_declaration && in_comma_array) return; if (scheduled_linefeed && indentation) scheduled_linefeed = 1; std::string indent = ""; for (size_t i = 0; i < indentation; i++) indent += ctx ? ctx->indent : " "; append_string(indent); }
void Emitter::append_delimiter() { scheduled_delimiter = true; if (output_style() == COMPACT) { if (indentation == 0) { append_mandatory_linefeed(); } else { append_mandatory_space(); } } else if (output_style() != COMPRESSED) { append_optional_linefeed(); } }
// statements void Inspect::operator()(Block* block) { if (!block->is_root()) { add_open_mapping(block); append_scope_opener(); } if (output_style() == NESTED) indentation += block->tabs(); for (size_t i = 0, L = block->length(); i < L; ++i) { (*block)[i]->perform(this); } if (output_style() == NESTED) indentation -= block->tabs(); if (!block->is_root()) { append_scope_closer(); add_close_mapping(block); } }
void Emitter::append_mandatory_linefeed() { if (output_style() != COMPRESSED) { scheduled_linefeed = 1; scheduled_space = 0; // flush_schedules(); } }
void Emitter::append_special_linefeed() { if (output_style() == COMPACT) { append_mandatory_linefeed(); for (size_t p = 0; p < indentation; p++) append_string(ctx ? ctx->indent : " "); } }
void Emitter::append_optional_linefeed() { if (in_declaration && in_comma_array) return; if (output_style() == COMPACT) { append_mandatory_space(); } else { append_mandatory_linefeed(); } }
void Emitter::append_optional_space() { if (output_style() != COMPRESSED && buffer().size()) { char lst = buffer().at(buffer().length() - 1); if (!isspace(lst) || scheduled_delimiter) { append_mandatory_space(); } } }
void Emitter::append_scope_closer(AST_Node* node) { -- indentation; scheduled_linefeed = 0; if (output_style() == COMPRESSED) scheduled_delimiter = false; if (output_style() == EXPANDED) { append_optional_linefeed(); append_indentation(); } else { append_optional_space(); } append_string("}"); if (node) add_close_mapping(node); append_optional_linefeed(); if (indentation != 0) return; if (output_style() != COMPRESSED) scheduled_linefeed = 2; }
void Inspect::operator()(Compound_Selector* s) { for (size_t i = 0, L = s->length(); i < L; ++i) { (*s)[i]->perform(this); } if (s->has_line_break()) { if (output_style() != COMPACT) { append_optional_linefeed(); } } }
void Output::operator()(String_Constant* s) { std::string value(s->value()); if (s->can_compress_whitespace() && output_style() == COMPRESSED) { value.erase(std::remove_if(value.begin(), value.end(), ::isspace), value.end()); } if (!in_comment) { append_token(string_to_output(value), s); } else { append_token(value, s); } }
void Inspect::operator()(List* list) { std::string sep(list->separator() == SASS_SPACE ? " " : ","); if (output_style() != COMPRESSED && sep == ",") sep += " "; else if (in_media_block && sep != " ") sep += " "; // verified if (list->empty()) return; bool items_output = false; bool was_space_array = in_space_array; bool was_comma_array = in_comma_array; if (!in_declaration && ( (list->separator() == SASS_SPACE && in_space_array) || (list->separator() == SASS_COMMA && in_comma_array) )) { append_string("("); } if (list->separator() == SASS_SPACE) in_space_array = true; else if (list->separator() == SASS_COMMA) in_comma_array = true; for (size_t i = 0, L = list->size(); i < L; ++i) { Expression* list_item = (*list)[i]; if (list_item->is_invisible()) { continue; } if (items_output) { append_string(sep); } if (items_output && sep != " ") append_optional_space(); list_item->perform(this); items_output = true; } in_comma_array = was_comma_array; in_space_array = was_space_array; if (!in_declaration && ( (list->separator() == SASS_SPACE && in_space_array) || (list->separator() == SASS_COMMA && in_comma_array) )) { append_string(")"); } }
OutputBuffer Output::get_buffer(void) { Emitter emitter(opt); Inspect inspect(emitter); size_t size_nodes = top_nodes.size(); for (size_t i = 0; i < size_nodes; i++) { top_nodes[i]->perform(&inspect); inspect.append_mandatory_linefeed(); } // flush scheduled outputs // maybe omit semicolon if possible inspect.finalize(wbuf.buffer.size() == 0); // prepend buffer on top prepend_output(inspect.output()); // make sure we end with a linefeed if (!ends_with(wbuf.buffer, opt.linefeed)) { // if the output is not completely empty if (!wbuf.buffer.empty()) append_string(opt.linefeed); } // search for unicode char for(const char& chr : wbuf.buffer) { // skip all ascii chars // static cast to unsigned to handle `char` being signed / unsigned if (static_cast<unsigned>(chr) < 128) continue; // declare the charset if (output_style() != COMPRESSED) charset = "@charset \"UTF-8\";" + std::string(opt.linefeed); else charset = "\xEF\xBB\xBF"; // abort search break; } // add charset as first line, before comments and imports if (!charset.empty()) prepend_string(charset); return wbuf; }
// append some text or token to the buffer void Emitter::append_string(const std::string& text) { // write space/lf flush_schedules(); if (in_comment && output_style() == COMPACT) { // unescape comment nodes std::string out = comment_to_string(text); // add to buffer wbuf.buffer += out; // account for data in source-maps wbuf.smap.append(Offset(out)); } else { // add to buffer wbuf.buffer += text; // account for data in source-maps wbuf.smap.append(Offset(text)); } }
OutputBuffer Output::get_buffer(void) { Emitter emitter(ctx); Inspect inspect(emitter); size_t size_nodes = top_nodes.size(); for (size_t i = 0; i < size_nodes; i++) { top_nodes[i]->perform(&inspect); inspect.append_mandatory_linefeed(); } // flush scheduled outputs inspect.finalize(); // prepend buffer on top prepend_output(inspect.output()); // make sure we end with a linefeed if (!ends_with(wbuf.buffer, ctx->linefeed)) { // if the output is not completely empty if (!wbuf.buffer.empty()) append_string(ctx->linefeed); } // search for unicode char for(const char& chr : wbuf.buffer) { // skip all ascii chars if (chr >= 0) continue; // declare the charset if (output_style() != SASS_STYLE_COMPRESSED) charset = "@charset \"UTF-8\";" + ctx->linefeed; else charset = "\xEF\xBB\xBF"; // abort search break; } // add charset as first line, before comments and imports if (!charset.empty()) prepend_string(charset); return wbuf; }
void Output::operator()(Comment* c) { std::string txt = c->text()->to_string(opt); // if (indentation && txt == "/**/") return; bool important = c->is_important(); if (output_style() != COMPRESSED || important) { if (buffer().size() == 0) { top_nodes.push_back(c); } else { in_comment = true; append_indentation(); c->text()->perform(this); in_comment = false; if (indentation == 0) { append_mandatory_linefeed(); } else { append_optional_linefeed(); } } } }
void Output::operator()(Ruleset* r) { Selector* s = r->selector(); Block* b = r->block(); bool decls = false; // Filter out rulesets that aren't printable (process its children though) if (!Util::isPrintable(r, output_style())) { for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; if (dynamic_cast<Has_Block*>(stm)) { stm->perform(this); } } return; } if (b->has_non_hoistable()) { decls = true; if (output_style() == NESTED) indentation += r->tabs(); if (opt.source_comments) { std::stringstream ss; append_indentation(); ss << "/* line " << r->pstate().line + 1 << ", " << r->pstate().path << " */"; append_string(ss.str()); append_optional_linefeed(); } s->perform(this); append_scope_opener(b); for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; bool bPrintExpression = true; // Check print conditions if (typeid(*stm) == typeid(Declaration)) { Declaration* dec = static_cast<Declaration*>(stm); if (dec->value()->concrete_type() == Expression::STRING) { String_Constant* valConst = static_cast<String_Constant*>(dec->value()); std::string val(valConst->value()); if (auto qstr = dynamic_cast<String_Quoted*>(valConst)) { if (!qstr->quote_mark() && val.empty()) { bPrintExpression = false; } } } else if (dec->value()->concrete_type() == Expression::LIST) { List* list = static_cast<List*>(dec->value()); bool all_invisible = true; for (size_t list_i = 0, list_L = list->length(); list_i < list_L; ++list_i) { Expression* item = (*list)[list_i]; if (!item->is_invisible()) all_invisible = false; } if (all_invisible) bPrintExpression = false; } } // Print if OK if (!stm->is_hoistable() && bPrintExpression) { stm->perform(this); } } if (output_style() == NESTED) indentation -= r->tabs(); append_scope_closer(b); } if (b->has_hoistable()) { if (decls) ++indentation; for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; if (stm->is_hoistable()) { stm->perform(this); } } if (decls) --indentation; } }