Initial commit
[yaffs-website] / node_modules / node-sass / src / libsass / src / output.cpp
1 #include "sass.hpp"
2 #include "ast.hpp"
3 #include "output.hpp"
4
5 namespace Sass {
6
7   Output::Output(Sass_Output_Options& opt)
8   : Inspect(Emitter(opt)),
9     charset(""),
10     top_nodes(0)
11   {}
12
13   Output::~Output() { }
14
15   void Output::fallback_impl(AST_Node_Ptr n)
16   {
17     return n->perform(this);
18   }
19
20   void Output::operator()(Number_Ptr n)
21   {
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);
28     }
29     // output the final token
30     append_token(res, n);
31   }
32
33   void Output::operator()(Import_Ptr imp)
34   {
35     top_nodes.push_back(imp);
36   }
37
38   void Output::operator()(Map_Ptr m)
39   {
40     std::string dbg(m->to_string(opt));
41     error(dbg + " isn't a valid CSS value.", m->pstate());
42   }
43
44   OutputBuffer Output::get_buffer(void)
45   {
46
47     Emitter emitter(opt);
48     Inspect inspect(emitter);
49
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();
54     }
55
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);
65     }
66
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";
77       // abort search
78       break;
79     }
80
81     // add charset as first line, before comments and imports
82     if (!charset.empty()) prepend_string(charset);
83
84     return wbuf;
85
86   }
87
88   void Output::operator()(Comment_Ptr c)
89   {
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);
96       } else {
97         in_comment = true;
98         append_indentation();
99         c->text()->perform(this);
100         in_comment = false;
101         if (indentation == 0) {
102           append_mandatory_linefeed();
103         } else {
104           append_optional_linefeed();
105         }
106       }
107     }
108   }
109
110   void Output::operator()(Ruleset_Ptr r)
111   {
112     Selector_Obj s     = r->selector();
113     Block_Obj    b     = r->block();
114
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)) {
121             stm->perform(this);
122           }
123         }
124       }
125       return;
126     }
127
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();
136     }
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;
149             }
150           }
151         }
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;
157           }
158           if (all_invisible && !list->is_bracketed()) bPrintExpression = false;
159         }
160       }
161       // Print if OK
162       if (bPrintExpression) {
163         stm->perform(this);
164       }
165     }
166     if (output_style() == NESTED) indentation -= r->tabs();
167     append_scope_closer(b);
168
169   }
170   void Output::operator()(Keyframe_Rule_Ptr r)
171   {
172     Block_Obj b = r->block();
173     Selector_Obj v = r->name();
174
175     if (!v.isNull()) {
176       v->perform(this);
177     }
178
179     if (!b) {
180       append_colon_separator();
181       return;
182     }
183
184     append_scope_opener();
185     for (size_t i = 0, L = b->length(); i < L; ++i) {
186       Statement_Obj stm = b->at(i);
187       stm->perform(this);
188       if (i < L - 1) append_special_linefeed();
189     }
190     append_scope_closer();
191   }
192
193   void Output::operator()(Supports_Block_Ptr f)
194   {
195     if (f->is_invisible()) return;
196
197     Supports_Condition_Obj c = f->condition();
198     Block_Obj b              = f->block();
199
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)) {
205           stm->perform(this);
206         }
207       }
208       return;
209     }
210
211     if (output_style() == NESTED) indentation += f->tabs();
212     append_indentation();
213     append_token("@supports", f);
214     append_mandatory_space();
215     c->perform(this);
216     append_scope_opener();
217
218     for (size_t i = 0, L = b->length(); i < L; ++i) {
219       Statement_Obj stm = b->at(i);
220       stm->perform(this);
221       if (i < L - 1) append_special_linefeed();
222     }
223
224     if (output_style() == NESTED) indentation -= f->tabs();
225
226     append_scope_closer();
227
228   }
229
230   void Output::operator()(Media_Block_Ptr m)
231   {
232     if (m->is_invisible()) return;
233
234     Block_Obj b     = m->block();
235
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)) {
241           stm->perform(this);
242         }
243       }
244       return;
245     }
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();
254
255     for (size_t i = 0, L = b->length(); i < L; ++i) {
256       if (b->at(i)) {
257       Statement_Obj stm = b->at(i);
258         stm->perform(this);
259       }
260       if (i < L - 1) append_special_linefeed();
261     }
262
263     if (output_style() == NESTED) indentation -= m->tabs();
264     append_scope_closer();
265   }
266
267   void Output::operator()(Directive_Ptr a)
268   {
269     std::string      kwd   = a->keyword();
270     Selector_Obj   s     = a->selector();
271     Expression_Obj v     = a->value();
272     Block_Obj      b     = a->block();
273
274     append_indentation();
275     append_token(kwd, a);
276     if (s) {
277       append_mandatory_space();
278       in_wrapped = true;
279       s->perform(this);
280       in_wrapped = false;
281     }
282     if (v) {
283       append_mandatory_space();
284       // ruby sass bug? should use options?
285       append_token(v->to_string(/* opt */), v);
286     }
287     if (!b) {
288       append_delimiter();
289       return;
290     }
291
292     if (b->is_invisible() || b->length() == 0) {
293       append_optional_space();
294       return append_string("{}");
295     }
296
297     append_scope_opener();
298
299     bool format = kwd != "@font-face";;
300
301     for (size_t i = 0, L = b->length(); i < L; ++i) {
302       Statement_Obj stm = b->at(i);
303       stm->perform(this);
304       if (i < L - 1 && format) append_special_linefeed();
305     }
306
307     append_scope_closer();
308   }
309
310   void Output::operator()(String_Quoted_Ptr s)
311   {
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);
316     } else {
317       append_token(s->value(), s);
318     }
319   }
320
321   void Output::operator()(String_Constant_Ptr s)
322   {
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());
326     }
327     if (!in_comment) {
328       append_token(string_to_output(value), s);
329     } else {
330       append_token(value, s);
331     }
332   }
333
334 }