#include "sass.hpp"
#include "util.hpp"
#include "context.hpp"
#include "output.hpp"
#include "emitter.hpp"
#include "utf8_string.hpp"
namespace Sass {
Emitter::Emitter(struct Sass_Output_Options& opt)
: wbuf(),
opt(opt),
indentation(0),
scheduled_space(0),
scheduled_linefeed(0),
scheduled_delimiter(false),
scheduled_mapping(0),
in_comment(false),
in_wrapped(false),
in_media_block(false),
in_declaration(false),
in_space_array(false),
in_comma_array(false)
{ }
// return buffer as string
std::string Emitter::get_buffer(void)
{
return wbuf.buffer;
}
Sass_Output_Style Emitter::output_style(void) const
{
return opt.output_style;
}
// PROXY METHODS FOR SOURCE MAPS
void Emitter::add_source_index(size_t idx)
{ wbuf.smap.source_index.push_back(idx); }
std::string Emitter::render_srcmap(Context &ctx)
{ return wbuf.smap.render_srcmap(ctx); }
void Emitter::set_filename(const std::string& str)
{ wbuf.smap.file = str; }
void Emitter::schedule_mapping(const AST_Node_Ptr node)
{ scheduled_mapping = node; }
void Emitter::add_open_mapping(const AST_Node_Ptr node)
{ wbuf.smap.add_open_mapping(node); }
void Emitter::add_close_mapping(const AST_Node_Ptr node)
{ wbuf.smap.add_close_mapping(node); }
ParserState Emitter::remap(const ParserState& pstate)
{ return wbuf.smap.remap(pstate); }
// MAIN BUFFER MANIPULATION
// add outstanding delimiter
void Emitter::finalize(bool final)
{
scheduled_space = 0;
if (output_style() == SASS_STYLE_COMPRESSED)
if (final) scheduled_delimiter = false;
if (scheduled_linefeed)
scheduled_linefeed = 1;
flush_schedules();
}
// flush scheduled space/linefeed
void Emitter::flush_schedules(void)
{
// check the schedule
if (scheduled_linefeed) {
std::string linefeeds = "";
for (size_t i = 0; i < scheduled_linefeed; i++)
linefeeds += opt.linefeed;
scheduled_space = 0;
scheduled_linefeed = 0;
append_string(linefeeds);
} else if (scheduled_space) {
std::string spaces(scheduled_space, ' ');
scheduled_space = 0;
append_string(spaces);
}
if (scheduled_delimiter) {
scheduled_delimiter = false;
append_string(";");
}
}
// prepend some text or token to the buffer
void Emitter::prepend_output(const OutputBuffer& output)
{
wbuf.smap.prepend(output);
wbuf.buffer = output.buffer + wbuf.buffer;
}
// prepend some text or token to the buffer
void Emitter::prepend_string(const std::string& text)
{
wbuf.smap.prepend(Offset(text));
wbuf.buffer = text + wbuf.buffer;
}
// 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));
}
}
// append some white-space only text
void Emitter::append_wspace(const std::string& text)
{
if (text.empty()) return;
if (peek_linefeed(text.c_str())) {
scheduled_space = 0;
append_mandatory_linefeed();
}
}
// append some text or token to the buffer
// this adds source-mappings for node start and end
void Emitter::append_token(const std::string& text, const AST_Node_Ptr node)
{
flush_schedules();
add_open_mapping(node);
// hotfix for browser issues
// this is pretty ugly indeed
if (scheduled_mapping) {
add_open_mapping(scheduled_mapping);
scheduled_mapping = 0;
}
append_string(text);
add_close_mapping(node);
}
// HELPER METHODS
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 += opt.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();
}
}
void Emitter::append_comma_separator()
{
// scheduled_space = 0;
append_string(",");
append_optional_space();
}
void Emitter::append_colon_separator()
{
scheduled_space = 0;
append_string(":");
append_optional_space();
}
void Emitter::append_mandatory_space()
{
scheduled_space = 1;
}
void Emitter::append_optional_space()
{
if ((output_style() != COMPRESSED) && buffer().size()) {
unsigned char lst = buffer().at(buffer().length() - 1);
if (!isspace(lst) || scheduled_delimiter) {
append_mandatory_space();
}
}
}
void Emitter::append_special_linefeed()
{
if (output_style() == COMPACT) {
append_mandatory_linefeed();
for (size_t p = 0; p < indentation; p++)
append_string(opt.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_mandatory_linefeed()
{
if (output_style() != COMPRESSED) {
scheduled_linefeed = 1;
scheduled_space = 0;
// flush_schedules();
}
}
void Emitter::append_scope_opener(AST_Node_Ptr node)
{
scheduled_linefeed = 0;
append_optional_space();
flush_schedules();
if (node) add_open_mapping(node);
append_string("{");
append_optional_linefeed();
// append_optional_space();
++ indentation;
}
void Emitter::append_scope_closer(AST_Node_Ptr 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;
}
}