sassc 0.0.10 → 0.0.11

Sign up to get free protection for your applications and to get access to all the features.
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;