7 Output::Output(Sass_Output_Options& opt)
8 : Inspect(Emitter(opt)),
15 void Output::fallback_impl(AST_Node_Ptr n)
17 return n->perform(this);
20 void Output::operator()(Number_Ptr n)
22 // use values to_string facility
23 std::string res = n->to_string(opt);
24 // check for a valid unit here
25 // includes result for reporting
26 if (!n->is_valid_css_unit()) {
27 throw Exception::InvalidValue(*n);
29 // output the final token
33 void Output::operator()(Import_Ptr imp)
35 top_nodes.push_back(imp);
38 void Output::operator()(Map_Ptr m)
40 std::string dbg(m->to_string(opt));
41 error(dbg + " isn't a valid CSS value.", m->pstate());
44 OutputBuffer Output::get_buffer(void)
48 Inspect inspect(emitter);
50 size_t size_nodes = top_nodes.size();
51 for (size_t i = 0; i < size_nodes; i++) {
52 top_nodes[i]->perform(&inspect);
53 inspect.append_mandatory_linefeed();
56 // flush scheduled outputs
57 // maybe omit semicolon if possible
58 inspect.finalize(wbuf.buffer.size() == 0);
59 // prepend buffer on top
60 prepend_output(inspect.output());
61 // make sure we end with a linefeed
62 if (!ends_with(wbuf.buffer, opt.linefeed)) {
63 // if the output is not completely empty
64 if (!wbuf.buffer.empty()) append_string(opt.linefeed);
67 // search for unicode char
68 for(const char& chr : wbuf.buffer) {
69 // skip all ascii chars
70 // static cast to unsigned to handle `char` being signed / unsigned
71 if (static_cast<unsigned>(chr) < 128) continue;
72 // declare the charset
73 if (output_style() != COMPRESSED)
74 charset = "@charset \"UTF-8\";"
75 + std::string(opt.linefeed);
76 else charset = "\xEF\xBB\xBF";
81 // add charset as first line, before comments and imports
82 if (!charset.empty()) prepend_string(charset);
88 void Output::operator()(Comment_Ptr c)
90 std::string txt = c->text()->to_string(opt);
91 // if (indentation && txt == "/**/") return;
92 bool important = c->is_important();
93 if (output_style() != COMPRESSED || important) {
94 if (buffer().size() == 0) {
95 top_nodes.push_back(c);
99 c->text()->perform(this);
101 if (indentation == 0) {
102 append_mandatory_linefeed();
104 append_optional_linefeed();
110 void Output::operator()(Ruleset_Ptr r)
112 Selector_Obj s = r->selector();
113 Block_Obj b = r->block();
115 // Filter out rulesets that aren't printable (process its children though)
116 if (!Util::isPrintable(r, output_style())) {
117 for (size_t i = 0, L = b->length(); i < L; ++i) {
118 const Statement_Obj& stm = b->at(i);
119 if (Cast<Has_Block>(stm)) {
120 if (!Cast<Declaration>(stm)) {
128 if (output_style() == NESTED) indentation += r->tabs();
129 if (opt.source_comments) {
130 std::stringstream ss;
131 append_indentation();
132 std::string path(File::abs2rel(r->pstate().path));
133 ss << "/* line " << r->pstate().line + 1 << ", " << path << " */";
134 append_string(ss.str());
135 append_optional_linefeed();
137 if (s) s->perform(this);
138 append_scope_opener(b);
139 for (size_t i = 0, L = b->length(); i < L; ++i) {
140 Statement_Obj stm = b->at(i);
141 bool bPrintExpression = true;
142 // Check print conditions
143 if (Declaration_Ptr dec = Cast<Declaration>(stm)) {
144 if (String_Constant_Ptr valConst = Cast<String_Constant>(dec->value())) {
145 std::string val(valConst->value());
146 if (String_Quoted_Ptr qstr = Cast<String_Quoted>(valConst)) {
147 if (!qstr->quote_mark() && val.empty()) {
148 bPrintExpression = false;
152 else if (List_Ptr list = Cast<List>(dec->value())) {
153 bool all_invisible = true;
154 for (size_t list_i = 0, list_L = list->length(); list_i < list_L; ++list_i) {
155 Expression_Ptr item = list->at(list_i);
156 if (!item->is_invisible()) all_invisible = false;
158 if (all_invisible && !list->is_bracketed()) bPrintExpression = false;
162 if (bPrintExpression) {
166 if (output_style() == NESTED) indentation -= r->tabs();
167 append_scope_closer(b);
170 void Output::operator()(Keyframe_Rule_Ptr r)
172 Block_Obj b = r->block();
173 Selector_Obj v = r->name();
180 append_colon_separator();
184 append_scope_opener();
185 for (size_t i = 0, L = b->length(); i < L; ++i) {
186 Statement_Obj stm = b->at(i);
188 if (i < L - 1) append_special_linefeed();
190 append_scope_closer();
193 void Output::operator()(Supports_Block_Ptr f)
195 if (f->is_invisible()) return;
197 Supports_Condition_Obj c = f->condition();
198 Block_Obj b = f->block();
200 // Filter out feature blocks that aren't printable (process its children though)
201 if (!Util::isPrintable(f, output_style())) {
202 for (size_t i = 0, L = b->length(); i < L; ++i) {
203 Statement_Obj stm = b->at(i);
204 if (Cast<Has_Block>(stm)) {
211 if (output_style() == NESTED) indentation += f->tabs();
212 append_indentation();
213 append_token("@supports", f);
214 append_mandatory_space();
216 append_scope_opener();
218 for (size_t i = 0, L = b->length(); i < L; ++i) {
219 Statement_Obj stm = b->at(i);
221 if (i < L - 1) append_special_linefeed();
224 if (output_style() == NESTED) indentation -= f->tabs();
226 append_scope_closer();
230 void Output::operator()(Media_Block_Ptr m)
232 if (m->is_invisible()) return;
234 Block_Obj b = m->block();
236 // Filter out media blocks that aren't printable (process its children though)
237 if (!Util::isPrintable(m, output_style())) {
238 for (size_t i = 0, L = b->length(); i < L; ++i) {
239 Statement_Obj stm = b->at(i);
240 if (Cast<Has_Block>(stm)) {
246 if (output_style() == NESTED) indentation += m->tabs();
247 append_indentation();
248 append_token("@media", m);
249 append_mandatory_space();
250 in_media_block = true;
251 m->media_queries()->perform(this);
252 in_media_block = false;
253 append_scope_opener();
255 for (size_t i = 0, L = b->length(); i < L; ++i) {
257 Statement_Obj stm = b->at(i);
260 if (i < L - 1) append_special_linefeed();
263 if (output_style() == NESTED) indentation -= m->tabs();
264 append_scope_closer();
267 void Output::operator()(Directive_Ptr a)
269 std::string kwd = a->keyword();
270 Selector_Obj s = a->selector();
271 Expression_Obj v = a->value();
272 Block_Obj b = a->block();
274 append_indentation();
275 append_token(kwd, a);
277 append_mandatory_space();
283 append_mandatory_space();
284 // ruby sass bug? should use options?
285 append_token(v->to_string(/* opt */), v);
292 if (b->is_invisible() || b->length() == 0) {
293 append_optional_space();
294 return append_string("{}");
297 append_scope_opener();
299 bool format = kwd != "@font-face";;
301 for (size_t i = 0, L = b->length(); i < L; ++i) {
302 Statement_Obj stm = b->at(i);
304 if (i < L - 1 && format) append_special_linefeed();
307 append_scope_closer();
310 void Output::operator()(String_Quoted_Ptr s)
312 if (s->quote_mark()) {
313 append_token(quote(s->value(), s->quote_mark()), s);
314 } else if (!in_comment) {
315 append_token(string_to_output(s->value()), s);
317 append_token(s->value(), s);
321 void Output::operator()(String_Constant_Ptr s)
323 std::string value(s->value());
324 if (s->can_compress_whitespace() && output_style() == COMPRESSED) {
325 value.erase(std::remove_if(value.begin(), value.end(), ::isspace), value.end());
328 append_token(string_to_output(value), s);
330 append_token(value, s);