sassc 0.0.10 → 0.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/ext/libsass/.gitignore +6 -0
  4. data/ext/libsass/.travis.yml +5 -1
  5. data/ext/libsass/Makefile +12 -3
  6. data/ext/libsass/Makefile.am +16 -28
  7. data/ext/libsass/Readme.md +1 -0
  8. data/ext/libsass/appveyor.yml +1 -2
  9. data/ext/libsass/ast.cpp +9 -0
  10. data/ext/libsass/ast.hpp +152 -55
  11. data/ext/libsass/ast_factory.hpp +2 -0
  12. data/ext/libsass/ast_fwd_decl.hpp +1 -0
  13. data/ext/libsass/backtrace.hpp +2 -2
  14. data/ext/libsass/bind.cpp +15 -13
  15. data/ext/libsass/configure.ac +17 -5
  16. data/ext/libsass/constants.cpp +22 -2
  17. data/ext/libsass/constants.hpp +21 -2
  18. data/ext/libsass/context.cpp +79 -57
  19. data/ext/libsass/context.hpp +23 -9
  20. data/ext/libsass/contextualize.cpp +2 -28
  21. data/ext/libsass/contextualize.hpp +6 -10
  22. data/ext/libsass/contextualize_eval.cpp +93 -0
  23. data/ext/libsass/contextualize_eval.hpp +44 -0
  24. data/ext/libsass/contrib/plugin.cpp +57 -0
  25. data/ext/libsass/cssize.cpp +3 -1
  26. data/ext/libsass/debugger.hpp +242 -83
  27. data/ext/libsass/emitter.cpp +1 -1
  28. data/ext/libsass/emitter.hpp +1 -1
  29. data/ext/libsass/environment.hpp +109 -25
  30. data/ext/libsass/error_handling.cpp +3 -3
  31. data/ext/libsass/error_handling.hpp +0 -1
  32. data/ext/libsass/eval.cpp +145 -61
  33. data/ext/libsass/eval.hpp +9 -1
  34. data/ext/libsass/expand.cpp +134 -60
  35. data/ext/libsass/expand.hpp +5 -2
  36. data/ext/libsass/extend.cpp +7 -5
  37. data/ext/libsass/file.cpp +176 -123
  38. data/ext/libsass/file.hpp +44 -7
  39. data/ext/libsass/functions.cpp +36 -17
  40. data/ext/libsass/functions.hpp +2 -2
  41. data/ext/libsass/inspect.cpp +23 -14
  42. data/ext/libsass/inspect.hpp +1 -0
  43. data/ext/libsass/json.cpp +132 -135
  44. data/ext/libsass/lexer.cpp +133 -0
  45. data/ext/libsass/lexer.hpp +239 -0
  46. data/ext/libsass/listize.cpp +83 -0
  47. data/ext/libsass/listize.hpp +41 -0
  48. data/ext/libsass/operation.hpp +2 -0
  49. data/ext/libsass/output.cpp +5 -6
  50. data/ext/libsass/parser.cpp +426 -388
  51. data/ext/libsass/parser.hpp +97 -109
  52. data/ext/libsass/plugins.cpp +15 -2
  53. data/ext/libsass/plugins.hpp +6 -4
  54. data/ext/libsass/position.cpp +52 -17
  55. data/ext/libsass/position.hpp +19 -17
  56. data/ext/libsass/prelexer.cpp +202 -235
  57. data/ext/libsass/prelexer.hpp +73 -333
  58. data/ext/libsass/sass.cpp +21 -11
  59. data/ext/libsass/sass.h +6 -6
  60. data/ext/libsass/sass_context.cpp +167 -81
  61. data/ext/libsass/sass_context.h +26 -6
  62. data/ext/libsass/sass_functions.cpp +49 -40
  63. data/ext/libsass/sass_functions.h +55 -43
  64. data/ext/libsass/sass_interface.cpp +9 -8
  65. data/ext/libsass/sass_interface.h +3 -3
  66. data/ext/libsass/sass_version.h +8 -0
  67. data/ext/libsass/sass_version.h.in +8 -0
  68. data/ext/libsass/script/ci-build-libsass +3 -3
  69. data/ext/libsass/script/ci-report-coverage +2 -1
  70. data/ext/libsass/source_map.cpp +2 -2
  71. data/ext/libsass/util.cpp +60 -11
  72. data/ext/libsass/util.hpp +6 -1
  73. data/ext/libsass/win/libsass.filters +12 -0
  74. data/ext/libsass/win/libsass.vcxproj +10 -0
  75. data/lib/sassc.rb +3 -1
  76. data/lib/sassc/cache_stores/base.rb +2 -0
  77. data/lib/sassc/dependency.rb +3 -1
  78. data/lib/sassc/engine.rb +31 -16
  79. data/lib/sassc/error.rb +3 -2
  80. data/lib/sassc/functions_handler.rb +54 -0
  81. data/lib/sassc/import_handler.rb +41 -0
  82. data/lib/sassc/importer.rb +4 -31
  83. data/lib/sassc/native.rb +1 -1
  84. data/lib/sassc/native/native_context_api.rb +3 -2
  85. data/lib/sassc/script.rb +0 -51
  86. data/lib/sassc/version.rb +1 -1
  87. data/sassc.gemspec +1 -0
  88. data/test/custom_importer_test.rb +72 -69
  89. data/test/engine_test.rb +53 -54
  90. data/test/functions_test.rb +40 -39
  91. data/test/native_test.rb +145 -149
  92. data/test/output_style_test.rb +98 -0
  93. data/test/test_helper.rb +21 -7
  94. metadata +28 -2
@@ -7,6 +7,8 @@
7
7
  #include "position.hpp"
8
8
  #include "operation.hpp"
9
9
  #include "environment.hpp"
10
+ #include "contextualize.hpp"
11
+ #include "listize.hpp"
10
12
  #include "sass_values.h"
11
13
 
12
14
  namespace Sass {
@@ -14,6 +16,8 @@ namespace Sass {
14
16
 
15
17
  typedef Environment<AST_Node*> Env;
16
18
  struct Backtrace;
19
+ class Contextualize;
20
+ class Listize;
17
21
 
18
22
  class Eval : public Operation_CRTP<Expression*, Eval> {
19
23
 
@@ -22,11 +26,14 @@ namespace Sass {
22
26
  Expression* fallback_impl(AST_Node* n);
23
27
 
24
28
  public:
29
+ Contextualize* contextualize;
30
+ Listize* listize;
25
31
  Env* env;
26
32
  Backtrace* backtrace;
27
- Eval(Context&, Env*, Backtrace*);
33
+ Eval(Context&, Contextualize*, Listize*, Env*, Backtrace*);
28
34
  virtual ~Eval();
29
35
  Eval* with(Env* e, Backtrace* bt); // for setting the env before eval'ing an expression
36
+ Eval* with(Selector* c, Env* e, Backtrace* bt, Selector* placeholder = 0, Selector* extender = 0); // for setting the env before eval'ing an expression
30
37
  using Operation<Expression*>::operator();
31
38
 
32
39
  // for evaluating function bodies
@@ -62,6 +69,7 @@ namespace Sass {
62
69
  Expression* operator()(Argument*);
63
70
  Expression* operator()(Arguments*);
64
71
  Expression* operator()(Comment*);
72
+ Expression* operator()(Parent_Selector* p);
65
73
 
66
74
  template <typename U>
67
75
  Expression* fallback(U x) { return fallback_impl(x); }
@@ -4,7 +4,7 @@
4
4
  #include "expand.hpp"
5
5
  #include "bind.hpp"
6
6
  #include "eval.hpp"
7
- #include "contextualize.hpp"
7
+ #include "contextualize_eval.hpp"
8
8
  #include "to_string.hpp"
9
9
  #include "backtrace.hpp"
10
10
  #include "context.hpp"
@@ -12,10 +12,10 @@
12
12
 
13
13
  namespace Sass {
14
14
 
15
- Expand::Expand(Context& ctx, Eval* eval, Contextualize* contextualize, Env* env, Backtrace* bt)
15
+ Expand::Expand(Context& ctx, Eval* eval, Contextualize_Eval* contextualize_eval, Env* env, Backtrace* bt)
16
16
  : ctx(ctx),
17
17
  eval(eval),
18
- contextualize(contextualize),
18
+ contextualize_eval(contextualize_eval),
19
19
  env(env),
20
20
  block_stack(vector<Block*>()),
21
21
  property_stack(vector<String*>()),
@@ -47,19 +47,15 @@ namespace Sass {
47
47
  if (in_keyframes) {
48
48
  To_String to_string;
49
49
  Keyframe_Rule* k = new (ctx.mem) Keyframe_Rule(r->pstate(), r->block()->perform(this)->block());
50
- if (r->selector()) {
51
- string s(r->selector()->perform(eval->with(env, backtrace))->perform(&to_string));
52
- String_Constant* ss = new (ctx.mem) String_Constant(r->selector()->pstate(), s);
53
- k->rules(ss);
54
- }
50
+ if (r->selector()) k->selector(r->selector()->perform(contextualize_eval->with(0, env, backtrace)));
55
51
  in_at_root = old_in_at_root;
56
52
  old_in_at_root = false;
57
53
  return k;
58
54
  }
59
55
 
60
- Contextualize* contextual = contextualize->with(selector_stack.back(), env, backtrace);
56
+ Contextualize_Eval* contextual = contextualize_eval->with(selector_stack.back(), env, backtrace);
61
57
  if (old_in_at_root && !r->selector()->has_reference())
62
- contextual = contextualize->with(at_root_selector_stack.back(), env, backtrace);
58
+ contextual = contextualize_eval->with(at_root_selector_stack.back(), env, backtrace);
63
59
 
64
60
  Selector* sel_ctx = r->selector()->perform(contextual);
65
61
  if (sel_ctx == 0) throw "Cannot expand null selector";
@@ -188,7 +184,7 @@ namespace Sass {
188
184
  Block* ab = a->block();
189
185
  Selector* as = a->selector();
190
186
  Expression* av = a->value();
191
- if (as) as = as->perform(contextualize->with(0, env, backtrace));
187
+ if (as) as = as->perform(contextualize_eval->with(0, env, backtrace));
192
188
  else if (av) av = av->perform(eval->with(env, backtrace));
193
189
  Block* bb = ab ? ab->perform(this)->block() : 0;
194
190
  At_Rule* aa = new (ctx.mem) At_Rule(a->pstate(),
@@ -204,10 +200,9 @@ namespace Sass {
204
200
  {
205
201
  String* old_p = d->property();
206
202
  String* new_p = static_cast<String*>(old_p->perform(eval->with(env, backtrace)));
207
- Expression* value = d->value()->perform(eval->with(env, backtrace));
208
-
203
+ Selector* p = selector_stack.size() <= 1 ? 0 : selector_stack.back();
204
+ Expression* value = d->value()->perform(eval->with(p, env, backtrace));
209
205
  if (value->is_invisible() && !d->is_important()) return 0;
210
-
211
206
  Declaration* decl = new (ctx.mem) Declaration(d->pstate(),
212
207
  new_p,
213
208
  value,
@@ -219,12 +214,60 @@ namespace Sass {
219
214
  Statement* Expand::operator()(Assignment* a)
220
215
  {
221
216
  string var(a->variable());
222
- if (env->has(var)) {
223
- Expression* v = static_cast<Expression*>((*env)[var]);
224
- if (!a->is_guarded() || v->concrete_type() == Expression::NULL_VAL) (*env)[var] = a->value()->perform(eval->with(env, backtrace));
217
+ Selector* p = selector_stack.size() <= 1 ? 0 : selector_stack.back();
218
+ if (a->is_global()) {
219
+ if (a->is_default()) {
220
+ if (env->has_global(var)) {
221
+ Expression* e = dynamic_cast<Expression*>(env->get_global(var));
222
+ if (!e || e->concrete_type() == Expression::NULL_VAL) {
223
+ env->set_global(var, a->value()->perform(eval->with(p, env, backtrace)));
224
+ }
225
+ }
226
+ else {
227
+ env->set_global(var, a->value()->perform(eval->with(p, env, backtrace)));
228
+ }
229
+ }
230
+ else {
231
+ env->set_global(var, a->value()->perform(eval->with(p, env, backtrace)));
232
+ }
233
+ }
234
+ else if (a->is_default()) {
235
+ if (env->has_lexical(var)) {
236
+ auto cur = env;
237
+ while (cur && cur->is_lexical()) {
238
+ if (cur->has_local(var)) {
239
+ if (AST_Node* node = cur->get_local(var)) {
240
+ Expression* e = dynamic_cast<Expression*>(node);
241
+ if (!e || e->concrete_type() == Expression::NULL_VAL) {
242
+ cur->set_local(var, a->value()->perform(eval->with(p, env, backtrace)));
243
+ }
244
+ }
245
+ else {
246
+ throw runtime_error("Env not in sync");
247
+ }
248
+ return 0;
249
+ }
250
+ cur = cur->parent();
251
+ }
252
+ throw runtime_error("Env not in sync");
253
+ }
254
+ else if (env->has_global(var)) {
255
+ if (AST_Node* node = env->get_global(var)) {
256
+ Expression* e = dynamic_cast<Expression*>(node);
257
+ if (!e || e->concrete_type() == Expression::NULL_VAL) {
258
+ env->set_global(var, a->value()->perform(eval->with(p, env, backtrace)));
259
+ }
260
+ }
261
+ }
262
+ else if (env->is_lexical()) {
263
+ env->set_local(var, a->value()->perform(eval->with(p, env, backtrace)));
264
+ }
265
+ else {
266
+ env->set_local(var, a->value()->perform(eval->with(p, env, backtrace)));
267
+ }
225
268
  }
226
269
  else {
227
- env->current_frame()[var] = a->value()->perform(eval->with(env, backtrace));
270
+ env->set_lexical(var, a->value()->perform(eval->with(p, env, backtrace)));
228
271
  }
229
272
  return 0;
230
273
  }
@@ -283,6 +326,8 @@ namespace Sass {
283
326
  return 0;
284
327
  }
285
328
 
329
+ // For does not create a new env scope
330
+ // But iteration vars are reset afterwards
286
331
  Statement* Expand::operator()(For* f)
287
332
  {
288
333
  string variable(f->variable());
@@ -294,32 +339,49 @@ namespace Sass {
294
339
  if (high->concrete_type() != Expression::NUMBER) {
295
340
  error("upper bound of `@for` directive must be numeric", high->pstate(), backtrace);
296
341
  }
297
- double start = static_cast<Number*>(low)->value();
298
- double end = static_cast<Number*>(high)->value();
299
- Env new_env;
300
- new_env[variable] = new (ctx.mem) Number(low->pstate(), start);
301
- new_env.link(env);
302
- env = &new_env;
342
+ Number* sass_start = static_cast<Number*>(low);
343
+ Number* sass_end = static_cast<Number*>(high);
344
+ // check if units are valid for sequence
345
+ if (sass_start->unit() != sass_end->unit()) {
346
+ stringstream msg; msg << "Incompatible units: '"
347
+ << sass_start->unit() << "' and '"
348
+ << sass_end->unit() << "'.";
349
+ error(msg.str(), low->pstate(), backtrace);
350
+ }
351
+ double start = sass_start->value();
352
+ double end = sass_end->value();
353
+ // only create iterator once in this environment
354
+ Number* it = new (env->mem) Number(low->pstate(), start, sass_end->unit());
355
+ AST_Node* old_var = env->has_local(variable) ? env->get_local(variable) : 0;
356
+ env->set_local(variable, it);
303
357
  Block* body = f->block();
304
358
  if (start < end) {
305
359
  if (f->is_inclusive()) ++end;
306
360
  for (double i = start;
307
361
  i < end;
308
- (*env)[variable] = new (ctx.mem) Number(low->pstate(), ++i)) {
362
+ ++i) {
363
+ it->value(i);
364
+ env->set_local(variable, it);
309
365
  append_block(body);
310
366
  }
311
367
  } else {
312
368
  if (f->is_inclusive()) --end;
313
369
  for (double i = start;
314
370
  i > end;
315
- (*env)[variable] = new (ctx.mem) Number(low->pstate(), --i)) {
371
+ --i) {
372
+ it->value(i);
373
+ env->set_local(variable, it);
316
374
  append_block(body);
317
375
  }
318
376
  }
319
- env = new_env.parent();
377
+ // restore original environment
378
+ if (!old_var) env->del_local(variable);
379
+ else env->set_local(variable, old_var);
320
380
  return 0;
321
381
  }
322
382
 
383
+ // Eval does not create a new env scope
384
+ // But iteration vars are reset afterwards
323
385
  Statement* Expand::operator()(Each* e)
324
386
  {
325
387
  vector<string> variables(e->variables());
@@ -336,10 +398,12 @@ namespace Sass {
336
398
  else {
337
399
  list = static_cast<List*>(expr);
338
400
  }
339
- Env new_env;
340
- for (size_t i = 0, L = variables.size(); i < L; ++i) new_env[variables[i]] = 0;
341
- new_env.link(env);
342
- env = &new_env;
401
+ // remember variables and then reset them
402
+ vector<AST_Node*> old_vars(variables.size());
403
+ for (size_t i = 0, L = variables.size(); i < L; ++i) {
404
+ old_vars[i] = env->has_local(variables[i]) ? env->get_local(variables[i]) : 0;
405
+ env->set_local(variables[i], 0);
406
+ }
343
407
  Block* body = e->block();
344
408
 
345
409
  if (map) {
@@ -351,10 +415,10 @@ namespace Sass {
351
415
  List* variable = new (ctx.mem) List(map->pstate(), 2, List::SPACE);
352
416
  *variable << k;
353
417
  *variable << v;
354
- (*env)[variables[0]] = variable;
418
+ env->set_local(variables[0], variable);
355
419
  } else {
356
- (*env)[variables[0]] = k;
357
- (*env)[variables[1]] = v;
420
+ env->set_local(variables[0], k);
421
+ env->set_local(variables[1], v);
358
422
  }
359
423
  append_block(body);
360
424
  }
@@ -371,16 +435,20 @@ namespace Sass {
371
435
  }
372
436
  for (size_t j = 0, K = variables.size(); j < K; ++j) {
373
437
  if (j < variable->length()) {
374
- (*env)[variables[j]] = (*variable)[j]->perform(eval->with(env, backtrace));
438
+ env->set_local(variables[j], (*variable)[j]->perform(eval->with(env, backtrace)));
375
439
  }
376
440
  else {
377
- (*env)[variables[j]] = new (ctx.mem) Null(expr->pstate());
441
+ env->set_local(variables[j], new (ctx.mem) Null(expr->pstate()));
378
442
  }
379
443
  }
380
444
  append_block(body);
381
445
  }
382
446
  }
383
- env = new_env.parent();
447
+ // restore original environment
448
+ for (size_t j = 0, K = variables.size(); j < K; ++j) {
449
+ if(!old_vars[j]) env->del_local(variables[j]);
450
+ else env->set_local(variables[j], old_vars[j]);
451
+ }
384
452
  return 0;
385
453
  }
386
454
 
@@ -405,26 +473,29 @@ namespace Sass {
405
473
  To_String to_string(&ctx);
406
474
  Selector_List* extender = static_cast<Selector_List*>(selector_stack.back());
407
475
  if (!extender) return 0;
408
- Selector_List* org_extendee = static_cast<Selector_List*>(e->selector());
409
- Selector_List* extendee = static_cast<Selector_List*>(org_extendee->perform(contextualize->with(0, env, backtrace)));
410
- if (extendee->length() != 1) {
411
- error("selector groups may not be extended", extendee->pstate(), backtrace);
412
- }
413
- Complex_Selector* c = (*extendee)[0];
414
- if (!c->head() || c->tail()) {
415
- error("nested selectors may not be extended", c->pstate(), backtrace);
416
- }
417
- Compound_Selector* s = c->head();
418
- s->is_optional(org_extendee->is_optional());
419
- // // need to convert the compound selector into a by-value data structure
420
- // vector<string> target_vec;
421
- // for (size_t i = 0, L = s->length(); i < L; ++i)
422
- // { target_vec.push_back((*s)[i]->perform(&to_string)); }
423
-
424
- for (size_t i = 0, L = extender->length(); i < L; ++i) {
425
- // let's test this out
426
- // cerr << "REGISTERING EXTENSION REQUEST: " << (*extender)[i]->perform(&to_string) << " <- " << s->perform(&to_string) << endl;
427
- ctx.subset_map.put(s->to_str_vec(), make_pair((*extender)[i], s));
476
+ Contextualize_Eval* eval = contextualize_eval->with(0, env, backtrace);
477
+ Selector_List* selector_list = static_cast<Selector_List*>(e->selector());
478
+ Selector_List* contextualized = static_cast<Selector_List*>(selector_list->perform(eval));
479
+ // ToDo: remove once feature proves stable!
480
+ // if (contextualized->length() != 1) {
481
+ // error("selector groups may not be extended", extendee->pstate(), backtrace);
482
+ // }
483
+ for (auto complex_sel : contextualized->elements()) {
484
+ Complex_Selector* c = complex_sel;
485
+ if (!c->head() || c->tail()) {
486
+ error("nested selectors may not be extended", c->pstate(), backtrace);
487
+ }
488
+ Compound_Selector* compound_sel = c->head();
489
+ compound_sel->is_optional(selector_list->is_optional());
490
+ // // need to convert the compound selector into a by-value data structure
491
+ // vector<string> target_vec;
492
+ // for (size_t i = 0, L = compound_sel->length(); i < L; ++i)
493
+ // { target_vec.push_back((*compound_sel)[i]->perform(&to_string)); }
494
+ for (size_t i = 0, L = extender->length(); i < L; ++i) {
495
+ // let's test this out
496
+ // cerr << "REGISTERING EXTENSION REQUEST: " << (*extender)[i]->perform(&to_string) << " <- " << compound_sel->perform(&to_string) << endl;
497
+ ctx.subset_map.put(compound_sel->to_str_vec(), make_pair((*extender)[i], compound_sel));
498
+ }
428
499
  }
429
500
  return 0;
430
501
  }
@@ -432,7 +503,7 @@ namespace Sass {
432
503
  Statement* Expand::operator()(Definition* d)
433
504
  {
434
505
  Definition* dd = new (ctx.mem) Definition(*d);
435
- env->current_frame()[d->name() +
506
+ env->local_frame()[d->name() +
436
507
  (d->type() == Definition::MIXIN ? "[m]" : "[f]")] = dd;
437
508
  // set the static link so we can have lexical scoping
438
509
  dd->environment(env);
@@ -448,8 +519,10 @@ namespace Sass {
448
519
  Definition* def = static_cast<Definition*>((*env)[full_name]);
449
520
  Block* body = def->block();
450
521
  Parameters* params = def->parameters();
522
+ Selector* p = selector_stack.size() <= 1 ? 0 : selector_stack.back();
523
+
451
524
  Arguments* args = static_cast<Arguments*>(c->arguments()
452
- ->perform(eval->with(env, backtrace)));
525
+ ->perform(eval->with(p, env, backtrace)));
453
526
  Backtrace here(backtrace, c->pstate(), ", in mixin `" + c->name() + "`");
454
527
  backtrace = &here;
455
528
  Env new_env;
@@ -460,9 +533,10 @@ namespace Sass {
460
533
  "@content",
461
534
  new (ctx.mem) Parameters(c->pstate()),
462
535
  c->block(),
536
+ &ctx,
463
537
  Definition::MIXIN);
464
538
  thunk->environment(env);
465
- new_env.current_frame()["@content[m]"] = thunk;
539
+ new_env.local_frame()["@content[m]"] = thunk;
466
540
  }
467
541
  bind("mixin " + c->name(), params, args, ctx, &new_env, eval);
468
542
  Env* old_env = env;
@@ -14,6 +14,9 @@
14
14
  namespace Sass {
15
15
  using namespace std;
16
16
 
17
+ class Context;
18
+ class Eval;
19
+ class Contextualize_Eval;
17
20
  typedef Environment<AST_Node*> Env;
18
21
  struct Backtrace;
19
22
 
@@ -21,7 +24,7 @@ namespace Sass {
21
24
 
22
25
  Context& ctx;
23
26
  Eval* eval;
24
- Contextualize* contextualize;
27
+ Contextualize_Eval* contextualize_eval;
25
28
  Env* env;
26
29
  vector<Block*> block_stack;
27
30
  vector<String*> property_stack;
@@ -34,7 +37,7 @@ namespace Sass {
34
37
  Statement* fallback_impl(AST_Node* n);
35
38
 
36
39
  public:
37
- Expand(Context&, Eval*, Contextualize*, Env*, Backtrace*);
40
+ Expand(Context&, Eval*, Contextualize_Eval*, Env*, Backtrace*);
38
41
  virtual ~Expand() { }
39
42
 
40
43
  using Operation<Statement*>::operator();
@@ -517,14 +517,14 @@ namespace Sass {
517
517
  // had an extra source that the ruby version did not have. Without a failing test case, this is going to be extra hard to find. My
518
518
  // best guess at this point is that we're cloning an object somewhere and maintaining the sources when we shouldn't be. This is purely
519
519
  // a guess though.
520
- int maxSpecificity = 0;
520
+ unsigned long maxSpecificity = 0;
521
521
  SourcesSet sources = pSeq1->sources();
522
522
 
523
523
  DEBUG_PRINTLN(TRIM, "TRIMASDF SEQ1: " << seq1)
524
524
  DEBUG_EXEC(TRIM, printSourcesSet(sources, ctx, "TRIMASDF SOURCES: "))
525
525
 
526
526
  for (SourcesSet::iterator sourcesSetIterator = sources.begin(), sourcesSetIteratorEnd = sources.end(); sourcesSetIterator != sourcesSetIteratorEnd; ++sourcesSetIterator) {
527
- const Complex_Selector* const pCurrentSelector = *sourcesSetIterator;
527
+ const Complex_Selector* const pCurrentSelector = *sourcesSetIterator;
528
528
  maxSpecificity = max(maxSpecificity, pCurrentSelector->specificity());
529
529
  }
530
530
 
@@ -1679,9 +1679,11 @@ namespace Sass {
1679
1679
  // check if both selectors have the same media block parent
1680
1680
  if (ext.first->media_block() == pComplexSelector->media_block()) continue;
1681
1681
  To_String to_string(&ctx);
1682
- if (ext.second->media_block() && ext.second->media_block()->media_queries() &&
1683
- pComplexSelector->media_block() && pComplexSelector->media_block()->media_queries())
1684
- {
1682
+ if (ext.second->media_block() == 0) continue;
1683
+ if (pComplexSelector->media_block() &&
1684
+ ext.second->media_block()->media_queries() &&
1685
+ pComplexSelector->media_block()->media_queries()
1686
+ ) {
1685
1687
  string query_left(ext.second->media_block()->media_queries()->perform(&to_string));
1686
1688
  string query_right(pComplexSelector->media_block()->media_queries()->perform(&to_string));
1687
1689
  if (query_left == query_right) continue;
@@ -33,54 +33,55 @@ namespace Sass {
33
33
  namespace File {
34
34
  using namespace std;
35
35
 
36
+ // return the current directory
37
+ // always with forward slashes
36
38
  string get_cwd()
37
39
  {
38
40
  const size_t wd_len = 1024;
39
41
  char wd[wd_len];
40
42
  string cwd = getcwd(wd, wd_len);
41
- #ifdef _WIN32
42
- //convert backslashes to forward slashes
43
- replace(cwd.begin(), cwd.end(), '\\', '/');
44
- #endif
43
+ #ifdef _WIN32
44
+ //convert backslashes to forward slashes
45
+ replace(cwd.begin(), cwd.end(), '\\', '/');
46
+ #endif
45
47
  if (cwd[cwd.length() - 1] != '/') cwd += '/';
46
48
  return cwd;
47
49
  }
48
50
 
49
- // no physical check on filesystem
50
- // only a logical cleanup of a path
51
- string make_canonical_path (string path)
51
+ // test if path exists and is a file
52
+ bool file_exists(const string& path)
52
53
  {
53
-
54
- // declarations
55
- size_t pos;
56
-
57
54
  #ifdef _WIN32
58
- //convert backslashes to forward slashes
59
- replace(path.begin(), path.end(), '\\', '/');
55
+ wstring wpath = UTF_8::convert_to_utf16(path);
56
+ DWORD dwAttrib = GetFileAttributesW(wpath.c_str());
57
+ return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
58
+ (!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)));
59
+ #else
60
+ struct stat st_buf;
61
+ return (stat (path.c_str(), &st_buf) == 0) &&
62
+ (!S_ISDIR (st_buf.st_mode));
60
63
  #endif
64
+ }
61
65
 
62
- pos = 0; // remove all self references inside the path string
63
- while((pos = path.find("/./", pos)) != string::npos) path.erase(pos, 2);
64
-
65
- pos = 0; // remove all leading and trailing self references
66
- while(path.length() > 1 && path.substr(0, 2) == "./") path.erase(0, 2);
67
- while((pos = path.length()) > 1 && path.substr(pos - 2) == "/.") path.erase(pos - 2);
68
-
69
- pos = 0; // collapse multiple delimiters into a single one
70
- while((pos = path.find("//", pos)) != string::npos) path.erase(pos, 1);
71
-
72
- return path;
73
-
66
+ // return if given path is absolute
67
+ // works with *nix and windows paths
68
+ bool is_absolute_path(const string& path)
69
+ {
70
+ #ifdef _WIN32
71
+ if (path.length() >= 2 && isalpha(path[0]) && path[1] == ':') return true;
72
+ #endif
73
+ return path[0] == '/';
74
74
  }
75
75
 
76
- size_t find_last_folder_separator(const string& path, size_t limit = string::npos)
76
+ // helper function to find the last directory seperator
77
+ inline size_t find_last_folder_separator(const string& path, size_t limit = string::npos)
77
78
  {
78
79
  size_t pos = string::npos;
79
80
  size_t pos_p = path.find_last_of('/', limit);
80
81
  #ifdef _WIN32
81
- size_t pos_w = path.find_last_of('\\', limit);
82
+ size_t pos_w = path.find_last_of('\\', limit);
82
83
  #else
83
- size_t pos_w = string::npos;
84
+ size_t pos_w = string::npos;
84
85
  #endif
85
86
  if (pos_p != string::npos && pos_w != string::npos) {
86
87
  pos = max(pos_p, pos_w);
@@ -94,26 +95,64 @@ namespace Sass {
94
95
  return pos;
95
96
  }
96
97
 
97
- string base_name(string path)
98
+ // return only the directory part of path
99
+ string dir_name(const string& path)
98
100
  {
99
101
  size_t pos = find_last_folder_separator(path);
100
- if (pos == string::npos) return path;
101
- else return path.substr(pos+1);
102
+ if (pos == string::npos) return "";
103
+ else return path.substr(0, pos+1);
102
104
  }
103
105
 
104
- string dir_name(string path)
106
+ // return only the filename part of path
107
+ string base_name(const string& path)
105
108
  {
106
109
  size_t pos = find_last_folder_separator(path);
107
- if (pos == string::npos) return "";
108
- else return path.substr(0, pos+1);
110
+ if (pos == string::npos) return path;
111
+ else return path.substr(pos+1);
112
+ }
113
+
114
+ // do a locigal clean up of the path
115
+ // no physical check on the filesystem
116
+ string make_canonical_path (string path)
117
+ {
118
+
119
+ // declarations
120
+ size_t pos;
121
+
122
+ #ifdef _WIN32
123
+ //convert backslashes to forward slashes
124
+ replace(path.begin(), path.end(), '\\', '/');
125
+ #endif
126
+
127
+ pos = 0; // remove all self references inside the path string
128
+ while((pos = path.find("/./", pos)) != string::npos) path.erase(pos, 2);
129
+
130
+ pos = 0; // remove all leading and trailing self references
131
+ while(path.length() > 1 && path.substr(0, 2) == "./") path.erase(0, 2);
132
+ while((pos = path.length()) > 1 && path.substr(pos - 2) == "/.") path.erase(pos - 2);
133
+
134
+ pos = 0; // collapse multiple delimiters into a single one
135
+ while((pos = path.find("//", pos)) != string::npos) path.erase(pos, 1);
136
+
137
+ return path;
138
+
109
139
  }
110
140
 
141
+ // join two path segments cleanly together
142
+ // but only if right side is not absolute yet
111
143
  string join_paths(string l, string r)
112
144
  {
145
+
146
+ #ifdef _WIN32
147
+ // convert Windows backslashes to URL forward slashes
148
+ replace(l.begin(), l.end(), '\\', '/');
149
+ replace(r.begin(), r.end(), '\\', '/');
150
+ #endif
151
+
113
152
  if (l.empty()) return r;
114
153
  if (r.empty()) return l;
115
- if (is_absolute_path(r)) return r;
116
154
 
155
+ if (is_absolute_path(r)) return r;
117
156
  if (l[l.length()-1] != '/') l += '/';
118
157
 
119
158
  while ((r.length() > 3) && ((r.substr(0, 3) == "../") || (r.substr(0, 3)) == "..\\")) {
@@ -125,27 +164,26 @@ namespace Sass {
125
164
  return l + r;
126
165
  }
127
166
 
128
- bool is_absolute_path(const string& path)
129
- {
130
- if (path[0] == '/') return true;
131
- // TODO: UN-HACKIFY THIS
132
- #ifdef _WIN32
133
- if (path.length() >= 2 && isalpha(path[0]) && path[1] == ':') return true;
134
- #endif
135
- return false;
136
- }
137
-
167
+ // create an absolute path by resolving relative paths with cwd
138
168
  string make_absolute_path(const string& path, const string& cwd)
139
169
  {
140
170
  return make_canonical_path((is_absolute_path(path) ? path : join_paths(cwd, path)));
141
171
  }
142
172
 
173
+ // create a path that is relative to the given base directory
174
+ // path and base will first be resolved against cwd to make them absolute
143
175
  string resolve_relative_path(const string& uri, const string& base, const string& cwd)
144
176
  {
145
177
 
146
178
  string absolute_uri = make_absolute_path(uri, cwd);
147
179
  string absolute_base = make_absolute_path(base, cwd);
148
180
 
181
+ #ifdef _WIN32
182
+ // absolute link must have a drive letter, and we know that we
183
+ // can only create relative links if both are on the same drive
184
+ if (absolute_base[0] != absolute_uri[0]) return absolute_uri;
185
+ #endif
186
+
149
187
  string stripped_uri = "";
150
188
  string stripped_base = "";
151
189
 
@@ -194,89 +232,104 @@ namespace Sass {
194
232
  return result;
195
233
  }
196
234
 
197
- char* resolve_and_load(string path, string& real_path)
235
+ // Resolution order for ambiguous imports:
236
+ // (1) filename as given
237
+ // (2) underscore + given
238
+ // (3) underscore + given + extension
239
+ // (4) given + extension
240
+ string resolve_file(const string& filename)
198
241
  {
199
- // Resolution order for ambiguous imports:
200
- // (1) filename as given
201
- // (2) underscore + given
202
- // (3) underscore + given + extension
203
- // (4) given + extension
204
- char* contents = 0;
205
- real_path = path;
206
- vector<string> exts(3);
207
- exts[0] = ".scss";
208
- exts[1] = ".sass";
209
- exts[2] = ".css";
210
-
211
- // if the file isn't found with the given filename (1)
212
- if (!(contents = read_file(real_path))) {
213
- string dir(dir_name(path));
214
- string base(base_name(path));
215
- real_path = dir + base;
216
- // (2) underscore + given
217
- string test_path(dir + "_" + base);
218
- if ((contents = read_file(test_path))) {
219
- real_path = test_path;
220
- }
221
- // (3) underscore + given + extension
222
- if (!contents) {
223
- for(auto ext : exts) {
224
- test_path = dir + "_" + base + ext;
225
- if ((contents = read_file(test_path))) {
226
- real_path = test_path;
227
- break;
228
- }
229
- }
230
- }
231
- // (4) given + extension
232
- if (!contents) {
233
- for(auto ext : exts) {
234
- test_path = dir + base + ext;
235
- if ((contents = read_file(test_path))) {
236
- real_path = test_path;
237
- break;
238
- }
239
- }
240
- }
242
+ // supported extensions
243
+ const vector<string> exts = {
244
+ ".scss", ".sass", ".css"
245
+ };
246
+ // split the filename
247
+ string base(dir_name(filename));
248
+ string name(base_name(filename));
249
+ // create full path (maybe relative)
250
+ string path(join_paths(base, name));
251
+ if (file_exists(path)) return path;
252
+ // next test variation with underscore
253
+ path = join_paths(base, "_" + name);
254
+ if (file_exists(path)) return path;
255
+ // next test exts plus underscore
256
+ for(auto ext : exts) {
257
+ path = join_paths(base, "_" + name + ext);
258
+ if (file_exists(path)) return path;
241
259
  }
242
- #ifdef _WIN32
243
- // convert Windows backslashes to URL forward slashes
244
- replace(real_path.begin(), real_path.end(), '\\', '/');
245
- #endif
246
- return contents;
260
+ // next test plain name with exts
261
+ for(auto ext : exts) {
262
+ path = join_paths(base, name + ext);
263
+ if (file_exists(path)) return path;
264
+ }
265
+ // nothing found
266
+ return string("");
247
267
  }
248
268
 
249
- char* read_file(string path)
269
+ // helper function to resolve a filename
270
+ string find_file(const string& file, const vector<string> paths)
250
271
  {
251
- #ifdef _WIN32
252
- BYTE* pBuffer;
253
- DWORD dwBytes;
254
- // windows unicode filepaths are encoded in utf16
255
- wstring wpath = UTF_8::convert_to_utf16(path);
256
- HANDLE hFile = CreateFileW(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
257
- if (hFile == INVALID_HANDLE_VALUE) return 0;
258
- DWORD dwFileLength = GetFileSize(hFile, NULL);
259
- if (dwFileLength == INVALID_FILE_SIZE) return 0;
260
- pBuffer = new BYTE[dwFileLength + 1];
261
- ReadFile(hFile, pBuffer, dwFileLength, &dwBytes, NULL);
262
- pBuffer[dwFileLength] = '\0';
263
- CloseHandle(hFile);
264
- // just convert from unsigned char*
265
- char* contents = (char*) pBuffer;
266
- #else
267
- struct stat st;
268
- if (stat(path.c_str(), &st) == -1 || S_ISDIR(st.st_mode)) return 0;
269
- ifstream file(path.c_str(), ios::in | ios::binary | ios::ate);
270
- char* contents = 0;
271
- if (file.is_open()) {
272
- size_t size = file.tellg();
273
- contents = new char[size + 1]; // extra byte for the null char
274
- file.seekg(0, ios::beg);
275
- file.read(contents, size);
276
- contents[size] = '\0';
277
- file.close();
272
+ // search in every include path for a match
273
+ for (size_t i = 0, S = paths.size(); i < S; ++i)
274
+ {
275
+ string path(join_paths(paths[i], file));
276
+ string resolved(resolve_file(path));
277
+ if (resolved != "") return resolved;
278
278
  }
279
- #endif
279
+ // nothing found
280
+ return string("");
281
+ }
282
+
283
+ // inc paths can be directly passed from C code
284
+ string find_file(const string& file, const char* paths[])
285
+ {
286
+ if (paths == 0) return string("");
287
+ vector<string> includes(0);
288
+ // includes.push_back(".");
289
+ const char** it = paths;
290
+ while (it && *it) {
291
+ includes.push_back(*it);
292
+ ++it;
293
+ }
294
+ return find_file(file, includes);
295
+ }
296
+
297
+ // try to load the given filename
298
+ // returned memory must be freed
299
+ // will auto convert .sass files
300
+ char* read_file(const string& path)
301
+ {
302
+ #ifdef _WIN32
303
+ BYTE* pBuffer;
304
+ DWORD dwBytes;
305
+ // windows unicode filepaths are encoded in utf16
306
+ wstring wpath = UTF_8::convert_to_utf16(path);
307
+ HANDLE hFile = CreateFileW(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
308
+ if (hFile == INVALID_HANDLE_VALUE) return 0;
309
+ DWORD dwFileLength = GetFileSize(hFile, NULL);
310
+ if (dwFileLength == INVALID_FILE_SIZE) return 0;
311
+ // allocate an extra byte for the null char
312
+ pBuffer = (BYTE*)malloc((dwFileLength+1)*sizeof(BYTE));
313
+ ReadFile(hFile, pBuffer, dwFileLength, &dwBytes, NULL);
314
+ pBuffer[dwFileLength] = '\0';
315
+ CloseHandle(hFile);
316
+ // just convert from unsigned char*
317
+ char* contents = (char*) pBuffer;
318
+ #else
319
+ struct stat st;
320
+ if (stat(path.c_str(), &st) == -1 || S_ISDIR(st.st_mode)) return 0;
321
+ ifstream file(path.c_str(), ios::in | ios::binary | ios::ate);
322
+ char* contents = 0;
323
+ if (file.is_open()) {
324
+ size_t size = file.tellg();
325
+ // allocate an extra byte for the null char
326
+ contents = (char*) malloc((size+1)*sizeof(char));
327
+ file.seekg(0, ios::beg);
328
+ file.read(contents, size);
329
+ contents[size] = '\0';
330
+ file.close();
331
+ }
332
+ #endif
280
333
  string extension;
281
334
  if (path.length() > 5) {
282
335
  extension = path.substr(path.length() - 5, 5);
@@ -285,7 +338,7 @@ namespace Sass {
285
338
  extension[i] = tolower(extension[i]);
286
339
  if (extension == ".sass" && contents != 0) {
287
340
  char * converted = sass2scss(contents, SASS2SCSS_PRETTIFY_1 | SASS2SCSS_KEEP_COMMENT);
288
- delete[] contents; // free the indented contents
341
+ free(contents); // free the indented contents
289
342
  return converted; // should be freed by caller
290
343
  } else {
291
344
  return contents;