Blame src/output.cpp

Packit Service 7770af
#include "sass.hpp"
Packit Service 7770af
#include "ast.hpp"
Packit Service 7770af
#include "output.hpp"
Packit Service 7770af
Packit Service 7770af
namespace Sass {
Packit Service 7770af
Packit Service 7770af
  Output::Output(Sass_Output_Options& opt)
Packit Service 7770af
  : Inspect(Emitter(opt)),
Packit Service 7770af
    charset(""),
Packit Service 7770af
    top_nodes(0)
Packit Service 7770af
  {}
Packit Service 7770af
Packit Service 7770af
  Output::~Output() { }
Packit Service 7770af
Packit Service 7770af
  void Output::fallback_impl(AST_Node_Ptr n)
Packit Service 7770af
  {
Packit Service 7770af
    return n->perform(this);
Packit Service 7770af
  }
Packit Service 7770af
Packit Service 7770af
  void Output::operator()(Number_Ptr n)
Packit Service 7770af
  {
Packit Service 7770af
    // use values to_string facility
Packit Service 7770af
    std::string res = n->to_string(opt);
Packit Service 7770af
    // check for a valid unit here
Packit Service 7770af
    // includes result for reporting
Packit Service 7770af
    if (!n->is_valid_css_unit()) {
Packit Service 7770af
      throw Exception::InvalidValue(*n);
Packit Service 7770af
    }
Packit Service 7770af
    // output the final token
Packit Service 7770af
    append_token(res, n);
Packit Service 7770af
  }
Packit Service 7770af
Packit Service 7770af
  void Output::operator()(Import_Ptr imp)
Packit Service 7770af
  {
Packit Service 7770af
    top_nodes.push_back(imp);
Packit Service 7770af
  }
Packit Service 7770af
Packit Service 7770af
  void Output::operator()(Map_Ptr m)
Packit Service 7770af
  {
Packit Service 7770af
    std::string dbg(m->to_string(opt));
Packit Service 7770af
    error(dbg + " isn't a valid CSS value.", m->pstate());
Packit Service 7770af
  }
Packit Service 7770af
Packit Service 7770af
  OutputBuffer Output::get_buffer(void)
Packit Service 7770af
  {
Packit Service 7770af
Packit Service 7770af
    Emitter emitter(opt);
Packit Service 7770af
    Inspect inspect(emitter);
Packit Service 7770af
Packit Service 7770af
    size_t size_nodes = top_nodes.size();
Packit Service 7770af
    for (size_t i = 0; i < size_nodes; i++) {
Packit Service 7770af
      top_nodes[i]->perform(&inspect);
Packit Service 7770af
      inspect.append_mandatory_linefeed();
Packit Service 7770af
    }
Packit Service 7770af
Packit Service 7770af
    // flush scheduled outputs
Packit Service 7770af
    // maybe omit semicolon if possible
Packit Service 7770af
    inspect.finalize(wbuf.buffer.size() == 0);
Packit Service 7770af
    // prepend buffer on top
Packit Service 7770af
    prepend_output(inspect.output());
Packit Service 7770af
    // make sure we end with a linefeed
Packit Service 7770af
    if (!ends_with(wbuf.buffer, opt.linefeed)) {
Packit Service 7770af
      // if the output is not completely empty
Packit Service 7770af
      if (!wbuf.buffer.empty()) append_string(opt.linefeed);
Packit Service 7770af
    }
Packit Service 7770af
Packit Service 7770af
    // search for unicode char
Packit Service 7770af
    for(const char& chr : wbuf.buffer) {
Packit Service 7770af
      // skip all ascii chars
Packit Service 7770af
      // static cast to unsigned to handle `char` being signed / unsigned
Packit Service 7770af
      if (static_cast<unsigned>(chr) < 128) continue;
Packit Service 7770af
      // declare the charset
Packit Service 7770af
      if (output_style() != COMPRESSED)
Packit Service 7770af
        charset = "@charset \"UTF-8\";"
Packit Service 7770af
                + std::string(opt.linefeed);
Packit Service 7770af
      else charset = "\xEF\xBB\xBF";
Packit Service 7770af
      // abort search
Packit Service 7770af
      break;
Packit Service 7770af
    }
Packit Service 7770af
Packit Service 7770af
    // add charset as first line, before comments and imports
Packit Service 7770af
    if (!charset.empty()) prepend_string(charset);
Packit Service 7770af
Packit Service 7770af
    return wbuf;
Packit Service 7770af
Packit Service 7770af
  }
Packit Service 7770af
Packit Service 7770af
  void Output::operator()(Comment_Ptr c)
Packit Service 7770af
  {
Packit Service 7770af
    std::string txt = c->text()->to_string(opt);
Packit Service 7770af
    // if (indentation && txt == "/**/") return;
Packit Service 7770af
    bool important = c->is_important();
Packit Service 7770af
    if (output_style() != COMPRESSED || important) {
Packit Service 7770af
      if (buffer().size() == 0) {
Packit Service 7770af
        top_nodes.push_back(c);
Packit Service 7770af
      } else {
Packit Service 7770af
        in_comment = true;
Packit Service 7770af
        append_indentation();
Packit Service 7770af
        c->text()->perform(this);
Packit Service 7770af
        in_comment = false;
Packit Service 7770af
        if (indentation == 0) {
Packit Service 7770af
          append_mandatory_linefeed();
Packit Service 7770af
        } else {
Packit Service 7770af
          append_optional_linefeed();
Packit Service 7770af
        }
Packit Service 7770af
      }
Packit Service 7770af
    }
Packit Service 7770af
  }
Packit Service 7770af
Packit Service 7770af
  void Output::operator()(Ruleset_Ptr r)
Packit Service 7770af
  {
Packit Service 7770af
    Selector_Obj s     = r->selector();
Packit Service 7770af
    Block_Obj    b     = r->block();
Packit Service 7770af
Packit Service 7770af
    // Filter out rulesets that aren't printable (process its children though)
Packit Service 7770af
    if (!Util::isPrintable(r, output_style())) {
Packit Service 7770af
      for (size_t i = 0, L = b->length(); i < L; ++i) {
Packit Service 7770af
        const Statement_Obj& stm = b->at(i);
Packit Service 7770af
        if (Cast<Has_Block>(stm)) {
Packit Service 7770af
          if (!Cast<Declaration>(stm)) {
Packit Service 7770af
            stm->perform(this);
Packit Service 7770af
          }
Packit Service 7770af
        }
Packit Service 7770af
      }
Packit Service 7770af
      return;
Packit Service 7770af
    }
Packit Service 7770af
Packit Service 7770af
    if (output_style() == NESTED) indentation += r->tabs();
Packit Service 7770af
    if (opt.source_comments) {
Packit Service 7770af
      std::stringstream ss;
Packit Service 7770af
      append_indentation();
Packit Service 7770af
      std::string path(File::abs2rel(r->pstate().path));
Packit Service 7770af
      ss << "/* line " << r->pstate().line + 1 << ", " << path << " */";
Packit Service 7770af
      append_string(ss.str());
Packit Service 7770af
      append_optional_linefeed();
Packit Service 7770af
    }
Packit Service 7770af
    if (s) s->perform(this);
Packit Service 7770af
    append_scope_opener(b);
Packit Service 7770af
    for (size_t i = 0, L = b->length(); i < L; ++i) {
Packit Service 7770af
      Statement_Obj stm = b->at(i);
Packit Service 7770af
      bool bPrintExpression = true;
Packit Service 7770af
      // Check print conditions
Packit Service 7770af
      if (Declaration_Ptr dec = Cast<Declaration>(stm)) {
Packit Service 7770af
        if (String_Constant_Ptr valConst = Cast<String_Constant>(dec->value())) {
Packit Service 7770af
          std::string val(valConst->value());
Packit Service 7770af
          if (String_Quoted_Ptr qstr = Cast<String_Quoted>(valConst)) {
Packit Service 7770af
            if (!qstr->quote_mark() && val.empty()) {
Packit Service 7770af
              bPrintExpression = false;
Packit Service 7770af
            }
Packit Service 7770af
          }
Packit Service 7770af
        }
Packit Service 7770af
        else if (List_Ptr list = Cast<List>(dec->value())) {
Packit Service 7770af
          bool all_invisible = true;
Packit Service 7770af
          for (size_t list_i = 0, list_L = list->length(); list_i < list_L; ++list_i) {
Packit Service 7770af
            Expression_Ptr item = list->at(list_i);
Packit Service 7770af
            if (!item->is_invisible()) all_invisible = false;
Packit Service 7770af
          }
Packit Service 7770af
          if (all_invisible) bPrintExpression = false;
Packit Service 7770af
        }
Packit Service 7770af
      }
Packit Service 7770af
      // Print if OK
Packit Service 7770af
      if (bPrintExpression) {
Packit Service 7770af
        stm->perform(this);
Packit Service 7770af
      }
Packit Service 7770af
    }
Packit Service 7770af
    if (output_style() == NESTED) indentation -= r->tabs();
Packit Service 7770af
    append_scope_closer(b);
Packit Service 7770af
Packit Service 7770af
  }
Packit Service 7770af
  void Output::operator()(Keyframe_Rule_Ptr r)
Packit Service 7770af
  {
Packit Service 7770af
    Block_Obj b = r->block();
Packit Service 7770af
    Selector_Obj v = r->name();
Packit Service 7770af
Packit Service 7770af
    if (!v.isNull()) {
Packit Service 7770af
      v->perform(this);
Packit Service 7770af
    }
Packit Service 7770af
Packit Service 7770af
    if (!b) {
Packit Service 7770af
      append_colon_separator();
Packit Service 7770af
      return;
Packit Service 7770af
    }
Packit Service 7770af
Packit Service 7770af
    append_scope_opener();
Packit Service 7770af
    for (size_t i = 0, L = b->length(); i < L; ++i) {
Packit Service 7770af
      Statement_Obj stm = b->at(i);
Packit Service 7770af
      stm->perform(this);
Packit Service 7770af
      if (i < L - 1) append_special_linefeed();
Packit Service 7770af
    }
Packit Service 7770af
    append_scope_closer();
Packit Service 7770af
  }
Packit Service 7770af
Packit Service 7770af
  void Output::operator()(Supports_Block_Ptr f)
Packit Service 7770af
  {
Packit Service 7770af
    if (f->is_invisible()) return;
Packit Service 7770af
Packit Service 7770af
    Supports_Condition_Obj c = f->condition();
Packit Service 7770af
    Block_Obj b              = f->block();
Packit Service 7770af
Packit Service 7770af
    // Filter out feature blocks that aren't printable (process its children though)
Packit Service 7770af
    if (!Util::isPrintable(f, output_style())) {
Packit Service 7770af
      for (size_t i = 0, L = b->length(); i < L; ++i) {
Packit Service 7770af
        Statement_Obj stm = b->at(i);
Packit Service 7770af
        if (Cast<Has_Block>(stm)) {
Packit Service 7770af
          stm->perform(this);
Packit Service 7770af
        }
Packit Service 7770af
      }
Packit Service 7770af
      return;
Packit Service 7770af
    }
Packit Service 7770af
Packit Service 7770af
    if (output_style() == NESTED) indentation += f->tabs();
Packit Service 7770af
    append_indentation();
Packit Service 7770af
    append_token("@supports", f);
Packit Service 7770af
    append_mandatory_space();
Packit Service 7770af
    c->perform(this);
Packit Service 7770af
    append_scope_opener();
Packit Service 7770af
Packit Service 7770af
    for (size_t i = 0, L = b->length(); i < L; ++i) {
Packit Service 7770af
      Statement_Obj stm = b->at(i);
Packit Service 7770af
      stm->perform(this);
Packit Service 7770af
      if (i < L - 1) append_special_linefeed();
Packit Service 7770af
    }
Packit Service 7770af
Packit Service 7770af
    if (output_style() == NESTED) indentation -= f->tabs();
Packit Service 7770af
Packit Service 7770af
    append_scope_closer();
Packit Service 7770af
Packit Service 7770af
  }
Packit Service 7770af
Packit Service 7770af
  void Output::operator()(Media_Block_Ptr m)
Packit Service 7770af
  {
Packit Service 7770af
    if (m->is_invisible()) return;
Packit Service 7770af
Packit Service 7770af
    Block_Obj b     = m->block();
Packit Service 7770af
Packit Service 7770af
    // Filter out media blocks that aren't printable (process its children though)
Packit Service 7770af
    if (!Util::isPrintable(m, output_style())) {
Packit Service 7770af
      for (size_t i = 0, L = b->length(); i < L; ++i) {
Packit Service 7770af
        Statement_Obj stm = b->at(i);
Packit Service 7770af
        if (Cast<Has_Block>(stm)) {
Packit Service 7770af
          stm->perform(this);
Packit Service 7770af
        }
Packit Service 7770af
      }
Packit Service 7770af
      return;
Packit Service 7770af
    }
Packit Service 7770af
    if (output_style() == NESTED) indentation += m->tabs();
Packit Service 7770af
    append_indentation();
Packit Service 7770af
    append_token("@media", m);
Packit Service 7770af
    append_mandatory_space();
Packit Service 7770af
    in_media_block = true;
Packit Service 7770af
    m->media_queries()->perform(this);
Packit Service 7770af
    in_media_block = false;
Packit Service 7770af
    append_scope_opener();
Packit Service 7770af
Packit Service 7770af
    for (size_t i = 0, L = b->length(); i < L; ++i) {
Packit Service 7770af
      if (b->at(i)) {
Packit Service 7770af
      Statement_Obj stm = b->at(i);
Packit Service 7770af
        stm->perform(this);
Packit Service 7770af
      }
Packit Service 7770af
      if (i < L - 1) append_special_linefeed();
Packit Service 7770af
    }
Packit Service 7770af
Packit Service 7770af
    if (output_style() == NESTED) indentation -= m->tabs();
Packit Service 7770af
    append_scope_closer();
Packit Service 7770af
  }
Packit Service 7770af
Packit Service 7770af
  void Output::operator()(Directive_Ptr a)
Packit Service 7770af
  {
Packit Service 7770af
    std::string      kwd   = a->keyword();
Packit Service 7770af
    Selector_Obj   s     = a->selector();
Packit Service 7770af
    Expression_Obj v     = a->value();
Packit Service 7770af
    Block_Obj      b     = a->block();
Packit Service 7770af
Packit Service 7770af
    append_indentation();
Packit Service 7770af
    append_token(kwd, a);
Packit Service 7770af
    if (s) {
Packit Service 7770af
      append_mandatory_space();
Packit Service 7770af
      in_wrapped = true;
Packit Service 7770af
      s->perform(this);
Packit Service 7770af
      in_wrapped = false;
Packit Service 7770af
    }
Packit Service 7770af
    if (v) {
Packit Service 7770af
      append_mandatory_space();
Packit Service 7770af
      // ruby sass bug? should use options?
Packit Service 7770af
      append_token(v->to_string(/* opt */), v);
Packit Service 7770af
    }
Packit Service 7770af
    if (!b) {
Packit Service 7770af
      append_delimiter();
Packit Service 7770af
      return;
Packit Service 7770af
    }
Packit Service 7770af
Packit Service 7770af
    if (b->is_invisible() || b->length() == 0) {
Packit Service 7770af
      append_optional_space();
Packit Service 7770af
      return append_string("{}");
Packit Service 7770af
    }
Packit Service 7770af
Packit Service 7770af
    append_scope_opener();
Packit Service 7770af
Packit Service 7770af
    bool format = kwd != "@font-face";;
Packit Service 7770af
Packit Service 7770af
    for (size_t i = 0, L = b->length(); i < L; ++i) {
Packit Service 7770af
      Statement_Obj stm = b->at(i);
Packit Service 7770af
      stm->perform(this);
Packit Service 7770af
      if (i < L - 1 && format) append_special_linefeed();
Packit Service 7770af
    }
Packit Service 7770af
Packit Service 7770af
    append_scope_closer();
Packit Service 7770af
  }
Packit Service 7770af
Packit Service 7770af
  void Output::operator()(String_Quoted_Ptr s)
Packit Service 7770af
  {
Packit Service 7770af
    if (s->quote_mark()) {
Packit Service 7770af
      append_token(quote(s->value(), s->quote_mark()), s);
Packit Service 7770af
    } else if (!in_comment) {
Packit Service 7770af
      append_token(string_to_output(s->value()), s);
Packit Service 7770af
    } else {
Packit Service 7770af
      append_token(s->value(), s);
Packit Service 7770af
    }
Packit Service 7770af
  }
Packit Service 7770af
Packit Service 7770af
  void Output::operator()(String_Constant_Ptr s)
Packit Service 7770af
  {
Packit Service 7770af
    std::string value(s->value());
Packit Service 7770af
    if (s->can_compress_whitespace() && output_style() == COMPRESSED) {
Packit Service 7770af
      value.erase(std::remove_if(value.begin(), value.end(), ::isspace), value.end());
Packit Service 7770af
    }
Packit Service 7770af
    if (!in_comment) {
Packit Service 7770af
      append_token(string_to_output(value), s);
Packit Service 7770af
    } else {
Packit Service 7770af
      append_token(value, s);
Packit Service 7770af
    }
Packit Service 7770af
  }
Packit Service 7770af
Packit Service 7770af
}