output_compressed.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. #include "output_compressed.hpp"
  2. #include "inspect.hpp"
  3. #include "ast.hpp"
  4. #include "context.hpp"
  5. #include "to_string.hpp"
  6. #include "util.hpp"
  7. #include <cmath>
  8. #include <iomanip>
  9. namespace Sass {
  10. using namespace std;
  11. Output_Compressed::Output_Compressed(Context* ctx) : buffer(""), rendered_imports(""), ctx(ctx), seen_utf8(false) { }
  12. Output_Compressed::~Output_Compressed() { }
  13. inline void Output_Compressed::fallback_impl(AST_Node* n)
  14. {
  15. Inspect i(ctx);
  16. n->perform(&i);
  17. const string& text = i.get_buffer();
  18. for(const char& chr : text) {
  19. // abort clause
  20. if (seen_utf8) break;
  21. // skip all normal ascii chars
  22. if (Util::isAscii(chr)) continue;
  23. // singleton
  24. seen_utf8 = true;
  25. }
  26. buffer += text;
  27. if (ctx && !ctx->_skip_source_map_update)
  28. ctx->source_map.update_column(text);
  29. }
  30. void Output_Compressed::operator()(Import* imp)
  31. {
  32. Inspect insp(ctx);
  33. imp->perform(&insp);
  34. rendered_imports += insp.get_buffer();
  35. }
  36. void Output_Compressed::operator()(Block* b)
  37. {
  38. if (!b->is_root()) return;
  39. for (size_t i = 0, L = b->length(); i < L; ++i) {
  40. (*b)[i]->perform(this);
  41. }
  42. }
  43. void Output_Compressed::operator()(Ruleset* r)
  44. {
  45. Selector* s = r->selector();
  46. Block* b = r->block();
  47. // Filter out rulesets that aren't printable (process its children though)
  48. if (!Util::isPrintable(r)) {
  49. for (size_t i = 0, L = b->length(); i < L; ++i) {
  50. Statement* stm = (*b)[i];
  51. if (dynamic_cast<Has_Block*>(stm)) {
  52. stm->perform(this);
  53. }
  54. }
  55. return;
  56. }
  57. if (b->has_non_hoistable()) {
  58. s->perform(this);
  59. append_singleline_part_to_buffer("{");
  60. for (size_t i = 0, L = b->length(); i < L; ++i) {
  61. Statement* stm = (*b)[i];
  62. if (!stm->is_hoistable()) {
  63. stm->perform(this);
  64. }
  65. }
  66. size_t l = buffer.length();
  67. if (l > 0 && buffer.at(l - 1) == ';') buffer.erase(l - 1);
  68. append_singleline_part_to_buffer("}");
  69. }
  70. if (b->has_hoistable()) {
  71. for (size_t i = 0, L = b->length(); i < L; ++i) {
  72. Statement* stm = (*b)[i];
  73. if (stm->is_hoistable()) {
  74. stm->perform(this);
  75. }
  76. }
  77. }
  78. }
  79. void Output_Compressed::operator()(Media_Block* m)
  80. {
  81. List* q = m->media_queries();
  82. Block* b = m->block();
  83. // Filter out media blocks that aren't printable (process its children though)
  84. if (!Util::isPrintable(m)) {
  85. for (size_t i = 0, L = b->length(); i < L; ++i) {
  86. Statement* stm = (*b)[i];
  87. if (dynamic_cast<Has_Block*>(stm)) {
  88. stm->perform(this);
  89. }
  90. }
  91. return;
  92. }
  93. ctx->source_map.add_mapping(m);
  94. append_singleline_part_to_buffer("@media ");
  95. q->perform(this);
  96. append_singleline_part_to_buffer("{");
  97. Selector* e = m->selector();
  98. if (e && b->has_non_hoistable()) {
  99. // JMA - hoisted, output the non-hoistable in a nested block, followed by the hoistable
  100. e->perform(this);
  101. append_singleline_part_to_buffer("{");
  102. for (size_t i = 0, L = b->length(); i < L; ++i) {
  103. Statement* stm = (*b)[i];
  104. if (!stm->is_hoistable()) {
  105. stm->perform(this);
  106. }
  107. }
  108. append_singleline_part_to_buffer("}");
  109. for (size_t i = 0, L = b->length(); i < L; ++i) {
  110. Statement* stm = (*b)[i];
  111. if (stm->is_hoistable()) {
  112. stm->perform(this);
  113. }
  114. }
  115. }
  116. else {
  117. // JMA - not hoisted, just output in order
  118. for (size_t i = 0, L = b->length(); i < L; ++i) {
  119. Statement* stm = (*b)[i];
  120. stm->perform(this);
  121. }
  122. }
  123. append_singleline_part_to_buffer("}");
  124. }
  125. void Output_Compressed::operator()(At_Rule* a)
  126. {
  127. string kwd = a->keyword();
  128. Selector* s = a->selector();
  129. Expression* v = a->value();
  130. Block* b = a->block();
  131. append_singleline_part_to_buffer(kwd);
  132. if (s) {
  133. append_singleline_part_to_buffer(" ");
  134. s->perform(this);
  135. }
  136. else if (v) {
  137. append_singleline_part_to_buffer(" ");
  138. v->perform(this);
  139. }
  140. if (!b) {
  141. append_singleline_part_to_buffer(";");
  142. return;
  143. }
  144. append_singleline_part_to_buffer("{");
  145. for (size_t i = 0, L = b->length(); i < L; ++i) {
  146. Statement* stm = (*b)[i];
  147. if (!stm->is_hoistable()) {
  148. stm->perform(this);
  149. }
  150. }
  151. for (size_t i = 0, L = b->length(); i < L; ++i) {
  152. Statement* stm = (*b)[i];
  153. if (stm->is_hoistable()) {
  154. stm->perform(this);
  155. }
  156. }
  157. append_singleline_part_to_buffer("}");
  158. }
  159. void Output_Compressed::operator()(Declaration* d)
  160. {
  161. bool bPrintExpression = true;
  162. // Check print conditions
  163. if (d->value()->concrete_type() == Expression::NULL_VAL) {
  164. bPrintExpression = false;
  165. }
  166. if (d->value()->concrete_type() == Expression::STRING) {
  167. String_Constant* valConst = static_cast<String_Constant*>(d->value());
  168. string val(valConst->value());
  169. if (val.empty()) {
  170. bPrintExpression = false;
  171. }
  172. }
  173. // Print if OK
  174. if(bPrintExpression) {
  175. if (ctx) ctx->source_map.add_mapping(d->property());
  176. d->property()->perform(this);
  177. append_singleline_part_to_buffer(":");
  178. if (ctx) ctx->source_map.add_mapping(d->value());
  179. d->value()->perform(this);
  180. if (d->is_important()) append_singleline_part_to_buffer("!important");
  181. append_singleline_part_to_buffer(";");
  182. }
  183. }
  184. void Output_Compressed::operator()(Comment* c)
  185. {
  186. To_String to_string;
  187. string txt = c->text()->perform(&to_string);
  188. if(txt[2] != '!') {
  189. return;
  190. }
  191. else {
  192. Inspect i(ctx);
  193. c->perform(&i);
  194. const string& text = i.get_buffer();
  195. for(const char& chr : text) {
  196. // abort clause
  197. if (seen_utf8) break;
  198. // skip all normal ascii chars
  199. if (Util::isAscii(chr)) continue;
  200. // singleton
  201. seen_utf8 = true;
  202. }
  203. buffer += text;
  204. if (ctx && !ctx->_skip_source_map_update)
  205. ctx->source_map.update_column(text);
  206. }
  207. }
  208. void Output_Compressed::operator()(List* list)
  209. {
  210. string sep(list->separator() == List::SPACE ? " " : ",");
  211. if (list->empty()) return;
  212. Expression* first = (*list)[0];
  213. bool first_invisible = first->is_invisible();
  214. if (!first_invisible) first->perform(this);
  215. for (size_t i = 1, L = list->length(); i < L; ++i) {
  216. Expression* next = (*list)[i];
  217. bool next_invisible = next->is_invisible();
  218. if (i == 1 && !first_invisible && !next_invisible) append_singleline_part_to_buffer(sep);
  219. else if (!next_invisible) append_singleline_part_to_buffer(sep);
  220. next->perform(this);
  221. }
  222. }
  223. // helper function for serializing colors
  224. template <size_t range>
  225. static double cap_channel(double c) {
  226. if (c > range) return range;
  227. else if (c < 0) return 0;
  228. else return c;
  229. }
  230. void Output_Compressed::operator()(Color* c)
  231. {
  232. stringstream ss;
  233. double r = round(cap_channel<0xff>(c->r()));
  234. double g = round(cap_channel<0xff>(c->g()));
  235. double b = round(cap_channel<0xff>(c->b()));
  236. double a = cap_channel<1> (c->a());
  237. // retain the originally specified color definition if unchanged
  238. if (!c->disp().empty()) {
  239. ss << c->disp();
  240. }
  241. else if (r == 0 && g == 0 && b == 0 && a == 0) {
  242. ss << "transparent";
  243. }
  244. else if (a >= 1) {
  245. // see if it's a named color
  246. int numval = r * 0x10000;
  247. numval += g * 0x100;
  248. numval += b;
  249. if (ctx && ctx->colors_to_names.count(numval)) {
  250. ss << ctx->colors_to_names[numval];
  251. }
  252. else {
  253. // otherwise output the hex triplet
  254. ss << '#' << setw(2) << setfill('0');
  255. ss << hex << setw(2) << static_cast<unsigned long>(r);
  256. ss << hex << setw(2) << static_cast<unsigned long>(g);
  257. ss << hex << setw(2) << static_cast<unsigned long>(b);
  258. }
  259. }
  260. else {
  261. ss << "rgba(";
  262. ss << static_cast<unsigned long>(r) << ",";
  263. ss << static_cast<unsigned long>(g) << ",";
  264. ss << static_cast<unsigned long>(b) << ",";
  265. ss << a << ')';
  266. }
  267. append_singleline_part_to_buffer(ss.str());
  268. }
  269. void Output_Compressed::operator()(Media_Query_Expression* mqe)
  270. {
  271. if (mqe->is_interpolated()) {
  272. mqe->feature()->perform(this);
  273. }
  274. else {
  275. append_singleline_part_to_buffer("(");
  276. mqe->feature()->perform(this);
  277. if (mqe->value()) {
  278. append_singleline_part_to_buffer(":");
  279. mqe->value()->perform(this);
  280. }
  281. append_singleline_part_to_buffer(")");
  282. }
  283. }
  284. void Output_Compressed::operator()(Null* n)
  285. {
  286. // noop
  287. }
  288. void Output_Compressed::operator()(Argument* a)
  289. {
  290. if (!a->name().empty()) {
  291. append_singleline_part_to_buffer(a->name());
  292. append_singleline_part_to_buffer(":");
  293. }
  294. a->value()->perform(this);
  295. if (a->is_rest_argument()) {
  296. append_singleline_part_to_buffer("...");
  297. }
  298. }
  299. void Output_Compressed::operator()(Arguments* a)
  300. {
  301. append_singleline_part_to_buffer("(");
  302. if (!a->empty()) {
  303. (*a)[0]->perform(this);
  304. for (size_t i = 1, L = a->length(); i < L; ++i) {
  305. append_singleline_part_to_buffer(",");
  306. (*a)[i]->perform(this);
  307. }
  308. }
  309. append_singleline_part_to_buffer(")");
  310. }
  311. void Output_Compressed::operator()(Complex_Selector* c)
  312. {
  313. Compound_Selector* head = c->head();
  314. Complex_Selector* tail = c->tail();
  315. Complex_Selector::Combinator comb = c->combinator();
  316. if (head && head->is_empty_reference() && tail)
  317. {
  318. tail->perform(this);
  319. return;
  320. }
  321. if (head && !head->is_empty_reference()) head->perform(this);
  322. switch (comb) {
  323. case Complex_Selector::ANCESTOR_OF:
  324. if (tail) append_singleline_part_to_buffer(" ");
  325. break;
  326. case Complex_Selector::PARENT_OF:
  327. append_singleline_part_to_buffer(">");
  328. break;
  329. case Complex_Selector::PRECEDES:
  330. // Apparently need to preserve spaces around this combinator?
  331. if (head && !head->is_empty_reference()) append_singleline_part_to_buffer(" ");
  332. append_singleline_part_to_buffer("~");
  333. if (tail) append_singleline_part_to_buffer(" ");
  334. break;
  335. case Complex_Selector::ADJACENT_TO:
  336. append_singleline_part_to_buffer("+");
  337. break;
  338. }
  339. if (tail) tail->perform(this);
  340. }
  341. void Output_Compressed::operator()(Selector_List* g)
  342. {
  343. if (g->empty()) return;
  344. (*g)[0]->perform(this);
  345. for (size_t i = 1, L = g->length(); i < L; ++i) {
  346. append_singleline_part_to_buffer(",");
  347. (*g)[i]->perform(this);
  348. }
  349. }
  350. void Output_Compressed::append_singleline_part_to_buffer(const string& text)
  351. {
  352. buffer += text;
  353. if (ctx && !ctx->_skip_source_map_update)
  354. ctx->source_map.update_column(text);
  355. for(const char& chr : text) {
  356. // abort clause
  357. if (seen_utf8) break;
  358. // skip all normal ascii chars
  359. if (Util::isAscii(chr)) continue;
  360. // singleton
  361. seen_utf8 = true;
  362. }
  363. }
  364. }