1// sass.hpp must go before all system headers to get the
2// __EXTENSIONS__ fix on Solaris.
3#include "sass.hpp"
4
5#include "ast.hpp"
6#include "output.hpp"
7#include "util.hpp"
8
9namespace Sass {
10
11 Output::Output(Sass_Output_Options& opt)
12 : Inspect(Emitter(opt)),
13 charset(""),
14 top_nodes(0)
15 {}
16
17 Output::~Output() { }
18
19 void Output::fallback_impl(AST_Node* n)
20 {
21 return n->perform(op: this);
22 }
23
24 void Output::operator()(Number* n)
25 {
26 // check for a valid unit here
27 // includes result for reporting
28 if (!n->is_valid_css_unit()) {
29 // should be handle in check_expression
30 throw Exception::InvalidValue({}, *n);
31 }
32 // use values to_string facility
33 sass::string res = n->to_string(opt);
34 // output the final token
35 append_token(text: res, node: n);
36 }
37
38 void Output::operator()(Import* imp)
39 {
40 top_nodes.push_back(x: imp);
41 }
42
43 void Output::operator()(Map* m)
44 {
45 // should be handle in check_expression
46 throw Exception::InvalidValue({}, *m);
47 }
48
49 OutputBuffer Output::get_buffer(void)
50 {
51
52 Emitter emitter(opt);
53 Inspect inspect(emitter);
54
55 size_t size_nodes = top_nodes.size();
56 for (size_t i = 0; i < size_nodes; i++) {
57 top_nodes[i]->perform(op: &inspect);
58 inspect.append_mandatory_linefeed();
59 }
60
61 // flush scheduled outputs
62 // maybe omit semicolon if possible
63 inspect.finalize(final: wbuf.buffer.size() == 0);
64 // prepend buffer on top
65 prepend_output(out: inspect.output());
66 // make sure we end with a linefeed
67 if (!ends_with(str: wbuf.buffer, suffix: opt.linefeed)) {
68 // if the output is not completely empty
69 if (!wbuf.buffer.empty()) append_string(text: opt.linefeed);
70 }
71
72 // search for unicode char
73 for(const char& chr : wbuf.buffer) {
74 // skip all ascii chars
75 // static cast to unsigned to handle `char` being signed / unsigned
76 if (static_cast<unsigned>(chr) < 128) continue;
77 // declare the charset
78 if (output_style() != COMPRESSED)
79 charset = "@charset \"UTF-8\";"
80 + sass::string(opt.linefeed);
81 else charset = "\xEF\xBB\xBF";
82 // abort search
83 break;
84 }
85
86 // add charset as first line, before comments and imports
87 if (!charset.empty()) prepend_string(text: charset);
88
89 return wbuf;
90
91 }
92
93 void Output::operator()(Comment* c)
94 {
95 // if (indentation && txt == "/**/") return;
96 bool important = c->is_important();
97 if (output_style() != COMPRESSED || important) {
98 if (buffer().size() == 0) {
99 top_nodes.push_back(x: c);
100 } else {
101 in_comment = true;
102 append_indentation();
103 c->text()->perform(op: this);
104 in_comment = false;
105 if (indentation == 0) {
106 append_mandatory_linefeed();
107 } else {
108 append_optional_linefeed();
109 }
110 }
111 }
112 }
113
114 void Output::operator()(StyleRule* r)
115 {
116 Block_Obj b = r->block();
117 SelectorListObj s = r->selector();
118
119 if (!s || s->empty()) return;
120
121 // Filter out rulesets that aren't printable (process its children though)
122 if (!Util::isPrintable(r, style: output_style())) {
123 for (size_t i = 0, L = b->length(); i < L; ++i) {
124 const Statement_Obj& stm = b->get(i);
125 if (Cast<ParentStatement>(ptr: stm)) {
126 if (!Cast<Declaration>(ptr: stm)) {
127 stm->perform(op: this);
128 }
129 }
130 }
131 return;
132 }
133
134 if (output_style() == NESTED) {
135 indentation += r->tabs();
136 }
137 if (opt.source_comments) {
138 sass::ostream ss;
139 append_indentation();
140 sass::string path(File::abs2rel(path: r->pstate().getPath()));
141 ss << "/* line " << r->pstate().getLine() << ", " << path << " */";
142 append_string(text: ss.str());
143 append_optional_linefeed();
144 }
145 scheduled_crutch = s;
146 if (s) s->perform(op: this);
147 append_scope_opener(node: b);
148 for (size_t i = 0, L = b->length(); i < L; ++i) {
149 Statement_Obj stm = b->get(i);
150 bool bPrintExpression = true;
151 // Check print conditions
152 if (Declaration* dec = Cast<Declaration>(ptr: stm)) {
153 if (const String_Constant* valConst = Cast<String_Constant>(ptr: dec->value())) {
154 const sass::string& val = valConst->value();
155 if (const String_Quoted* qstr = Cast<const String_Quoted>(ptr: valConst)) {
156 if (!qstr->quote_mark() && val.empty()) {
157 bPrintExpression = false;
158 }
159 }
160 }
161 else if (List* list = Cast<List>(ptr: dec->value())) {
162 bool all_invisible = true;
163 for (size_t list_i = 0, list_L = list->length(); list_i < list_L; ++list_i) {
164 Expression* item = list->get(i: list_i);
165 if (!item->is_invisible()) all_invisible = false;
166 }
167 if (all_invisible && !list->is_bracketed()) bPrintExpression = false;
168 }
169 }
170 // Print if OK
171 if (bPrintExpression) {
172 stm->perform(op: this);
173 }
174 }
175 if (output_style() == NESTED) indentation -= r->tabs();
176 append_scope_closer(node: b);
177
178 }
179 void Output::operator()(Keyframe_Rule* r)
180 {
181 Block_Obj b = r->block();
182 Selector_Obj v = r->name();
183
184 if (!v.isNull()) {
185 v->perform(op: this);
186 }
187
188 if (!b) {
189 append_colon_separator();
190 return;
191 }
192
193 append_scope_opener();
194 for (size_t i = 0, L = b->length(); i < L; ++i) {
195 Statement_Obj stm = b->get(i);
196 stm->perform(op: this);
197 if (i < L - 1) append_special_linefeed();
198 }
199 append_scope_closer();
200 }
201
202 void Output::operator()(SupportsRule* f)
203 {
204 if (f->is_invisible()) return;
205
206 SupportsConditionObj c = f->condition();
207 Block_Obj b = f->block();
208
209 // Filter out feature blocks that aren't printable (process its children though)
210 if (!Util::isPrintable(r: f, style: output_style())) {
211 for (size_t i = 0, L = b->length(); i < L; ++i) {
212 Statement_Obj stm = b->get(i);
213 if (Cast<ParentStatement>(ptr: stm)) {
214 stm->perform(op: this);
215 }
216 }
217 return;
218 }
219
220 if (output_style() == NESTED) indentation += f->tabs();
221 append_indentation();
222 append_token(text: "@supports", node: f);
223 append_mandatory_space();
224 c->perform(op: this);
225 append_scope_opener();
226
227 for (size_t i = 0, L = b->length(); i < L; ++i) {
228 Statement_Obj stm = b->get(i);
229 stm->perform(op: this);
230 if (i < L - 1) append_special_linefeed();
231 }
232
233 if (output_style() == NESTED) indentation -= f->tabs();
234
235 append_scope_closer();
236
237 }
238
239 void Output::operator()(CssMediaRule* rule)
240 {
241 // Avoid null pointer exception
242 if (rule == nullptr) return;
243 // Skip empty/invisible rule
244 if (rule->isInvisible()) return;
245 // Avoid null pointer exception
246 if (rule->block() == nullptr) return;
247 // Skip empty/invisible rule
248 if (rule->block()->isInvisible()) return;
249 // Skip if block is empty/invisible
250 if (Util::isPrintable(r: rule, style: output_style())) {
251 // Let inspect do its magic
252 Inspect::operator()(rule);
253 }
254 }
255
256 void Output::operator()(AtRule* a)
257 {
258 sass::string kwd = a->keyword();
259 Selector_Obj s = a->selector();
260 ExpressionObj v = a->value();
261 Block_Obj b = a->block();
262
263 append_indentation();
264 append_token(text: kwd, node: a);
265 if (s) {
266 append_mandatory_space();
267 in_wrapped = true;
268 s->perform(op: this);
269 in_wrapped = false;
270 }
271 if (v) {
272 append_mandatory_space();
273 // ruby sass bug? should use options?
274 append_token(text: v->to_string(/* opt */), node: v);
275 }
276 if (!b) {
277 append_delimiter();
278 return;
279 }
280
281 if (b->is_invisible() || b->length() == 0) {
282 append_optional_space();
283 return append_string(text: "{}");
284 }
285
286 append_scope_opener();
287
288 bool format = kwd != "@font-face";;
289
290 for (size_t i = 0, L = b->length(); i < L; ++i) {
291 Statement_Obj stm = b->get(i);
292 if (stm) stm->perform(op: this);
293 if (i < L - 1 && format) append_special_linefeed();
294 }
295
296 append_scope_closer();
297 }
298
299 void Output::operator()(String_Quoted* s)
300 {
301 if (s->quote_mark()) {
302 append_token(text: quote(s->value(), q: s->quote_mark()), node: s);
303 } else if (!in_comment) {
304 append_token(text: string_to_output(str: s->value()), node: s);
305 } else {
306 append_token(text: s->value(), node: s);
307 }
308 }
309
310 void Output::operator()(String_Constant* s)
311 {
312 sass::string value(s->value());
313 if (!in_comment && !in_custom_property) {
314 append_token(text: string_to_output(str: value), node: s);
315 } else {
316 append_token(text: value, node: s);
317 }
318 }
319
320}
321

source code of gtk/subprojects/libsass/src/output.cpp