1// sass.hpp must go before all system headers to get the
2// __EXTENSIONS__ fix on Solaris.
3#include "sass.hpp"
4#include "emitter.hpp"
5#include "util_string.hpp"
6#include "util.hpp"
7
8namespace Sass {
9
10 Emitter::Emitter(struct Sass_Output_Options& opt)
11 : wbuf(),
12 opt(opt),
13 indentation(0),
14 scheduled_space(0),
15 scheduled_linefeed(0),
16 scheduled_delimiter(false),
17 scheduled_crutch(0),
18 scheduled_mapping(0),
19 in_custom_property(false),
20 in_comment(false),
21 in_wrapped(false),
22 in_media_block(false),
23 in_declaration(false),
24 in_space_array(false),
25 in_comma_array(false)
26 { }
27
28 // return buffer as string
29 sass::string Emitter::get_buffer(void)
30 {
31 return wbuf.buffer;
32 }
33
34 Sass_Output_Style Emitter::output_style(void) const
35 {
36 return opt.output_style;
37 }
38
39 // PROXY METHODS FOR SOURCE MAPS
40
41 void Emitter::add_source_index(size_t idx)
42 { wbuf.smap.source_index.push_back(x: idx); }
43
44 sass::string Emitter::render_srcmap(Context &ctx)
45 { return wbuf.smap.render_srcmap(ctx); }
46
47 void Emitter::set_filename(const sass::string& str)
48 { wbuf.smap.file = str; }
49
50 void Emitter::schedule_mapping(const AST_Node* node)
51 { scheduled_mapping = node; }
52 void Emitter::add_open_mapping(const AST_Node* node)
53 { wbuf.smap.add_open_mapping(node); }
54 void Emitter::add_close_mapping(const AST_Node* node)
55 { wbuf.smap.add_close_mapping(node); }
56 SourceSpan Emitter::remap(const SourceSpan& pstate)
57 { return wbuf.smap.remap(pstate); }
58
59 // MAIN BUFFER MANIPULATION
60
61 // add outstanding delimiter
62 void Emitter::finalize(bool final)
63 {
64 scheduled_space = 0;
65 if (output_style() == SASS_STYLE_COMPRESSED)
66 if (final) scheduled_delimiter = false;
67 if (scheduled_linefeed)
68 scheduled_linefeed = 1;
69 flush_schedules();
70 }
71
72 // flush scheduled space/linefeed
73 void Emitter::flush_schedules(void)
74 {
75 // check the schedule
76 if (scheduled_linefeed) {
77 sass::string linefeeds = "";
78
79 for (size_t i = 0; i < scheduled_linefeed; i++)
80 linefeeds += opt.linefeed;
81 scheduled_space = 0;
82 scheduled_linefeed = 0;
83 append_string(text: linefeeds);
84
85 } else if (scheduled_space) {
86 sass::string spaces(scheduled_space, ' ');
87 scheduled_space = 0;
88 append_string(text: spaces);
89 }
90 if (scheduled_delimiter) {
91 scheduled_delimiter = false;
92 append_string(text: ";");
93 }
94 }
95
96 // prepend some text or token to the buffer
97 void Emitter::prepend_output(const OutputBuffer& output)
98 {
99 wbuf.smap.prepend(out: output);
100 wbuf.buffer = output.buffer + wbuf.buffer;
101 }
102
103 // prepend some text or token to the buffer
104 void Emitter::prepend_string(const sass::string& text)
105 {
106 // do not adjust mappings for utf8 bom
107 // seems they are not counted in any UA
108 if (text.compare(s: "\xEF\xBB\xBF") != 0) {
109 wbuf.smap.prepend(offset: Offset(text));
110 }
111 wbuf.buffer = text + wbuf.buffer;
112 }
113
114 char Emitter::last_char()
115 {
116 return wbuf.buffer.back();
117 }
118
119 // append a single char to the buffer
120 void Emitter::append_char(const char chr)
121 {
122 // write space/lf
123 flush_schedules();
124 // add to buffer
125 wbuf.buffer += chr;
126 // account for data in source-maps
127 wbuf.smap.append(offset: Offset(chr));
128 }
129
130 // append some text or token to the buffer
131 void Emitter::append_string(const sass::string& text)
132 {
133
134 // write space/lf
135 flush_schedules();
136
137 if (in_comment) {
138 sass::string out = Util::normalize_newlines(str: text);
139 if (output_style() == COMPACT) {
140 out = comment_to_compact_string(text: out);
141 }
142 wbuf.smap.append(offset: Offset(out));
143 wbuf.buffer += std::move(out);
144 } else {
145 // add to buffer
146 wbuf.buffer += text;
147 // account for data in source-maps
148 wbuf.smap.append(offset: Offset(text));
149 }
150 }
151
152 // append some white-space only text
153 void Emitter::append_wspace(const sass::string& text)
154 {
155 if (text.empty()) return;
156 if (peek_linefeed(start: text.c_str())) {
157 scheduled_space = 0;
158 append_mandatory_linefeed();
159 }
160 }
161
162 // append some text or token to the buffer
163 // this adds source-mappings for node start and end
164 void Emitter::append_token(const sass::string& text, const AST_Node* node)
165 {
166 flush_schedules();
167 add_open_mapping(node);
168 // hotfix for browser issues
169 // this is pretty ugly indeed
170 if (scheduled_crutch) {
171 add_open_mapping(node: scheduled_crutch);
172 scheduled_crutch = 0;
173 }
174 append_string(text);
175 add_close_mapping(node);
176 }
177
178 // HELPER METHODS
179
180 void Emitter::append_indentation()
181 {
182 if (output_style() == COMPRESSED) return;
183 if (output_style() == COMPACT) return;
184 if (in_declaration && in_comma_array) return;
185 if (scheduled_linefeed && indentation)
186 scheduled_linefeed = 1;
187 sass::string indent = "";
188 for (size_t i = 0; i < indentation; i++)
189 indent += opt.indent;
190 append_string(text: indent);
191 }
192
193 void Emitter::append_delimiter()
194 {
195 scheduled_delimiter = true;
196 if (output_style() == COMPACT) {
197 if (indentation == 0) {
198 append_mandatory_linefeed();
199 } else {
200 append_mandatory_space();
201 }
202 } else if (output_style() != COMPRESSED) {
203 append_optional_linefeed();
204 }
205 }
206
207 void Emitter::append_comma_separator()
208 {
209 // scheduled_space = 0;
210 append_string(text: ",");
211 append_optional_space();
212 }
213
214 void Emitter::append_colon_separator()
215 {
216 scheduled_space = 0;
217 append_string(text: ":");
218 if (!in_custom_property) append_optional_space();
219 }
220
221 void Emitter::append_mandatory_space()
222 {
223 scheduled_space = 1;
224 }
225
226 void Emitter::append_optional_space()
227 {
228 if ((output_style() != COMPRESSED) && buffer().size()) {
229 unsigned char lst = buffer().at(n: buffer().length() - 1);
230 if (!isspace(lst) || scheduled_delimiter) {
231 if (last_char() != '(') {
232 append_mandatory_space();
233 }
234 }
235 }
236 }
237
238 void Emitter::append_special_linefeed()
239 {
240 if (output_style() == COMPACT) {
241 append_mandatory_linefeed();
242 for (size_t p = 0; p < indentation; p++)
243 append_string(text: opt.indent);
244 }
245 }
246
247 void Emitter::append_optional_linefeed()
248 {
249 if (in_declaration && in_comma_array) return;
250 if (output_style() == COMPACT) {
251 append_mandatory_space();
252 } else {
253 append_mandatory_linefeed();
254 }
255 }
256
257 void Emitter::append_mandatory_linefeed()
258 {
259 if (output_style() != COMPRESSED) {
260 scheduled_linefeed = 1;
261 scheduled_space = 0;
262 // flush_schedules();
263 }
264 }
265
266 void Emitter::append_scope_opener(AST_Node* node)
267 {
268 scheduled_linefeed = 0;
269 append_optional_space();
270 flush_schedules();
271 if (node) add_open_mapping(node);
272 append_string(text: "{");
273 append_optional_linefeed();
274 // append_optional_space();
275 ++ indentation;
276 }
277 void Emitter::append_scope_closer(AST_Node* node)
278 {
279 -- indentation;
280 scheduled_linefeed = 0;
281 if (output_style() == COMPRESSED)
282 scheduled_delimiter = false;
283 if (output_style() == EXPANDED) {
284 append_optional_linefeed();
285 append_indentation();
286 } else {
287 append_optional_space();
288 }
289 append_string(text: "}");
290 if (node) add_close_mapping(node);
291 append_optional_linefeed();
292 if (indentation != 0) return;
293 if (output_style() != COMPRESSED)
294 scheduled_linefeed = 2;
295 }
296
297}
298

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