sassc 1.8.1 → 1.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -1
  3. data/ext/libsass/Makefile +10 -6
  4. data/ext/libsass/Readme.md +4 -4
  5. data/ext/libsass/appveyor.yml +16 -1
  6. data/ext/libsass/docs/README.md +1 -1
  7. data/ext/libsass/docs/api-context-example.md +1 -1
  8. data/ext/libsass/docs/api-context.md +1 -1
  9. data/ext/libsass/docs/api-doc.md +1 -1
  10. data/ext/libsass/docs/api-function-example.md +12 -3
  11. data/ext/libsass/docs/api-function-internal.md +4 -4
  12. data/ext/libsass/docs/api-function.md +15 -13
  13. data/ext/libsass/docs/api-importer-internal.md +9 -4
  14. data/ext/libsass/docs/api-value.md +1 -1
  15. data/ext/libsass/docs/build-shared-library.md +3 -3
  16. data/ext/libsass/docs/custom-functions-internal.md +1 -1
  17. data/ext/libsass/docs/{plugins.go → plugins.md} +0 -0
  18. data/ext/libsass/script/ci-build-libsass +25 -36
  19. data/ext/libsass/script/ci-install-deps +3 -8
  20. data/ext/libsass/script/ci-report-coverage +17 -13
  21. data/ext/libsass/src/ast.cpp +102 -7
  22. data/ext/libsass/src/ast.hpp +53 -27
  23. data/ext/libsass/src/ast_def_macros.hpp +8 -0
  24. data/ext/libsass/src/ast_fwd_decl.hpp +3 -2
  25. data/ext/libsass/src/backtrace.hpp +1 -1
  26. data/ext/libsass/src/bind.cpp +28 -17
  27. data/ext/libsass/src/bind.hpp +1 -1
  28. data/ext/libsass/src/context.cpp +441 -184
  29. data/ext/libsass/src/context.hpp +79 -82
  30. data/ext/libsass/src/debugger.hpp +3 -1
  31. data/ext/libsass/src/emitter.cpp +18 -17
  32. data/ext/libsass/src/emitter.hpp +5 -2
  33. data/ext/libsass/src/error_handling.cpp +78 -7
  34. data/ext/libsass/src/error_handling.hpp +50 -9
  35. data/ext/libsass/src/eval.cpp +100 -36
  36. data/ext/libsass/src/eval.hpp +5 -5
  37. data/ext/libsass/src/expand.cpp +32 -3
  38. data/ext/libsass/src/extend.cpp +1 -1
  39. data/ext/libsass/src/file.cpp +39 -27
  40. data/ext/libsass/src/file.hpp +67 -13
  41. data/ext/libsass/src/functions.cpp +39 -32
  42. data/ext/libsass/src/inspect.cpp +21 -21
  43. data/ext/libsass/src/json.cpp +1 -1
  44. data/ext/libsass/src/lexer.hpp +33 -4
  45. data/ext/libsass/src/output.cpp +11 -11
  46. data/ext/libsass/src/parser.cpp +28 -130
  47. data/ext/libsass/src/parser.hpp +0 -4
  48. data/ext/libsass/src/prelexer.cpp +8 -5
  49. data/ext/libsass/src/prelexer.hpp +1 -3
  50. data/ext/libsass/src/sass_context.cpp +52 -241
  51. data/ext/libsass/src/sass_context.hpp +156 -0
  52. data/ext/libsass/src/sass_functions.cpp +1 -26
  53. data/ext/libsass/src/sass_functions.hpp +32 -0
  54. data/ext/libsass/src/sass_interface.cpp +14 -48
  55. data/ext/libsass/src/sass_values.cpp +3 -77
  56. data/ext/libsass/src/sass_values.hpp +81 -0
  57. data/ext/libsass/src/source_map.cpp +7 -7
  58. data/ext/libsass/src/source_map.hpp +1 -4
  59. data/ext/libsass/src/to_string.cpp +4 -3
  60. data/ext/libsass/src/to_string.hpp +2 -1
  61. data/ext/libsass/src/util.cpp +34 -16
  62. data/ext/libsass/src/util.hpp +10 -8
  63. data/lib/sassc/version.rb +1 -1
  64. data/lib/tasks/libsass.rb +1 -1
  65. data/test/custom_importer_test.rb +6 -4
  66. data/test/engine_test.rb +5 -3
  67. data/test/functions_test.rb +1 -0
  68. data/test/native_test.rb +1 -1
  69. metadata +6 -4
  70. data/ext/libsass/script/coveralls-debug +0 -32
@@ -12,7 +12,7 @@ namespace Sass {
12
12
  class Eval;
13
13
  typedef Environment<AST_Node*> Env;
14
14
 
15
- void bind(std::string caller, Parameters*, Arguments*, Context&, Env*, Eval*);
15
+ void bind(std::string type, std::string name, Parameters*, Arguments*, Context*, Env*, Eval*);
16
16
  }
17
17
 
18
18
  #endif
@@ -38,88 +38,80 @@ namespace Sass {
38
38
  using namespace File;
39
39
  using namespace Sass;
40
40
 
41
- Sass_Queued::Sass_Queued(const std::string& load_path, const std::string& abs_path, const char* source)
41
+ inline bool sort_importers (const Sass_Importer_Entry& i, const Sass_Importer_Entry& j)
42
+ { return sass_importer_get_priority(i) > sass_importer_get_priority(j); }
43
+
44
+ static std::string safe_input(const char* in_path)
42
45
  {
43
- this->load_path = load_path;
44
- this->abs_path = abs_path;
45
- this->source = source;
46
+ // enforce some safe defaults
47
+ // used to create relative file links
48
+ std::string safe_path(in_path ? in_path : "");
49
+ return safe_path == "" ? "stdin" : safe_path;
46
50
  }
47
51
 
48
- inline bool sort_importers (const Sass_Importer_Entry& i, const Sass_Importer_Entry& j)
49
- { return sass_importer_get_priority(i) > sass_importer_get_priority(j); }
52
+ static std::string safe_output(const char* out_path, const std::string& input_path = "")
53
+ {
54
+ std::string safe_path(out_path ? out_path : "");
55
+ // maybe we can extract an output path from input path
56
+ if (safe_path == "" && input_path != "") {
57
+ int lastindex = static_cast<int>(input_path.find_last_of("."));
58
+ return (lastindex > -1 ? input_path.substr(0, lastindex) : input_path) + ".css";
59
+ }
60
+ // enforce some safe defaults
61
+ // used to create relative file links
62
+ return safe_path == "" ? "stdout" : safe_path;
63
+ }
50
64
 
51
- Context::Context(Context::Data initializers)
52
- : // Output(this),
65
+ Context::Context(struct Sass_Context* c_ctx)
66
+ : CWD(File::get_cwd()),
67
+ entry_path(""),
53
68
  head_imports(0),
54
69
  mem(Memory_Manager()),
55
- c_options (initializers.c_options()),
56
- c_compiler (initializers.c_compiler()),
57
- source_c_str (initializers.source_c_str()),
58
- sources (std::vector<char*>()),
59
- strings (std::vector<char*>()),
60
- plugin_paths (initializers.plugin_paths()),
61
- include_paths (initializers.include_paths()),
62
- queue (std::vector<Sass_Queued>()),
63
- style_sheets (std::map<std::string, Block*>()),
64
- emitter (this),
70
+ plugins(),
71
+ emitter(this),
72
+
73
+ strings(),
74
+ resources(),
75
+ sheets(),
76
+ subset_map(),
77
+ import_stack(),
78
+
79
+ c_options (c_ctx),
80
+
65
81
  c_headers (std::vector<Sass_Importer_Entry>()),
66
82
  c_importers (std::vector<Sass_Importer_Entry>()),
67
83
  c_functions (std::vector<Sass_Function_Entry>()),
68
- indent (initializers.indent()),
69
- linefeed (initializers.linefeed()),
70
- input_path (make_canonical_path(initializers.input_path())),
71
- output_path (make_canonical_path(initializers.output_path())),
72
- source_comments (initializers.source_comments()),
73
- output_style (initializers.output_style()),
74
- source_map_file (make_canonical_path(initializers.source_map_file())),
75
- source_map_root (initializers.source_map_root()), // pass-through
76
- source_map_embed (initializers.source_map_embed()),
77
- source_map_contents (initializers.source_map_contents()),
78
- omit_source_map_url (initializers.omit_source_map_url()),
79
- is_indented_syntax_src (initializers.is_indented_syntax_src()),
80
- precision (initializers.precision()),
81
- plugins(),
82
- subset_map (Subset_Map<std::string, std::pair<Complex_Selector*, Compound_Selector*> >())
83
- {
84
84
 
85
- cwd = get_cwd();
85
+ indent (safe_str(c_options->indent, " ")),
86
+ linefeed (safe_str(c_options->linefeed, "\n")),
86
87
 
87
- // enforce some safe defaults
88
- // used to create relative file links
89
- if (input_path == "") input_path = "stdin";
90
- if (output_path == "") output_path = "stdout";
88
+ input_path (make_canonical_path(safe_input(c_options->input_path))),
89
+ output_path (make_canonical_path(safe_output(c_options->output_path, input_path))),
90
+ source_map_file (make_canonical_path(safe_str(c_options->source_map_file, ""))),
91
+ source_map_root (make_canonical_path(safe_str(c_options->source_map_root, "")))
92
+
93
+ {
91
94
 
92
- include_paths.push_back(cwd);
93
- collect_include_paths(initializers.include_paths_c_str());
95
+ // add cwd to include paths
96
+ include_paths.push_back(CWD);
97
+
98
+ // collect more paths from different options
99
+ collect_include_paths(sass_option_get_include_path(c_options));
94
100
  // collect_include_paths(initializers.include_paths_array());
95
- collect_plugin_paths(initializers.plugin_paths_c_str());
101
+ collect_plugin_paths(sass_option_get_plugin_path(c_options));
96
102
  // collect_plugin_paths(initializers.plugin_paths_array());
97
103
 
98
- for (size_t i = 0, S = plugin_paths.size(); i < S; ++i) {
99
- plugins.load_plugins(plugin_paths[i]);
100
- }
101
-
102
- for(auto fn : plugins.get_functions()) {
103
- c_functions.push_back(fn);
104
- }
105
- for(auto fn : plugins.get_headers()) {
106
- c_headers.push_back(fn);
107
- }
108
- for(auto fn : plugins.get_importers()) {
109
- c_importers.push_back(fn);
110
- }
104
+ // load plugins and register custom behaviors
105
+ for(auto plug : plugin_paths) plugins.load_plugins(plug);
106
+ for(auto fn : plugins.get_headers()) c_headers.push_back(fn);
107
+ for(auto fn : plugins.get_importers()) c_importers.push_back(fn);
108
+ for(auto fn : plugins.get_functions()) c_functions.push_back(fn);
111
109
 
110
+ // sort the items by priority (lowest first)
112
111
  sort (c_headers.begin(), c_headers.end(), sort_importers);
113
112
  sort (c_importers.begin(), c_importers.end(), sort_importers);
114
- std::string entry_point = initializers.entry_point();
115
- if (!entry_point.empty()) {
116
- std::string result(add_file(entry_point, true));
117
- if (result.empty()) {
118
- throw "File to read not found or unreadable: " + entry_point;
119
- }
120
- }
121
113
 
122
- emitter.set_filename(resolve_relative_path(output_path, source_map_file, cwd));
114
+ emitter.set_filename(abs2rel(output_path, source_map_file, CWD));
123
115
 
124
116
  }
125
117
 
@@ -142,16 +134,35 @@ namespace Sass {
142
134
 
143
135
  Context::~Context()
144
136
  {
145
- // make sure we free the source even if not processed!
146
- if (sources.size() == 0 && source_c_str) free(source_c_str);
147
- // sources are allocated by strdup or malloc (overtaken from C code)
148
- for (size_t i = 0; i < sources.size(); ++i) free(sources[i]);
137
+ // resources were allocated by strdup or malloc
138
+ for (size_t i = 0; i < resources.size(); ++i) {
139
+ free(resources[i].contents);
140
+ free(resources[i].srcmap);
141
+ }
149
142
  // free all strings we kept alive during compiler execution
150
143
  for (size_t n = 0; n < strings.size(); ++n) free(strings[n]);
151
144
  // everything that gets put into sources will be freed by us
152
- for (size_t m = 0; m < import_stack.size(); ++m) sass_delete_import(import_stack[m]);
145
+ // this shouldn't have anything in it anyway!?
146
+ for (size_t m = 0; m < import_stack.size(); ++m) {
147
+ sass_import_take_source(import_stack[m]);
148
+ sass_import_take_srcmap(import_stack[m]);
149
+ sass_delete_import(import_stack[m]);
150
+ }
153
151
  // clear inner structures (vectors) and input source
154
- sources.clear(); import_stack.clear(); source_c_str = 0;
152
+ resources.clear(); import_stack.clear();
153
+ }
154
+
155
+ Data_Context::~Data_Context()
156
+ {
157
+ // --> this will be freed by resources
158
+ // make sure we free the source even if not processed!
159
+ // if (resources.size() == 0 && source_c_str) free(source_c_str);
160
+ // if (resources.size() == 0 && srcmap_c_str) free(srcmap_c_str);
161
+ // source_c_str = 0; srcmap_c_str = 0;
162
+ }
163
+
164
+ File_Context::~File_Context()
165
+ {
155
166
  }
156
167
 
157
168
  void Context::collect_include_paths(const char* paths_str)
@@ -181,10 +192,9 @@ namespace Sass {
181
192
 
182
193
  void Context::collect_include_paths(const char** paths_array)
183
194
  {
184
- if (paths_array) {
185
- for (size_t i = 0; paths_array[i]; i++) {
186
- collect_include_paths(paths_array[i]);
187
- }
195
+ if (!paths_array) return;
196
+ for (size_t i = 0; paths_array[i]; i++) {
197
+ collect_include_paths(paths_array[i]);
188
198
  }
189
199
  }
190
200
 
@@ -215,70 +225,255 @@ namespace Sass {
215
225
 
216
226
  void Context::collect_plugin_paths(const char** paths_array)
217
227
  {
218
- if (paths_array) {
219
- for (size_t i = 0; paths_array[i]; i++) {
220
- collect_plugin_paths(paths_array[i]);
221
- }
228
+ if (!paths_array) return;
229
+ for (size_t i = 0; paths_array[i]; i++) {
230
+ collect_plugin_paths(paths_array[i]);
222
231
  }
223
232
  }
224
- void Context::add_source(std::string load_path, std::string abs_path, char* contents)
233
+
234
+
235
+ // resolve the imp_path in base_path or include_paths
236
+ // looks for alternatives and returns a list from one directory
237
+ std::vector<Include> Context::find_includes(const Importer& import)
225
238
  {
226
- sources.push_back(contents);
227
- included_files.push_back(abs_path);
228
- queue.push_back(Sass_Queued(load_path, abs_path, contents));
229
- emitter.add_source_index(sources.size() - 1);
230
- include_links.push_back(resolve_relative_path(abs_path, source_map_file, cwd));
239
+ // make sure we resolve against an absolute path
240
+ std::string base_path(rel2abs(import.base_path));
241
+ // first try to resolve the load path relative to the base path
242
+ std::vector<Include> vec(resolve_includes(base_path, import.imp_path));
243
+ // then search in every include path (but only if nothing found yet)
244
+ for (size_t i = 0, S = include_paths.size(); vec.size() == 0 && i < S; ++i)
245
+ {
246
+ // call resolve_includes and individual base path and append all results
247
+ std::vector<Include> resolved(resolve_includes(include_paths[i], import.imp_path));
248
+ if (resolved.size()) vec.insert(vec.end(), resolved.begin(), resolved.end());
249
+ }
250
+ // return vector
251
+ return vec;
231
252
  }
232
253
 
233
- // Add a new import file to the context
234
- std::string Context::add_file(const std::string& file, bool delay)
254
+
255
+ // register include with resolved path and its content
256
+ // memory of the resources will be freed by us on exit
257
+ void Context::register_resource(const Include& inc, const Resource& res)
235
258
  {
236
- using namespace File;
237
- std::string path(make_canonical_path(file));
238
- std::string resolved(find_file(path, include_paths));
239
- if (resolved == "") return resolved;
240
- if (char* contents = read_file(resolved)) {
241
- add_source(path, resolved, contents);
242
- style_sheets[path] = 0;
243
- if (delay == false) {
244
- size_t i = queue.size() - 1;
245
- process_queue_entry(queue[i], i);
246
- }
247
- return path;
248
- }
249
- return std::string("");
259
+
260
+ // do not parse same resource twice
261
+ // maybe raise an error in this case
262
+ // if (sheets.count(inc.abs_path)) {
263
+ // free(res.contents); free(res.srcmap);
264
+ // throw std::runtime_error("duplicate resource registered");
265
+ // return;
266
+ // }
267
+
268
+ // get index for this resource
269
+ size_t idx = resources.size();
270
+
271
+ // tell emitter about new resource
272
+ emitter.add_source_index(idx);
273
+
274
+ // put resources under our control
275
+ // the memory will be freed later
276
+ resources.push_back(res);
277
+
278
+ // add a relative link to the working directory
279
+ included_files.push_back(inc.abs_path);
280
+ // add a relative link to the source map output file
281
+ srcmap_links.push_back(abs2rel(inc.abs_path, source_map_file, CWD));
282
+
283
+ // get pointer to the loaded content
284
+ Sass_Import_Entry import = sass_make_import(
285
+ inc.imp_path.c_str(),
286
+ inc.abs_path.c_str(),
287
+ res.contents,
288
+ res.srcmap
289
+ );
290
+ // add the entry to the stack
291
+ import_stack.push_back(import);
292
+
293
+ // get pointer to the loaded content
294
+ const char* contents = resources[idx].contents;
295
+ // keep a copy of the path around (for parserstates)
296
+ // ToDo: we clean it, but still not very elegant!?
297
+ strings.push_back(sass_strdup(inc.abs_path.c_str()));
298
+ // create the initial parser state from resource
299
+ ParserState pstate(strings.back(), contents, idx);
300
+ // create a parser instance from the given c_str buffer
301
+ Parser p(Parser::from_c_str(contents, *this, pstate));
302
+ // do not yet dispose these buffers
303
+ sass_import_take_source(import);
304
+ sass_import_take_srcmap(import);
305
+ // then parse the root block
306
+ Block* root = p.parse();
307
+ // delete memory of current stack frame
308
+ sass_delete_import(import_stack.back());
309
+ // remove current stack frame
310
+ import_stack.pop_back();
311
+ // create key/value pair for ast node
312
+ std::pair<const std::string, const StyleSheet>
313
+ ast_pair(inc.abs_path, { res, root });
314
+ // register resulting resource
315
+ sheets.insert(ast_pair);
316
+
250
317
  }
251
318
 
252
- // Add a new import file to the context
253
- // This has some previous directory context
254
- std::string Context::add_file(const std::string& base, const std::string& file, ParserState pstate)
319
+ // Add a new import to the context (called from `import_url`)
320
+ Include Context::load_import(const Importer& imp, ParserState pstate)
255
321
  {
256
- using namespace File;
257
- std::string path(make_canonical_path(file));
258
- std::string base_file(join_paths(base, path));
259
- if (style_sheets.count(base_file)) return base_file;
260
- std::vector<Sass_Queued> resolved(resolve_file(base, path));
322
+
323
+ // search for valid imports (ie. partials) on the filesystem
324
+ // this may return more than one valid result (ambiguous imp_path)
325
+ const std::vector<Include> resolved(find_includes(imp));
326
+
327
+ // error nicely on ambiguous imp_path
261
328
  if (resolved.size() > 1) {
262
329
  std::stringstream msg_stream;
263
330
  msg_stream << "It's not clear which file to import for ";
264
- msg_stream << "'@import \"" << file << "\"'." << "\n";
331
+ msg_stream << "'@import \"" << imp.imp_path << "\"'." << "\n";
265
332
  msg_stream << "Candidates:" << "\n";
266
333
  for (size_t i = 0, L = resolved.size(); i < L; ++i)
267
- { msg_stream << " " << resolved[i].load_path << "\n"; }
334
+ { msg_stream << " " << resolved[i].imp_path << "\n"; }
268
335
  msg_stream << "Please delete or rename all but one of these files." << "\n";
269
336
  error(msg_stream.str(), pstate);
270
337
  }
271
- if (resolved.size()) {
338
+
339
+ // process the resolved entry
340
+ else if (resolved.size() == 1) {
341
+ // use cache for the resource loading
342
+ if (sheets.count(resolved[0].abs_path)) return resolved[0];
343
+ // try to read the content of the resolved file entry
344
+ // the memory buffer returned must be freed by us!
272
345
  if (char* contents = read_file(resolved[0].abs_path)) {
273
- add_source(base_file, resolved[0].abs_path, contents);
274
- style_sheets[base_file] = 0;
275
- size_t i = queue.size() - 1;
276
- process_queue_entry(queue[i], i);
277
- return base_file;
346
+ // register the newly resolved file resource
347
+ register_resource(resolved[0], { contents, 0 });
348
+ // return resolved entry
349
+ return resolved[0];
350
+ }
351
+ }
352
+
353
+ // nothing found
354
+ return { imp, "" };
355
+
356
+ }
357
+
358
+ void Context::import_url (Import* imp, std::string load_path, const std::string& ctx_path) {
359
+
360
+ ParserState pstate(imp->pstate());
361
+ std::string imp_path(unquote(load_path));
362
+ std::string protocol("file");
363
+
364
+ using namespace Prelexer;
365
+ if (const char* proto = sequence< identifier, exactly<':'>, exactly<'/'>, exactly<'/'> >(imp_path.c_str())) {
366
+
367
+ protocol = std::string(imp_path.c_str(), proto - 3);
368
+ // std::cerr << "==================== " << protocol << "\n";
369
+ if (protocol.compare("file") && true) {
370
+
371
+ }
372
+ }
373
+
374
+ // add urls (protocol other than file) and urls without procotol to `urls` member
375
+ // ToDo: if ctx_path is already a file resource, we should not add it here?
376
+ if (imp->media_queries() || protocol != "file" || imp_path.substr(0, 2) == "//") {
377
+ imp->urls().push_back(SASS_MEMORY_NEW(mem, String_Quoted, imp->pstate(), load_path));
378
+ }
379
+ else if (imp_path.length() > 4 && imp_path.substr(imp_path.length() - 4, 4) == ".css") {
380
+ String_Constant* loc = SASS_MEMORY_NEW(mem, String_Constant, pstate, unquote(load_path));
381
+ Argument* loc_arg = SASS_MEMORY_NEW(mem, Argument, pstate, loc);
382
+ Arguments* loc_args = SASS_MEMORY_NEW(mem, Arguments, pstate);
383
+ (*loc_args) << loc_arg;
384
+ Function_Call* new_url = SASS_MEMORY_NEW(mem, Function_Call, pstate, "url", loc_args);
385
+ imp->urls().push_back(new_url);
386
+ }
387
+ else {
388
+ const Importer importer(imp_path, ctx_path);
389
+ Include include(load_import(importer, pstate));
390
+ if (include.abs_path.empty()) {
391
+ error("File to import not found or unreadable: " + imp_path + "\nParent style sheet: " + ctx_path, pstate);
392
+ }
393
+ imp->incs().push_back(include);
394
+ }
395
+
396
+ }
397
+
398
+
399
+ // call custom importers on the given (unquoted) load_path and eventually parse the resulting style_sheet
400
+ bool Context::call_loader(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp, std::vector<Sass_Importer_Entry> importers, bool only_one)
401
+ {
402
+ // unique counter
403
+ size_t count = 0;
404
+ // need one correct import
405
+ bool has_import = false;
406
+ // process all custom importers (or custom headers)
407
+ for (Sass_Importer_Entry& importer : importers) {
408
+ // int priority = sass_importer_get_priority(importer);
409
+ Sass_Importer_Fn fn = sass_importer_get_function(importer);
410
+ // skip importer if it returns NULL
411
+ if (Sass_Import_List includes =
412
+ fn(load_path.c_str(), importer, c_compiler)
413
+ ) {
414
+ // get c pointer copy to iterate over
415
+ Sass_Import_List it_includes = includes;
416
+ while (*it_includes) { ++count;
417
+ // create unique path to use as key
418
+ std::string uniq_path = load_path;
419
+ if (!only_one && count) {
420
+ std::stringstream path_strm;
421
+ path_strm << uniq_path << ":" << count;
422
+ uniq_path = path_strm.str();
423
+ }
424
+ // create the importer struct
425
+ Importer importer(uniq_path, ctx_path);
426
+ // query data from the current include
427
+ Sass_Import_Entry include = *it_includes;
428
+ char* source = sass_import_take_source(include);
429
+ char* srcmap = sass_import_take_source(include);
430
+ size_t line = sass_import_get_error_line(include);
431
+ size_t column = sass_import_get_error_column(include);
432
+ const char *abs_path = sass_import_get_abs_path(include);
433
+ // handle error message passed back from custom importer
434
+ // it may (or may not) override the line and column info
435
+ if (const char* err_message = sass_import_get_error_message(include)) {
436
+ if (source || srcmap) register_resource({ importer, uniq_path }, { source, srcmap });
437
+ if (line == std::string::npos && column == std::string::npos) error(err_message, pstate);
438
+ else error(err_message, ParserState(ctx_path, source, Position(line, column)));
439
+ }
440
+ // content for import was set
441
+ else if (source) {
442
+ // resolved abs_path should be set by custom importer
443
+ // use the created uniq_path as fallback (maybe enforce)
444
+ std::string path_key(abs_path ? abs_path : uniq_path);
445
+ // create the importer struct
446
+ Include include(importer, path_key);
447
+ // attach information to AST node
448
+ imp->incs().push_back(include);
449
+ // register the resource buffers
450
+ register_resource(include, { source, srcmap });
451
+ }
452
+ // only a path was retuned
453
+ // try to load it like normal
454
+ else if(abs_path) {
455
+ // checks some urls to preserve
456
+ // `http://`, `https://` and `//`
457
+ // or dispatchs to `import_file`
458
+ // which will check for a `.css` extension
459
+ // or resolves the file on the filesystem
460
+ // added and resolved via `add_file`
461
+ // finally stores everything on `imp`
462
+ import_url(imp, abs_path, ctx_path);
463
+ }
464
+ // move to next
465
+ ++it_includes;
466
+ }
467
+ // deallocate the returned memory
468
+ sass_delete_import_list(includes);
469
+ // set success flag
470
+ has_import = true;
471
+ // break out of loop
472
+ if (only_one) break;
278
473
  }
279
474
  }
280
- // now go the regular code path
281
- return add_file(path);
475
+ // return result
476
+ return has_import;
282
477
  }
283
478
 
284
479
  void register_function(Context&, Signature sig, Native_Function f, Env* env);
@@ -288,53 +483,148 @@ namespace Sass {
288
483
  void register_c_functions(Context&, Env* env, Sass_Function_List);
289
484
  void register_c_function(Context&, Env* env, Sass_Function_Entry);
290
485
 
291
- char* Context::compile_block(Block* root)
486
+ char* Context::render(Block* root)
292
487
  {
488
+ // check for valid block
293
489
  if (!root) return 0;
490
+ // start the render process
294
491
  root->perform(&emitter);
492
+ // finish emitter stream
295
493
  emitter.finalize();
494
+ // get the resulting buffer from stream
296
495
  OutputBuffer emitted = emitter.get_buffer();
297
- std::string output = emitted.buffer;
298
- if (!omit_source_map_url) {
299
- if (source_map_embed) {
300
- output += linefeed + format_embedded_source_map();
496
+ // should we append a source map url?
497
+ if (!c_options->omit_source_map_url) {
498
+ // generate an embeded source map
499
+ if (c_options->source_map_embed) {
500
+ emitted.buffer += linefeed;
501
+ emitted.buffer += format_embedded_source_map();
301
502
  }
503
+ // or just link the generated one
302
504
  else if (source_map_file != "") {
303
- output += linefeed + format_source_mapping_url(source_map_file);
505
+ emitted.buffer += linefeed;
506
+ emitted.buffer += format_source_mapping_url(source_map_file);
304
507
  }
305
508
  }
306
- return sass_strdup(output.c_str());
509
+ // create a copy of the resulting buffer string
510
+ // this must be freed or taken over by implementor
511
+ return sass_strdup(emitted.buffer.c_str());
307
512
  }
308
513
 
309
- void Context::process_queue_entry(Sass_Queued& entry, size_t i)
514
+ void Context::apply_custom_headers(Block* root, const char* ctx_path, ParserState pstate)
310
515
  {
311
- if (style_sheets[queue[i].load_path]) return;
516
+ // create a custom import to resolve headers
517
+ Import* imp = SASS_MEMORY_NEW(mem, Import, pstate);
518
+ // dispatch headers which will add custom functions
519
+ // custom headers are added to the import instance
520
+ call_headers(entry_path, ctx_path, pstate, imp);
521
+ // increase head count to skip later
522
+ head_imports += resources.size() - 1;
523
+ // add the statement if we have urls
524
+ if (!imp->urls().empty()) (*root) << imp;
525
+ // process all other resources (add Import_Stub nodes)
526
+ for (size_t i = 0, S = imp->incs().size(); i < S; ++i) {
527
+ (*root) << SASS_MEMORY_NEW(mem, Import_Stub, pstate, imp->incs()[i]);
528
+ }
529
+ }
530
+
531
+ Block* File_Context::parse()
532
+ {
533
+
534
+ // check if entry file is given
535
+ if (input_path.empty()) return 0;
536
+
537
+ // create absolute path from input filename
538
+ // ToDo: this should be resolved via custom importers
539
+ std::string abs_path(rel2abs(input_path, CWD));
540
+
541
+ // try to load the entry file
542
+ char* contents = read_file(abs_path);
543
+
544
+ // alternatively also look inside each include path folder
545
+ // I think this differs from ruby sass (IMO too late to remove)
546
+ for (size_t i = 0, S = include_paths.size(); contents == 0 && i < S; ++i) {
547
+ // build absolute path for this include path entry
548
+ abs_path = rel2abs(input_path, include_paths[i]);
549
+ // try to load the resulting path
550
+ contents = read_file(abs_path);
551
+ }
552
+
553
+ // abort early if no content could be loaded (various reasons)
554
+ if (!contents) throw "File to read not found or unreadable: " + input_path;
555
+
556
+ // store entry path
557
+ entry_path = abs_path;
558
+
559
+ // create entry only for import stack
312
560
  Sass_Import_Entry import = sass_make_import(
313
- queue[i].load_path.c_str(),
314
- queue[i].abs_path.c_str(),
315
- 0, 0
561
+ input_path.c_str(),
562
+ entry_path.c_str(),
563
+ contents,
564
+ 0
316
565
  );
566
+ // add the entry to the stack
317
567
  import_stack.push_back(import);
318
- // keep a copy of the path around (for parser states)
319
- strings.push_back(sass_strdup(queue[i].abs_path.c_str()));
320
- ParserState pstate(strings.back(), queue[i].source, i);
321
- Parser p(Parser::from_c_str(queue[i].source, *this, pstate));
322
- Block* ast = p.parse();
323
- sass_delete_import(import_stack.back());
324
- import_stack.pop_back();
325
- // ToDo: we store by load_path, which can lead
326
- // to duplicates if importer reports the same path
327
- // Maybe we should add an error for duplicates!?
328
- style_sheets[queue[i].load_path] = ast;
568
+
569
+ // create the source entry for file entry
570
+ register_resource({{ input_path, "." }, abs_path }, { contents, 0 });
571
+
572
+ // create root ast tree node
573
+ return compile();
574
+
329
575
  }
330
576
 
331
- Block* Context::parse_file()
577
+ Block* Data_Context::parse()
332
578
  {
333
- Block* root = 0;
334
- for (size_t i = 0; i < queue.size(); ++i) {
335
- process_queue_entry(queue[i], i);
336
- if (i == 0) root = style_sheets[queue[i].load_path];
579
+
580
+ // check if source string is given
581
+ if (!source_c_str) return 0;
582
+
583
+ // convert indented sass syntax
584
+ if(c_options->is_indented_syntax_src) {
585
+ // call sass2scss to convert the string
586
+ char * converted = sass2scss(source_c_str,
587
+ // preserve the structure as much as possible
588
+ SASS2SCSS_PRETTIFY_1 | SASS2SCSS_KEEP_COMMENT);
589
+ // replace old source_c_str with converted
590
+ free(source_c_str); source_c_str = converted;
337
591
  }
592
+
593
+ // remember entry path (defaults to stdin for string)
594
+ entry_path = input_path.empty() ? "stdin" : input_path;
595
+
596
+ // ToDo: this may be resolved via custom importers
597
+ std::string abs_path(rel2abs(entry_path));
598
+ char* abs_path_c_str = sass_strdup(abs_path.c_str());
599
+ strings.push_back(abs_path_c_str);
600
+
601
+ // create entry only for the import stack
602
+ Sass_Import_Entry import = sass_make_import(
603
+ entry_path.c_str(),
604
+ abs_path_c_str,
605
+ source_c_str,
606
+ srcmap_c_str
607
+ );
608
+ // add the entry to the stack
609
+ import_stack.push_back(import);
610
+
611
+ // register a synthetic resource (path does not really exist, skip in includes)
612
+ register_resource({{ input_path, "." }, input_path }, { source_c_str, srcmap_c_str });
613
+
614
+ // create root ast tree node
615
+ return compile();
616
+ }
617
+
618
+
619
+
620
+ // parse root block from includes
621
+ Block* Context::compile()
622
+ {
623
+ // abort if there is no data
624
+ if (resources.size() == 0) return 0;
625
+ // get root block from the first style sheet
626
+ Block* root = sheets.at(entry_path).root;
627
+ // abort on invalid root
338
628
  if (root == 0) return 0;
339
629
 
340
630
  Env global; // create root environment
@@ -367,39 +657,11 @@ namespace Sass {
367
657
  // return processed tree
368
658
  return root;
369
659
  }
370
- // EO parse_file
371
-
372
- Block* Context::parse_string()
373
- {
374
- if (!source_c_str) return 0;
375
- queue.clear();
376
- if(is_indented_syntax_src) {
377
- char * contents = sass2scss(source_c_str, SASS2SCSS_PRETTIFY_1 | SASS2SCSS_KEEP_COMMENT);
378
- add_source(input_path, input_path, contents);
379
- free(source_c_str);
380
- return parse_file();
381
- }
382
- add_source(input_path, input_path, source_c_str);
383
- size_t idx = queue.size() - 1;
384
- process_queue_entry(queue[idx], idx);
385
- return parse_file();
386
- }
387
-
388
- char* Context::compile_file()
389
- {
390
- // returns NULL if something fails
391
- return compile_block(parse_file());
392
- }
393
-
394
- char* Context::compile_string()
395
- {
396
- // returns NULL if something fails
397
- return compile_block(parse_string());
398
- }
660
+ // EO compile
399
661
 
400
662
  std::string Context::format_embedded_source_map()
401
663
  {
402
- std::string map = emitter.generate_source_map(*this);
664
+ std::string map = emitter.render_srcmap(*this);
403
665
  std::istringstream is( map );
404
666
  std::ostringstream buffer;
405
667
  base64::encoder E;
@@ -411,15 +673,15 @@ namespace Sass {
411
673
 
412
674
  std::string Context::format_source_mapping_url(const std::string& file)
413
675
  {
414
- std::string url = resolve_relative_path(file, output_path, cwd);
676
+ std::string url = abs2rel(file, output_path, CWD);
415
677
  return "/*# sourceMappingURL=" + url + " */";
416
678
  }
417
679
 
418
- char* Context::generate_source_map()
680
+ char* Context::render_srcmap()
419
681
  {
420
682
  if (source_map_file == "") return 0;
421
683
  char* result = 0;
422
- std::string map = emitter.generate_source_map(*this);
684
+ std::string map = emitter.render_srcmap(*this);
423
685
  result = sass_strdup(map.c_str());
424
686
  return result;
425
687
  }
@@ -429,7 +691,7 @@ namespace Sass {
429
691
  // we probably always want to skip the header includes?
430
692
  std::vector<std::string> Context::get_included_files(bool skip, size_t headers)
431
693
  {
432
- // create a copy of the vector for manupulations
694
+ // create a copy of the vector for manipulations
433
695
  std::vector<std::string> includes = included_files;
434
696
  if (includes.size() == 0) return includes;
435
697
  if (skip) { includes.erase( includes.begin(), includes.begin() + 1 + headers); }
@@ -439,11 +701,6 @@ namespace Sass {
439
701
  return includes;
440
702
  }
441
703
 
442
- std::string Context::get_cwd()
443
- {
444
- return Sass::File::get_cwd();
445
- }
446
-
447
704
  void register_function(Context& ctx, Signature sig, Native_Function f, Env* env)
448
705
  {
449
706
  Definition* def = make_native_function(sig, f, ctx);