sassc 0.0.10 → 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/ext/libsass/.gitignore +6 -0
- data/ext/libsass/.travis.yml +5 -1
- data/ext/libsass/Makefile +12 -3
- data/ext/libsass/Makefile.am +16 -28
- data/ext/libsass/Readme.md +1 -0
- data/ext/libsass/appveyor.yml +1 -2
- data/ext/libsass/ast.cpp +9 -0
- data/ext/libsass/ast.hpp +152 -55
- data/ext/libsass/ast_factory.hpp +2 -0
- data/ext/libsass/ast_fwd_decl.hpp +1 -0
- data/ext/libsass/backtrace.hpp +2 -2
- data/ext/libsass/bind.cpp +15 -13
- data/ext/libsass/configure.ac +17 -5
- data/ext/libsass/constants.cpp +22 -2
- data/ext/libsass/constants.hpp +21 -2
- data/ext/libsass/context.cpp +79 -57
- data/ext/libsass/context.hpp +23 -9
- data/ext/libsass/contextualize.cpp +2 -28
- data/ext/libsass/contextualize.hpp +6 -10
- data/ext/libsass/contextualize_eval.cpp +93 -0
- data/ext/libsass/contextualize_eval.hpp +44 -0
- data/ext/libsass/contrib/plugin.cpp +57 -0
- data/ext/libsass/cssize.cpp +3 -1
- data/ext/libsass/debugger.hpp +242 -83
- data/ext/libsass/emitter.cpp +1 -1
- data/ext/libsass/emitter.hpp +1 -1
- data/ext/libsass/environment.hpp +109 -25
- data/ext/libsass/error_handling.cpp +3 -3
- data/ext/libsass/error_handling.hpp +0 -1
- data/ext/libsass/eval.cpp +145 -61
- data/ext/libsass/eval.hpp +9 -1
- data/ext/libsass/expand.cpp +134 -60
- data/ext/libsass/expand.hpp +5 -2
- data/ext/libsass/extend.cpp +7 -5
- data/ext/libsass/file.cpp +176 -123
- data/ext/libsass/file.hpp +44 -7
- data/ext/libsass/functions.cpp +36 -17
- data/ext/libsass/functions.hpp +2 -2
- data/ext/libsass/inspect.cpp +23 -14
- data/ext/libsass/inspect.hpp +1 -0
- data/ext/libsass/json.cpp +132 -135
- data/ext/libsass/lexer.cpp +133 -0
- data/ext/libsass/lexer.hpp +239 -0
- data/ext/libsass/listize.cpp +83 -0
- data/ext/libsass/listize.hpp +41 -0
- data/ext/libsass/operation.hpp +2 -0
- data/ext/libsass/output.cpp +5 -6
- data/ext/libsass/parser.cpp +426 -388
- data/ext/libsass/parser.hpp +97 -109
- data/ext/libsass/plugins.cpp +15 -2
- data/ext/libsass/plugins.hpp +6 -4
- data/ext/libsass/position.cpp +52 -17
- data/ext/libsass/position.hpp +19 -17
- data/ext/libsass/prelexer.cpp +202 -235
- data/ext/libsass/prelexer.hpp +73 -333
- data/ext/libsass/sass.cpp +21 -11
- data/ext/libsass/sass.h +6 -6
- data/ext/libsass/sass_context.cpp +167 -81
- data/ext/libsass/sass_context.h +26 -6
- data/ext/libsass/sass_functions.cpp +49 -40
- data/ext/libsass/sass_functions.h +55 -43
- data/ext/libsass/sass_interface.cpp +9 -8
- data/ext/libsass/sass_interface.h +3 -3
- data/ext/libsass/sass_version.h +8 -0
- data/ext/libsass/sass_version.h.in +8 -0
- data/ext/libsass/script/ci-build-libsass +3 -3
- data/ext/libsass/script/ci-report-coverage +2 -1
- data/ext/libsass/source_map.cpp +2 -2
- data/ext/libsass/util.cpp +60 -11
- data/ext/libsass/util.hpp +6 -1
- data/ext/libsass/win/libsass.filters +12 -0
- data/ext/libsass/win/libsass.vcxproj +10 -0
- data/lib/sassc.rb +3 -1
- data/lib/sassc/cache_stores/base.rb +2 -0
- data/lib/sassc/dependency.rb +3 -1
- data/lib/sassc/engine.rb +31 -16
- data/lib/sassc/error.rb +3 -2
- data/lib/sassc/functions_handler.rb +54 -0
- data/lib/sassc/import_handler.rb +41 -0
- data/lib/sassc/importer.rb +4 -31
- data/lib/sassc/native.rb +1 -1
- data/lib/sassc/native/native_context_api.rb +3 -2
- data/lib/sassc/script.rb +0 -51
- data/lib/sassc/version.rb +1 -1
- data/sassc.gemspec +1 -0
- data/test/custom_importer_test.rb +72 -69
- data/test/engine_test.rb +53 -54
- data/test/functions_test.rb +40 -39
- data/test/native_test.rb +145 -149
- data/test/output_style_test.rb +98 -0
- data/test/test_helper.rb +21 -7
- metadata +28 -2
data/ext/libsass/eval.hpp
CHANGED
@@ -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); }
|
data/ext/libsass/expand.cpp
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
#include "expand.hpp"
|
5
5
|
#include "bind.hpp"
|
6
6
|
#include "eval.hpp"
|
7
|
-
#include "
|
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,
|
15
|
+
Expand::Expand(Context& ctx, Eval* eval, Contextualize_Eval* contextualize_eval, Env* env, Backtrace* bt)
|
16
16
|
: ctx(ctx),
|
17
17
|
eval(eval),
|
18
|
-
|
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
|
-
|
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 =
|
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(
|
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
|
-
|
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
|
-
|
223
|
-
|
224
|
-
if (
|
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->
|
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
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
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
|
-
|
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
|
-
|
371
|
+
--i) {
|
372
|
+
it->value(i);
|
373
|
+
env->set_local(variable, it);
|
316
374
|
append_block(body);
|
317
375
|
}
|
318
376
|
}
|
319
|
-
|
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
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
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
|
-
(
|
418
|
+
env->set_local(variables[0], variable);
|
355
419
|
} else {
|
356
|
-
(
|
357
|
-
(
|
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
|
-
(
|
438
|
+
env->set_local(variables[j], (*variable)[j]->perform(eval->with(env, backtrace)));
|
375
439
|
}
|
376
440
|
else {
|
377
|
-
(
|
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
|
-
|
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
|
-
|
409
|
-
Selector_List*
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
//
|
426
|
-
|
427
|
-
|
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->
|
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.
|
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;
|
data/ext/libsass/expand.hpp
CHANGED
@@ -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
|
-
|
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*,
|
40
|
+
Expand(Context&, Eval*, Contextualize_Eval*, Env*, Backtrace*);
|
38
41
|
virtual ~Expand() { }
|
39
42
|
|
40
43
|
using Operation<Statement*>::operator();
|
data/ext/libsass/extend.cpp
CHANGED
@@ -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
|
-
|
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
|
-
|
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()
|
1683
|
-
|
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;
|
data/ext/libsass/file.cpp
CHANGED
@@ -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
|
-
|
43
|
-
|
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
|
-
//
|
50
|
-
|
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
|
-
|
59
|
-
|
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
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
-
|
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
|
-
|
82
|
+
size_t pos_w = path.find_last_of('\\', limit);
|
82
83
|
#else
|
83
|
-
|
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
|
-
|
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
|
101
|
-
else
|
102
|
+
if (pos == string::npos) return "";
|
103
|
+
else return path.substr(0, pos+1);
|
102
104
|
}
|
103
105
|
|
104
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
//
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
//
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
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
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
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
|
-
|
269
|
+
// helper function to resolve a filename
|
270
|
+
string find_file(const string& file, const vector<string> paths)
|
250
271
|
{
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
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
|
-
|
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
|
-
|
341
|
+
free(contents); // free the indented contents
|
289
342
|
return converted; // should be freed by caller
|
290
343
|
} else {
|
291
344
|
return contents;
|