diff --git a/docs/api-function-example.md b/docs/api-function-example.md index 46b4b50635..0e41940ce3 100644 --- a/docs/api-function-example.md +++ b/docs/api-function-example.md @@ -5,8 +5,17 @@ #include #include "sass/context.h" -union Sass_Value* call_fn_foo(const union Sass_Value* s_args, void* cookie) +union Sass_Value* call_fn_foo(const union Sass_Value* s_args, Sass_Function_Entry cb, struct Sass_Compiler* comp) { + // get context/option struct associated with this compiler + struct Sass_Context* ctx = sass_compiler_get_context(comp); + struct Sass_Options* opts = sass_compiler_get_options(comp); + // get information about previous importer entry from the stack + struct Sass_Import* import = sass_compiler_get_last_import(comp); + const char* prev_abs_path = sass_import_get_abs_path(import); + const char* prev_imp_path = sass_import_get_imp_path(import); + // get the cookie from function descriptor + void* cookie = sass_function_get_cookie(cb); // we actually abuse the void* to store an "int" return sass_make_number((intptr_t)cookie, "px"); } @@ -23,11 +32,11 @@ int main( int argc, const char* argv[] ) struct Sass_Options* ctx_opt = sass_context_get_options(ctx); // allocate a custom function caller - Sass_C_Function_Callback fn_foo = + Sass_Function_Entry fn_foo = sass_make_function("foo()", call_fn_foo, (void*)42); // create list of all custom functions - Sass_C_Function_List fn_list = sass_make_function_list(1); + Sass_Function_List fn_list = sass_make_function_list(1); sass_function_set_list_entry(fn_list, 0, fn_foo); sass_option_set_c_functions(ctx_opt, fn_list); diff --git a/docs/api-function-internal.md b/docs/api-function-internal.md index 0077fd4265..69d81d04d1 100644 --- a/docs/api-function-internal.md +++ b/docs/api-function-internal.md @@ -1,8 +1,8 @@ ```C // Struct to hold custom function callback -struct Sass_C_Function_Descriptor { - const char* signature; - Sass_C_Function function; - void* cookie; +struct Sass_Function { + const char* signature; + Sass_Function_Fn function; + void* cookie; }; ``` diff --git a/docs/api-function.md b/docs/api-function.md index 4ef509d0c2..eeaa61a1d7 100644 --- a/docs/api-function.md +++ b/docs/api-function.md @@ -19,26 +19,28 @@ Note: The fallback implementation will be given the name of the called function ```C // Forward declaration -struct Sass_C_Function_Descriptor; +struct Sass_Compiler; +struct Sass_Function; -// Typedef defining null terminated list of custom callbacks -typedef struct Sass_C_Function_Descriptor* (*Sass_C_Function_List); -typedef struct Sass_C_Function_Descriptor (*Sass_C_Function_Callback); -// Typedef defining custom function prototype and its return value type -typedef union Sass_Value*(*Sass_C_Function) (const union Sass_Value*, void* cookie); +// Typedef helpers for custom functions lists +typedef struct Sass_Function (*Sass_Function_Entry); +typedef struct Sass_Function* (*Sass_Function_List); +// Typedef defining function signature and return type +typedef union Sass_Value* (*Sass_Function_Fn) + (const union Sass_Value*, Sass_Function_Entry cb, struct Sass_Compiler* compiler); // Creators for sass function list and function descriptors -Sass_C_Function_List sass_make_function_list (size_t length); -Sass_C_Function_Callback sass_make_function (const char* signature, Sass_C_Function fn, void* cookie); +ADDAPI Sass_Function_List ADDCALL sass_make_function_list (size_t length); +ADDAPI Sass_Function_Entry ADDCALL sass_make_function (const char* signature, Sass_Function_Fn cb, void* cookie); // Setters and getters for callbacks on function lists -Sass_C_Function_Callback sass_function_get_list_entry(Sass_C_Function_List list, size_t pos); -void sass_function_set_list_entry(Sass_C_Function_List list, size_t pos, Sass_C_Function_Callback cb); +ADDAPI Sass_Function_Entry ADDCALL sass_function_get_list_entry(Sass_Function_List list, size_t pos); +ADDAPI void ADDCALL sass_function_set_list_entry(Sass_Function_List list, size_t pos, Sass_Function_Entry cb); // Getters for custom function descriptors -const char* sass_function_get_signature (Sass_C_Function_Callback fn); -Sass_C_Function sass_function_get_function (Sass_C_Function_Callback fn); -void* sass_function_get_cookie (Sass_C_Function_Callback fn); +ADDAPI const char* ADDCALL sass_function_get_signature (Sass_Function_Entry cb); +ADDAPI Sass_Function_Fn ADDCALL sass_function_get_function (Sass_Function_Entry cb); +ADDAPI void* ADDCALL sass_function_get_cookie (Sass_Function_Entry cb); ``` ### More links diff --git a/docs/api-importer-internal.md b/docs/api-importer-internal.md index f7a63e70fa..63d70fe757 100644 --- a/docs/api-importer-internal.md +++ b/docs/api-importer-internal.md @@ -1,15 +1,20 @@ ```C // External import entry struct Sass_Import { - char* rel; - char* abs; + char* imp_path; // path as found in the import statement + char *abs_path; // path after importer has resolved it char* source; char* srcmap; + // error handling + char* error; + size_t line; + size_t column; }; // Struct to hold importer callback -struct Sass_C_Import_Descriptor { - Sass_C_Import_Fn function; +struct Sass_Importer { + Sass_Importer_Fn importer; + double priority; void* cookie; }; ``` diff --git a/src/ast.hpp b/src/ast.hpp index 8e24ca9790..d141b9dfa3 100644 --- a/src/ast.hpp +++ b/src/ast.hpp @@ -538,26 +538,32 @@ namespace Sass { // necessary to store a list of each in an Import node. //////////////////////////////////////////////////////////////////////////// class Import : public Statement { - std::vector files_; - std::vector urls_; - ADD_PROPERTY(List*, media_queries); + std::vector urls_; + std::vector incs_; + ADD_PROPERTY(List*, media_queries); public: Import(ParserState pstate) : Statement(pstate), - files_(std::vector()), urls_(std::vector()), + incs_(std::vector()), media_queries_(0) { statement_type(IMPORT); } - std::vector& files() { return files_; } - std::vector& urls() { return urls_; } + std::vector& urls() { return urls_; } + std::vector& incs() { return incs_; } ATTACH_OPERATIONS() }; + // not yet resolved single import + // so far we only know requested name class Import_Stub : public Statement { - ADD_PROPERTY(std::string, file_name) + Include resource_; public: - Import_Stub(ParserState pstate, std::string f) - : Statement(pstate), file_name_(f) + std::string abs_path() { return resource_.abs_path; }; + std::string imp_path() { return resource_.imp_path; }; + Include resource() { return resource_; }; + + Import_Stub(ParserState pstate, Include res) + : Statement(pstate), resource_(res) { statement_type(IMPORT_STUB); } ATTACH_OPERATIONS() }; diff --git a/src/ast_fwd_decl.hpp b/src/ast_fwd_decl.hpp index d66f47a7a8..7d4db19e55 100644 --- a/src/ast_fwd_decl.hpp +++ b/src/ast_fwd_decl.hpp @@ -83,6 +83,9 @@ namespace Sass { class Complex_Selector; class Selector_List; + // common classes + class Context; + } #endif diff --git a/src/context.cpp b/src/context.cpp index 104af31140..4339bb3480 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -38,88 +38,80 @@ namespace Sass { using namespace File; using namespace Sass; - Sass_Queued::Sass_Queued(const std::string& load_path, const std::string& abs_path, const char* source) + inline bool sort_importers (const Sass_Importer_Entry& i, const Sass_Importer_Entry& j) + { return sass_importer_get_priority(i) > sass_importer_get_priority(j); } + + static std::string safe_input(const char* in_path) { - this->load_path = load_path; - this->abs_path = abs_path; - this->source = source; + // enforce some safe defaults + // used to create relative file links + std::string safe_path(in_path ? in_path : ""); + return safe_path == "" ? "stdin" : safe_path; } - inline bool sort_importers (const Sass_Importer_Entry& i, const Sass_Importer_Entry& j) - { return sass_importer_get_priority(i) > sass_importer_get_priority(j); } + static std::string safe_output(const char* out_path, const std::string& input_path = "") + { + std::string safe_path(out_path ? out_path : ""); + // maybe we can extract an output path from input path + if (safe_path == "" && input_path != "") { + int lastindex = static_cast(input_path.find_last_of(".")); + return (lastindex > -1 ? input_path.substr(0, lastindex) : input_path) + ".css"; + } + // enforce some safe defaults + // used to create relative file links + return safe_path == "" ? "stdout" : safe_path; + } - Context::Context(Context::Data initializers) - : // Output(this), + Context::Context(struct Sass_Context* c_ctx) + : CWD(File::get_cwd()), + entry_path(""), head_imports(0), mem(Memory_Manager()), - c_options (initializers.c_options()), - c_compiler (initializers.c_compiler()), - source_c_str (initializers.source_c_str()), - sources (std::vector()), - strings (std::vector()), - plugin_paths (initializers.plugin_paths()), - include_paths (initializers.include_paths()), - queue (std::vector()), - style_sheets (std::map()), - emitter (this), + plugins(), + emitter(this), + + strings(), + resources(), + sheets(), + subset_map(), + import_stack(), + + c_options (c_ctx), + c_headers (std::vector()), c_importers (std::vector()), c_functions (std::vector()), - indent (initializers.indent()), - linefeed (initializers.linefeed()), - input_path (make_canonical_path(initializers.input_path())), - output_path (make_canonical_path(initializers.output_path())), - source_comments (initializers.source_comments()), - output_style (initializers.output_style()), - source_map_file (make_canonical_path(initializers.source_map_file())), - source_map_root (initializers.source_map_root()), // pass-through - source_map_embed (initializers.source_map_embed()), - source_map_contents (initializers.source_map_contents()), - omit_source_map_url (initializers.omit_source_map_url()), - is_indented_syntax_src (initializers.is_indented_syntax_src()), - precision (initializers.precision()), - plugins(), - subset_map (Subset_Map >()) - { - cwd = get_cwd(); + indent (safe_str(c_options->indent, " ")), + linefeed (safe_str(c_options->linefeed, "\n")), - // enforce some safe defaults - // used to create relative file links - if (input_path == "") input_path = "stdin"; - if (output_path == "") output_path = "stdout"; + input_path (make_canonical_path(safe_input(c_options->input_path))), + output_path (make_canonical_path(safe_output(c_options->output_path, input_path))), + source_map_file (make_canonical_path(safe_str(c_options->source_map_file, ""))), + source_map_root (make_canonical_path(safe_str(c_options->source_map_root, ""))) + + { - include_paths.push_back(cwd); - collect_include_paths(initializers.include_paths_c_str()); + // add cwd to include paths + include_paths.push_back(CWD); + + // collect more paths from different options + collect_include_paths(sass_option_get_include_path(c_options)); // collect_include_paths(initializers.include_paths_array()); - collect_plugin_paths(initializers.plugin_paths_c_str()); + collect_plugin_paths(sass_option_get_plugin_path(c_options)); // collect_plugin_paths(initializers.plugin_paths_array()); - for (size_t i = 0, S = plugin_paths.size(); i < S; ++i) { - plugins.load_plugins(plugin_paths[i]); - } - - for(auto fn : plugins.get_functions()) { - c_functions.push_back(fn); - } - for(auto fn : plugins.get_headers()) { - c_headers.push_back(fn); - } - for(auto fn : plugins.get_importers()) { - c_importers.push_back(fn); - } + // load plugins and register custom behaviors + for(auto plug : plugin_paths) plugins.load_plugins(plug); + for(auto fn : plugins.get_headers()) c_headers.push_back(fn); + for(auto fn : plugins.get_importers()) c_importers.push_back(fn); + for(auto fn : plugins.get_functions()) c_functions.push_back(fn); + // sort the items by priority (lowest first) sort (c_headers.begin(), c_headers.end(), sort_importers); sort (c_importers.begin(), c_importers.end(), sort_importers); - std::string entry_point = initializers.entry_point(); - if (!entry_point.empty()) { - std::string result(add_file(entry_point, true)); - if (result.empty()) { - throw "File to read not found or unreadable: " + entry_point; - } - } - emitter.set_filename(abs2rel(output_path, source_map_file, cwd)); + emitter.set_filename(abs2rel(output_path, source_map_file, CWD)); } @@ -142,16 +134,35 @@ namespace Sass { Context::~Context() { - // make sure we free the source even if not processed! - if (sources.size() == 0 && source_c_str) free(source_c_str); - // sources are allocated by strdup or malloc (overtaken from C code) - for (size_t i = 0; i < sources.size(); ++i) free(sources[i]); + // resources were allocated by strdup or malloc + for (size_t i = 0; i < resources.size(); ++i) { + free(resources[i].contents); + free(resources[i].srcmap); + } // free all strings we kept alive during compiler execution for (size_t n = 0; n < strings.size(); ++n) free(strings[n]); // everything that gets put into sources will be freed by us - for (size_t m = 0; m < import_stack.size(); ++m) sass_delete_import(import_stack[m]); + // this shouldn't have anything in it anyway!? + for (size_t m = 0; m < import_stack.size(); ++m) { + sass_import_take_source(import_stack[m]); + sass_import_take_srcmap(import_stack[m]); + sass_delete_import(import_stack[m]); + } // clear inner structures (vectors) and input source - sources.clear(); import_stack.clear(); source_c_str = 0; + resources.clear(); import_stack.clear(); + } + + Data_Context::~Data_Context() + { + // --> this will be freed by resources + // make sure we free the source even if not processed! + // if (resources.size() == 0 && source_c_str) free(source_c_str); + // if (resources.size() == 0 && srcmap_c_str) free(srcmap_c_str); + // source_c_str = 0; srcmap_c_str = 0; + } + + File_Context::~File_Context() + { } void Context::collect_include_paths(const char* paths_str) @@ -181,10 +192,9 @@ namespace Sass { void Context::collect_include_paths(const char** paths_array) { - if (paths_array) { - for (size_t i = 0; paths_array[i]; i++) { - collect_include_paths(paths_array[i]); - } + if (!paths_array) return; + for (size_t i = 0; paths_array[i]; i++) { + collect_include_paths(paths_array[i]); } } @@ -215,70 +225,255 @@ namespace Sass { void Context::collect_plugin_paths(const char** paths_array) { - if (paths_array) { - for (size_t i = 0; paths_array[i]; i++) { - collect_plugin_paths(paths_array[i]); - } + if (!paths_array) return; + for (size_t i = 0; paths_array[i]; i++) { + collect_plugin_paths(paths_array[i]); } } - void Context::add_source(std::string load_path, std::string abs_path, char* contents) + + + // resolve the imp_path in base_path or include_paths + // looks for alternatives and returns a list from one directory + std::vector Context::find_includes(const Importer& import) { - sources.push_back(contents); - included_files.push_back(abs_path); - queue.push_back(Sass_Queued(load_path, abs_path, contents)); - emitter.add_source_index(sources.size() - 1); - srcmap_links.push_back(abs2rel(abs_path, source_map_file, cwd)); + // make sure we resolve against an absolute path + std::string base_path(rel2abs(import.base_path)); + // first try to resolve the load path relative to the base path + std::vector vec(resolve_includes(base_path, import.imp_path)); + // then search in every include path (but only if nothing found yet) + for (size_t i = 0, S = include_paths.size(); vec.size() == 0 && i < S; ++i) + { + // call resolve_includes and individual base path and append all results + std::vector resolved(resolve_includes(include_paths[i], import.imp_path)); + if (resolved.size()) vec.insert(vec.end(), resolved.begin(), resolved.end()); + } + // return vector + return vec; } - // Add a new import file to the context - std::string Context::add_file(const std::string& file, bool delay) + + // register include with resolved path and its content + // memory of the resources will be freed by us on exit + void Context::register_resource(const Include& inc, const Resource& res) { - using namespace File; - std::string path(make_canonical_path(file)); - std::string resolved(find_file(path, include_paths)); - if (resolved == "") return resolved; - if (char* contents = read_file(resolved)) { - add_source(path, resolved, contents); - style_sheets[path] = 0; - if (delay == false) { - size_t i = queue.size() - 1; - process_queue_entry(queue[i], i); - } - return path; + + // do not parse same resource twice + // maybe raise an error in this case + if (sheets.count(inc.abs_path)) { + free(res.contents); free(res.srcmap); + throw std::runtime_error("duplicate resource registered"); + return; } - return std::string(""); + + // get index for this resource + size_t idx = resources.size(); + + // tell emitter about new resource + emitter.add_source_index(idx); + + // put resources under our control + // the memory will be freed later + resources.push_back(res); + + // add a relative link to the working directory + included_files.push_back(abs2rel(inc.abs_path, CWD)); + // add a relative link to the source map output file + srcmap_links.push_back(abs2rel(inc.abs_path, source_map_file, CWD)); + + // get pointer to the loaded content + Sass_Import_Entry import = sass_make_import( + inc.imp_path.c_str(), + inc.abs_path.c_str(), + res.contents, + res.srcmap + ); + // add the entry to the stack + import_stack.push_back(import); + + // get pointer to the loaded content + const char* contents = resources[idx].contents; + // keep a copy of the path around (for parserstates) + // ToDo: we clean it, but still not very elegant!? + strings.push_back(sass_strdup(inc.abs_path.c_str())); + // create the initial parser state from resource + ParserState pstate(strings.back(), contents, idx); + // create a parser instance from the given c_str buffer + Parser p(Parser::from_c_str(contents, *this, pstate)); + // do not yet dispose these buffers + sass_import_take_source(import); + sass_import_take_srcmap(import); + // then parse the root block + Block* root = p.parse(); + // delete memory of current stack frame + sass_delete_import(import_stack.back()); + // remove current stack frame + import_stack.pop_back(); + // create key/value pair for ast node + std::pair + ast_pair(inc.abs_path, { res, root }); + // register resulting resource + sheets.insert(ast_pair); + } - // Add a new import file to the context - // This has some previous directory context - std::string Context::add_file(const std::string& base, const std::string& file, ParserState pstate) + // Add a new import to the context (called from `import_url`) + Include Context::load_import(const Importer& imp, ParserState pstate) { - using namespace File; - std::string path(make_canonical_path(file)); - std::string base_file(join_paths(base, path)); - if (style_sheets.count(base_file)) return base_file; - std::vector resolved(resolve_includes(base, path)); + + // search for valid imports (ie. partials) on the filesystem + // this may return more than one valid result (ambiguous imp_path) + const std::vector resolved(find_includes(imp)); + + // error nicely on ambiguous imp_path if (resolved.size() > 1) { std::stringstream msg_stream; msg_stream << "It's not clear which file to import for "; - msg_stream << "'@import \"" << file << "\"'." << "\n"; + msg_stream << "'@import \"" << imp.imp_path << "\"'." << "\n"; msg_stream << "Candidates:" << "\n"; for (size_t i = 0, L = resolved.size(); i < L; ++i) - { msg_stream << " " << resolved[i].load_path << "\n"; } + { msg_stream << " " << resolved[i].imp_path << "\n"; } msg_stream << "Please delete or rename all but one of these files." << "\n"; error(msg_stream.str(), pstate); } - if (resolved.size()) { + + // process the resolved entry + else if (resolved.size() == 1) { + // use cache for the resource loading + if (sheets.count(resolved[0].abs_path)) return resolved[0]; + // try to read the content of the resolved file entry + // the memory buffer returned must be freed by us! if (char* contents = read_file(resolved[0].abs_path)) { - add_source(base_file, resolved[0].abs_path, contents); - style_sheets[base_file] = 0; - size_t i = queue.size() - 1; - process_queue_entry(queue[i], i); - return base_file; + // register the newly resolved file resource + register_resource(resolved[0], { contents, 0 }); + // return resolved entry + return resolved[0]; } } - // now go the regular code path - return add_file(path); + + // nothing found + return { imp, "" }; + + } + + void Context::import_url (Import* imp, std::string load_path, const std::string& ctx_path) { + + ParserState pstate(imp->pstate()); + std::string imp_path(unquote(load_path)); + std::string protocol("file"); + + using namespace Prelexer; + if (const char* proto = sequence< identifier, exactly<':'>, exactly<'/'>, exactly<'/'> >(imp_path.c_str())) { + + protocol = std::string(imp_path.c_str(), proto - 3); + // std::cerr << "==================== " << protocol << "\n"; + if (protocol.compare("file") && true) { + + } + } + + // add urls (protocol other than file) and urls without procotol to `urls` member + // ToDo: if ctx_path is already a file resource, we should not add it here? + if (imp->media_queries() || protocol != "file" || imp_path.substr(0, 2) == "//") { + imp->urls().push_back(SASS_MEMORY_NEW(mem, String_Quoted, imp->pstate(), load_path)); + } + else if (imp_path.length() > 4 && imp_path.substr(imp_path.length() - 4, 4) == ".css") { + String_Constant* loc = SASS_MEMORY_NEW(mem, String_Constant, pstate, unquote(load_path)); + Argument* loc_arg = SASS_MEMORY_NEW(mem, Argument, pstate, loc); + Arguments* loc_args = SASS_MEMORY_NEW(mem, Arguments, pstate); + (*loc_args) << loc_arg; + Function_Call* new_url = SASS_MEMORY_NEW(mem, Function_Call, pstate, "url", loc_args); + imp->urls().push_back(new_url); + } + else { + const Importer importer(imp_path, ctx_path); + Include include(load_import(importer, pstate)); + if (include.abs_path.empty()) { + error("File to import not found or unreadable: " + imp_path + "\nParent style sheet: " + ctx_path, pstate); + } + imp->incs().push_back(include); + } + + } + + + // call custom importers on the given (unquoted) load_path and eventually parse the resulting style_sheet + bool Context::call_loader(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp, std::vector importers, bool only_one) + { + // unique counter + size_t count = 0; + // need one correct import + bool has_import = false; + // process all custom importers (or custom headers) + for (Sass_Importer_Entry& importer : importers) { + // int priority = sass_importer_get_priority(importer); + Sass_Importer_Fn fn = sass_importer_get_function(importer); + // skip importer if it returns NULL + if (Sass_Import_List includes = + fn(load_path.c_str(), importer, c_compiler) + ) { + // get c pointer copy to iterate over + Sass_Import_List it_includes = includes; + while (*it_includes) { ++count; + // create unique path to use as key + std::string uniq_path = load_path; + if (!only_one && count) { + std::stringstream path_strm; + path_strm << uniq_path << ":" << count; + uniq_path = path_strm.str(); + } + // create the importer struct + Importer importer(uniq_path, ctx_path); + // query data from the current include + Sass_Import_Entry include = *it_includes; + char* source = sass_import_take_source(include); + char* srcmap = sass_import_take_source(include); + size_t line = sass_import_get_error_line(include); + size_t column = sass_import_get_error_column(include); + const char *abs_path = sass_import_get_abs_path(include); + // handle error message passed back from custom importer + // it may (or may not) override the line and column info + if (const char* err_message = sass_import_get_error_message(include)) { + if (source || srcmap) register_resource({ importer, uniq_path }, { source, srcmap }); + if (line == std::string::npos && column == std::string::npos) error(err_message, pstate); + else error(err_message, ParserState(ctx_path, source, Position(line, column))); + } + // content for import was set + else if (source) { + // resolved abs_path should be set by custom importer + // use the created uniq_path as fallback (maybe enforce) + std::string path_key(abs_path ? abs_path : uniq_path); + // create the importer struct + Include include(importer, path_key); + // attach information to AST node + imp->incs().push_back(include); + // register the resource buffers + register_resource(include, { source, srcmap }); + } + // only a path was retuned + // try to load it like normal + else if(abs_path) { + // checks some urls to preserve + // `http://`, `https://` and `//` + // or dispatchs to `import_file` + // which will check for a `.css` extension + // or resolves the file on the filesystem + // added and resolved via `add_file` + // finally stores everything on `imp` + import_url(imp, abs_path, ctx_path); + } + // move to next + ++it_includes; + } + // deallocate the returned memory + sass_delete_import_list(includes); + // set success flag + has_import = true; + // break out of loop + if (only_one) break; + } + } + // return result + return has_import; } void register_function(Context&, Signature sig, Native_Function f, Env* env); @@ -290,51 +485,146 @@ namespace Sass { char* Context::render(Block* root) { + // check for valid block if (!root) return 0; + // start the render process root->perform(&emitter); + // finish emitter stream emitter.finalize(); + // get the resulting buffer from stream OutputBuffer emitted = emitter.get_buffer(); - std::string output = emitted.buffer; - if (!omit_source_map_url) { - if (source_map_embed) { - output += linefeed + format_embedded_source_map(); + // should we append a source map url? + if (!c_options->omit_source_map_url) { + // generate an embeded source map + if (c_options->source_map_embed) { + emitted.buffer += linefeed; + emitted.buffer += format_embedded_source_map(); } + // or just link the generated one else if (source_map_file != "") { - output += linefeed + format_source_mapping_url(source_map_file); + emitted.buffer += linefeed; + emitted.buffer += format_source_mapping_url(source_map_file); } } - return sass_strdup(output.c_str()); + // create a copy of the resulting buffer string + // this must be freed or taken over by implementor + return sass_strdup(emitted.buffer.c_str()); + } + + void Context::apply_custom_headers(Block* root, const char* ctx_path, ParserState pstate) + { + // create a custom import to resolve headers + Import* imp = SASS_MEMORY_NEW(mem, Import, pstate); + // dispatch headers which will add custom functions + // custom headers are added to the import instance + call_headers(entry_path, ctx_path, pstate, imp); + // increase head count to skip later + head_imports += resources.size() - 1; + // add the statement if we have urls + if (!imp->urls().empty()) (*root) << imp; + // process all other resources (add Import_Stub nodes) + for (size_t i = 0, S = imp->incs().size(); i < S; ++i) { + (*root) << SASS_MEMORY_NEW(mem, Import_Stub, pstate, imp->incs()[i]); + } } - void Context::process_queue_entry(Sass_Queued& entry, size_t i) + Block* File_Context::parse() { - if (style_sheets[queue[i].load_path]) return; + + // check if entry file is given + if (input_path.empty()) return 0; + + // create absolute path from input filename + // ToDo: this should be resolved via custom importers + std::string abs_path(rel2abs(input_path, CWD)); + + // try to load the entry file + char* contents = read_file(abs_path); + + // alternatively also look inside each include path folder + // I think this differs from ruby sass (IMO too late to remove) + for (size_t i = 0, S = include_paths.size(); contents == 0 && i < S; ++i) { + // build absolute path for this include path entry + abs_path = rel2abs(input_path, include_paths[i]); + // try to load the resulting path + contents = read_file(abs_path); + } + + // abort early if no content could be loaded (various reasons) + if (!contents) throw "File to read not found or unreadable: " + input_path; + + // store entry path + entry_path = abs_path; + + // create entry only for import stack Sass_Import_Entry import = sass_make_import( - queue[i].load_path.c_str(), - queue[i].abs_path.c_str(), - 0, 0 + input_path.c_str(), + entry_path.c_str(), + contents, + 0 ); + // add the entry to the stack import_stack.push_back(import); - // keep a copy of the path around (for parser states) - strings.push_back(sass_strdup(queue[i].abs_path.c_str())); - ParserState pstate(strings.back(), queue[i].source, i); - Parser p(Parser::from_c_str(queue[i].source, *this, pstate)); - Block* ast = p.parse(); - sass_delete_import(import_stack.back()); - import_stack.pop_back(); - // ToDo: we store by load_path, which can lead - // to duplicates if importer reports the same path - // Maybe we should add an error for duplicates!? - style_sheets[queue[i].load_path] = ast; + + // create the source entry for file entry + register_resource({{ input_path, "." }, abs_path }, { contents, 0 }); + + // create root ast tree node + return compile(); + } - Block* Context::parse_file() + Block* Data_Context::parse() { - Block* root = 0; - for (size_t i = 0; i < queue.size(); ++i) { - process_queue_entry(queue[i], i); - if (i == 0) root = style_sheets[queue[i].load_path]; + + // check if source string is given + if (!source_c_str) return 0; + + // convert indented sass syntax + if(c_options->is_indented_syntax_src) { + // call sass2scss to convert the string + char * converted = sass2scss(source_c_str, + // preserve the structure as much as possible + SASS2SCSS_PRETTIFY_1 | SASS2SCSS_KEEP_COMMENT); + // replace old source_c_str with converted + free(source_c_str); source_c_str = converted; } + + // remember entry path (defaults to stdin for string) + entry_path = input_path.empty() ? "stdin" : input_path; + + // ToDo: this may be resolved via custom importers + std::string abs_path(rel2abs(entry_path)); + char* abs_path_c_str = sass_strdup(abs_path.c_str()); + strings.push_back(abs_path_c_str); + + // create entry only for the import stack + Sass_Import_Entry import = sass_make_import( + entry_path.c_str(), + abs_path_c_str, + source_c_str, + srcmap_c_str + ); + // add the entry to the stack + import_stack.push_back(import); + + // register a synthetic resource (path does not really exist, skip in includes) + register_resource({{ input_path, "." }, input_path }, { source_c_str, srcmap_c_str }); + + // create root ast tree node + return compile(); + } + + + + // parse root block from includes + Block* Context::compile() + { + // abort if there is no data + if (resources.size() == 0) return 0; + // get root block from the first style sheet + Block* root = sheets.at(entry_path).root; + // abort on invalid root if (root == 0) return 0; Env global; // create root environment @@ -367,35 +657,7 @@ namespace Sass { // return processed tree return root; } - // EO parse_file - - Block* Context::parse_string() - { - if (!source_c_str) return 0; - queue.clear(); - if(is_indented_syntax_src) { - char * contents = sass2scss(source_c_str, SASS2SCSS_PRETTIFY_1 | SASS2SCSS_KEEP_COMMENT); - add_source(input_path, input_path, contents); - free(source_c_str); - return parse_file(); - } - add_source(input_path, input_path, source_c_str); - size_t idx = queue.size() - 1; - process_queue_entry(queue[idx], idx); - return parse_file(); - } - - char* Context::compile_file() - { - // returns NULL if something fails - return render(parse_file()); - } - - char* Context::compile_string() - { - // returns NULL if something fails - return render(parse_string()); - } + // EO compile std::string Context::format_embedded_source_map() { @@ -411,7 +673,7 @@ namespace Sass { std::string Context::format_source_mapping_url(const std::string& file) { - std::string url = abs2rel(file, output_path, cwd); + std::string url = abs2rel(file, output_path, CWD); return "/*# sourceMappingURL=" + url + " */"; } @@ -429,7 +691,7 @@ namespace Sass { // we probably always want to skip the header includes? std::vector Context::get_included_files(bool skip, size_t headers) { - // create a copy of the vector for manupulations + // create a copy of the vector for manipulations std::vector includes = included_files; if (includes.size() == 0) return includes; if (skip) { includes.erase( includes.begin(), includes.begin() + 1 + headers); } @@ -439,11 +701,6 @@ namespace Sass { return includes; } - std::string Context::get_cwd() - { - return Sass::File::get_cwd(); - } - void register_function(Context& ctx, Signature sig, Native_Function f, Env* env) { Definition* def = make_native_function(sig, f, ctx); diff --git a/src/context.hpp b/src/context.hpp index 6bd46f700f..d81d543541 100644 --- a/src/context.hpp +++ b/src/context.hpp @@ -11,13 +11,15 @@ #include "ast_fwd_decl.hpp" #include "kwd_arg_macros.hpp" #include "memory_manager.hpp" +#include "ast_fwd_decl.hpp" +#include "sass_context.hpp" #include "environment.hpp" #include "source_map.hpp" #include "subset_map.hpp" #include "output.hpp" #include "plugins.hpp" #include "file.hpp" -#include "sass.h" + struct Sass_Function; @@ -25,30 +27,48 @@ namespace Sass { class Context { public: + void import_url (Import* imp, std::string load_path, const std::string& ctx_path); + bool call_headers(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp) + { return call_loader(load_path, ctx_path, pstate, imp, c_headers, false); }; + bool call_importers(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp) + { return call_loader(load_path, ctx_path, pstate, imp, c_importers, true); }; + + private: + bool call_loader(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp, std::vector importers, bool only_one = true); + + public: + const std::string CWD; + std::string entry_path; size_t head_imports; Memory_Manager mem; + Plugins plugins; + Output emitter; + + // resources add under our control + // these are guaranteed to be freed + std::vector strings; + std::vector resources; + std::map sheets; + Subset_Map > subset_map; + std::vector import_stack; - struct Sass_Options* c_options; struct Sass_Compiler* c_compiler; - char* source_c_str; + struct Sass_Options* c_options; - // c-strs containing Sass file contents - // we will overtake ownership of memory - std::vector sources; - // strings get freed with context - std::vector strings; // absolute paths to includes std::vector included_files; - // relative links to includes + // relative includes for sourcemap std::vector srcmap_links; // vectors above have same size std::vector plugin_paths; // relative paths to load plugins std::vector include_paths; // lookup paths for includes - std::vector queue; // queue of files to be parsed - std::map style_sheets; // map of paths to ASTs - // SourceMap source_map; - Output emitter; + + + + + + void apply_custom_headers(Block* root, const char* path, ParserState pstate); std::vector c_headers; std::vector c_importers; @@ -58,71 +78,25 @@ namespace Sass { void add_c_importer(Sass_Importer_Entry importer); void add_c_function(Sass_Function_Entry function); - std::string indent; // String to be used for indentation - std::string linefeed; // String to be used for line feeds - std::string input_path; // for relative paths in src-map - std::string output_path; // for relative paths to the output - bool source_comments; // for inline debug comments in css output - Sass_Output_Style output_style; // output style for the generated css code - std::string source_map_file; // path to source map file (enables feature) - std::string source_map_root; // path for sourceRoot property (pass-through) - bool source_map_embed; // embed in sourceMappingUrl (as data-url) - bool source_map_contents; // insert included contents into source map - bool omit_source_map_url; // disable source map comment in css output - bool is_indented_syntax_src; // treat source string as sass - - // overload import calls - std::vector import_stack; - - size_t precision; // precision for outputting fractional numbers - - KWD_ARG_SET(Data) { - KWD_ARG(Data, struct Sass_Options*, c_options) - KWD_ARG(Data, struct Sass_Compiler*, c_compiler) - KWD_ARG(Data, char*, source_c_str) - KWD_ARG(Data, std::string, entry_point) - KWD_ARG(Data, std::string, input_path) - KWD_ARG(Data, std::string, output_path) - KWD_ARG(Data, std::string, indent) - KWD_ARG(Data, std::string, linefeed) - KWD_ARG(Data, const char*, include_paths_c_str) - KWD_ARG(Data, const char*, plugin_paths_c_str) - // KWD_ARG(Data, const char**, include_paths_array) - // KWD_ARG(Data, const char**, plugin_paths_array) - KWD_ARG(Data, std::vector, include_paths) - KWD_ARG(Data, std::vector, plugin_paths) - KWD_ARG(Data, bool, source_comments) - KWD_ARG(Data, Sass_Output_Style, output_style) - KWD_ARG(Data, std::string, source_map_file) - KWD_ARG(Data, std::string, source_map_root) - KWD_ARG(Data, bool, omit_source_map_url) - KWD_ARG(Data, bool, is_indented_syntax_src) - KWD_ARG(Data, size_t, precision) - KWD_ARG(Data, bool, source_map_embed) - KWD_ARG(Data, bool, source_map_contents) - }; - - Context(Data); - ~Context(); - static std::string get_cwd(); - - Block* parse_file(); - Block* parse_string(); - void add_source(std::string, std::string, char*); - - std::string add_file(const std::string& imp_path, bool delay = false); - std::string add_file(const std::string& imp_path, const std::string& abs_path, ParserState pstate); - - void process_queue_entry(Sass_Queued& entry, size_t idx); - - // allow to optionally overwrite the input path - // default argument for input_path is std::string("stdin") - // usefull to influence the source-map generating etc. - char* compile_file(); - char* compile_string(); - char* render(Block* root); - char* render_srcmap(); - + const std::string indent; // String to be used for indentation + const std::string linefeed; // String to be used for line feeds + const std::string input_path; // for relative paths in src-map + const std::string output_path; // for relative paths to the output + const std::string source_map_file; // path to source map file (enables feature) + const std::string source_map_root; // path for sourceRoot property (pass-through) + + virtual ~Context(); + Context(struct Sass_Context*); + virtual Block* parse() = 0; + virtual Block* compile(); + virtual char* render(Block* root); + virtual char* render_srcmap(); + + void register_resource(const Include&, const Resource&); + std::vector find_includes(const Importer& import); + Include load_import(const Importer&, ParserState pstate); + + Sass_Output_Style output_style() { return c_options->output_style; }; std::vector get_included_files(bool skip = false, size_t headers = 0); private: @@ -133,8 +107,6 @@ namespace Sass { std::string format_embedded_source_map(); std::string format_source_mapping_url(const std::string& out_path); - std::string cwd; - Plugins plugins; // void register_built_in_functions(Env* env); // void register_function(Signature sig, Native_Function f, Env* env); @@ -142,7 +114,32 @@ namespace Sass { // void register_overload_stub(std::string name, Env* env); public: - Subset_Map > subset_map; + const std::string& cwd() { return CWD; }; + }; + + class File_Context : public Context { + public: + File_Context(struct Sass_File_Context* ctx) + : Context(ctx) + { } + virtual ~File_Context(); + virtual Block* parse(); + }; + + class Data_Context : public Context { + public: + char* source_c_str; + char* srcmap_c_str; + Data_Context(struct Sass_Data_Context* ctx) + : Context(ctx) + { + source_c_str = ctx->source_string; + srcmap_c_str = ctx->srcmap_string; + ctx->source_string = 0; // passed away + ctx->srcmap_string = 0; // passed away + } + virtual ~Data_Context(); + virtual Block* parse(); }; } diff --git a/src/emitter.cpp b/src/emitter.cpp index ae8dacc0c7..e18342fe4e 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -29,7 +29,7 @@ namespace Sass { Sass_Output_Style Emitter::output_style(void) { - return ctx ? ctx->output_style : SASS_STYLE_COMPRESSED; + return ctx ? ctx->output_style() : SASS_STYLE_COMPRESSED; } // PROXY METHODS FOR SOURCE MAPS diff --git a/src/eval.cpp b/src/eval.cpp index d26e5dd50a..5a3fc2c8ff 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -400,7 +400,7 @@ namespace Sass { } - std::string cwd(ctx.get_cwd()); + std::string cwd(ctx.cwd()); std::string result(unquote(message->perform(&to_string))); std::string rel_path(Sass::File::abs2rel(d->pstate().path, cwd, cwd)); std::cerr << rel_path << ":" << d->pstate().line+1 << " DEBUG: " << result; @@ -530,8 +530,8 @@ namespace Sass { // ToDo: throw error in op functions // ToDo: then catch and re-throw them ParserState pstate(b->pstate()); - int precision = (int)ctx.precision; - bool compressed = ctx.output_style == SASS_STYLE_COMPRESSED; + int precision = (int)ctx.c_options->precision; + bool compressed = ctx.output_style() == SASS_STYLE_COMPRESSED; if (l_type == Expression::NUMBER && r_type == Expression::NUMBER) { const Number* l_n = dynamic_cast(lhs); const Number* r_n = dynamic_cast(rhs); @@ -904,7 +904,7 @@ namespace Sass { } else if (List* list = dynamic_cast(s)) { std::string acc = ""; // ToDo: different output styles std::string sep = list->separator() == SASS_COMMA ? "," : " "; - if (ctx.output_style != SASS_STYLE_COMPRESSED && sep == ",") sep += " "; + if (ctx.output_style() != SASS_STYLE_COMPRESSED && sep == ",") sep += " "; bool initial = false; for(auto item : list->elements()) { if (item->concrete_type() != Expression::NULL_VAL) { diff --git a/src/expand.cpp b/src/expand.cpp index 7673257247..92dabfc357 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -28,6 +28,7 @@ namespace Sass { env_stack.push_back(0); env_stack.push_back(env); block_stack.push_back(0); + // import_stack.push_back(0); property_stack.push_back(0); selector_stack.push_back(0); backtrace_stack.push_back(0); @@ -303,19 +304,31 @@ namespace Sass { Statement* Expand::operator()(Import* imp) { Import* result = SASS_MEMORY_NEW(ctx.mem, Import, imp->pstate()); - if (imp->media_queries()) { + if (imp->media_queries() && imp->media_queries()->size()) { Expression* ex = imp->media_queries()->perform(&eval); result->media_queries(dynamic_cast(ex)); } for ( size_t i = 0, S = imp->urls().size(); i < S; ++i) { result->urls().push_back(imp->urls()[i]->perform(&eval)); } + // all resources have been dropped for Input_Stubs + // for ( size_t i = 0, S = imp->incs().size(); i < S; ++i) {} return result; } Statement* Expand::operator()(Import_Stub* i) { - append_block(ctx.style_sheets[i->file_name()]); + // we don't seem to need that actually afterall + Sass_Import_Entry import = sass_make_import( + i->imp_path().c_str(), + i->abs_path().c_str(), + 0, 0 + ); + ctx.import_stack.push_back(import); + const std::string& abs_path(i->resource().abs_path); + append_block(ctx.sheets.at(abs_path).root); + sass_delete_import(ctx.import_stack.back()); + ctx.import_stack.pop_back(); return 0; } diff --git a/src/file.cpp b/src/file.cpp index d8b74e6e94..b115010656 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -126,7 +126,7 @@ namespace Sass { else return path.substr(pos+1); } - // do a locigal clean up of the path + // do a logical clean up of the path // no physical check on the filesystem std::string make_canonical_path (std::string path) { @@ -200,51 +200,51 @@ namespace Sass { // create a path that is relative to the given base directory // path and base will first be resolved against cwd to make them absolute - std::string abs2rel(const std::string& uri, const std::string& base, const std::string& cwd) + std::string abs2rel(const std::string& path, const std::string& base, const std::string& cwd) { - std::string absolute_uri = rel2abs(uri, cwd); - std::string absolute_base = rel2abs(base, cwd); + std::string abs_path = rel2abs(path, cwd); + std::string abs_base = rel2abs(base, cwd); size_t proto = 0; // check if we have a protocol - if (uri[proto] && Prelexer::is_alpha(uri[proto])) { + if (path[proto] && Prelexer::is_alpha(path[proto])) { // skip over all alphanumeric characters - while (uri[proto] && Prelexer::is_alnum(uri[proto++])) {} + while (path[proto] && Prelexer::is_alnum(path[proto++])) {} // then skip over the mandatory colon - if (proto && uri[proto] == ':') ++ proto; + if (proto && path[proto] == ':') ++ proto; } // distinguish between windows absolute paths and valid protocols // we assume that protocols must at least have two chars to be valid - if (proto && uri[proto++] == '/' && proto > 3) return uri; + if (proto && path[proto++] == '/' && proto > 3) return path; #ifdef _WIN32 // absolute link must have a drive letter, and we know that we // can only create relative links if both are on the same drive - if (absolute_base[0] != absolute_uri[0]) return absolute_uri; + if (abs_base[0] != abs_path[0]) return abs_path; #endif std::string stripped_uri = ""; std::string stripped_base = ""; size_t index = 0; - size_t minSize = std::min(absolute_uri.size(), absolute_base.size()); + size_t minSize = std::min(abs_path.size(), abs_base.size()); for (size_t i = 0; i < minSize; ++i) { #ifdef FS_CASE_SENSITIVE - if (absolute_uri[i] != absolute_base[i]) break; + if (abs_path[i] != abs_base[i]) break; #else // compare the charactes in a case insensitive manner // windows fs is only case insensitive in ascii ranges - if (tolower(absolute_uri[i]) != tolower(absolute_base[i])) break; + if (tolower(abs_path[i]) != tolower(abs_base[i])) break; #endif - if (absolute_uri[i] == '/') index = i + 1; + if (abs_path[i] == '/') index = i + 1; } - for (size_t i = index; i < absolute_uri.size(); ++i) { - stripped_uri += absolute_uri[i]; + for (size_t i = index; i < abs_path.size(); ++i) { + stripped_uri += abs_path[i]; } - for (size_t i = index; i < absolute_base.size(); ++i) { - stripped_base += absolute_base[i]; + for (size_t i = index; i < abs_base.size(); ++i) { + stripped_base += abs_base[i]; } size_t left = 0; @@ -278,7 +278,7 @@ namespace Sass { // (2) underscore + given // (3) underscore + given + extension // (4) given + extension - std::vector resolve_includes(const std::string& root, const std::string& file) + std::vector resolve_includes(const std::string& root, const std::string& file) { std::string filename = join_paths(root, file); // supported extensions @@ -288,26 +288,26 @@ namespace Sass { // split the filename std::string base(dir_name(file)); std::string name(base_name(file)); - std::vector includes; + std::vector includes; // create full path (maybe relative) std::string rel_path(join_paths(base, name)); std::string abs_path(join_paths(root, rel_path)); - if (file_exists(abs_path)) includes.push_back(Sass_Queued(rel_path, abs_path, 0)); + if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path }); // next test variation with underscore rel_path = join_paths(base, "_" + name); abs_path = join_paths(root, rel_path); - if (file_exists(abs_path)) includes.push_back(Sass_Queued(rel_path, abs_path, 0)); + if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path }); // next test exts plus underscore for(auto ext : exts) { rel_path = join_paths(base, "_" + name + ext); abs_path = join_paths(root, rel_path); - if (file_exists(abs_path)) includes.push_back(Sass_Queued(rel_path, abs_path, 0)); + if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path }); } // next test plain name with exts for(auto ext : exts) { rel_path = join_paths(base, name + ext); abs_path = join_paths(root, rel_path); - if (file_exists(abs_path)) includes.push_back(Sass_Queued(rel_path, abs_path, 0)); + if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path }); } // nothing found return includes; @@ -319,7 +319,7 @@ namespace Sass { // search in every include path for a match for (size_t i = 0, S = paths.size(); i < S; ++i) { - std::vector resolved(resolve_includes(paths[i], file)); + std::vector resolved(resolve_includes(paths[i], file)); if (resolved.size()) return resolved[0].abs_path; } // nothing found diff --git a/src/file.hpp b/src/file.hpp index c0e48693ba..a098281bc8 100644 --- a/src/file.hpp +++ b/src/file.hpp @@ -6,16 +6,9 @@ namespace Sass { + class Block; class Context; - struct Sass_Queued { - std::string abs_path; - std::string load_path; - const char* source; - public: - Sass_Queued(const std::string& load_path, const std::string& abs_path, const char* source); - }; - namespace File { // return the current directory @@ -50,9 +43,6 @@ namespace Sass { // path and base will first be resolved against cwd to make them absolute std::string abs2rel(const std::string& path, const std::string& base = ".", const std::string& cwd = get_cwd()); - // try to find/resolve the filename - std::vector resolve_includes(const std::string& root, const std::string& file); - // helper function to resolve a filename std::string find_file(const std::string& file, const std::vector paths); // inc paths can be directly passed from C code @@ -64,6 +54,66 @@ namespace Sass { char* read_file(const std::string& file); } + + // requested import + class Importer { + public: + // requested import path + std::string imp_path; + // parent context path + std::string ctx_path; + // base derived from context path + // this really just acts as a cache + std::string base_path; + public: + Importer(std::string imp_path, std::string ctx_path) + : imp_path(File::make_canonical_path(imp_path)), + ctx_path(File::make_canonical_path(ctx_path)), + base_path(File::dir_name(ctx_path)) + { } + }; + + // a resolved include (final import) + class Include : public Importer { + public: + // resolved absolute path + std::string abs_path; + public: + Include(const Importer& imp, std::string abs_path) + : Importer(imp), abs_path(abs_path) + { } + }; + + // a loaded resource + class Resource { + public: + // the file contents + char* contents; + // conected sourcemap + char* srcmap; + public: + Resource(char* contents, char* srcmap) + : contents(contents), srcmap(srcmap) + { } + }; + + // parsed stylesheet from loaded resource + class StyleSheet : public Resource { + public: + // parsed root block + Block* root; + public: + StyleSheet(const Resource& res, Block* root) + : Resource(res), root(root) + { } + }; + + namespace File { + + std::vector resolve_includes(const std::string& root, const std::string& file); + + } + } #endif diff --git a/src/functions.cpp b/src/functions.cpp index a58704ebda..8eba3fc3b5 100644 --- a/src/functions.cpp +++ b/src/functions.cpp @@ -13,7 +13,6 @@ #include "utf8_string.hpp" #include "utf8.h" -#include #include #include #include @@ -1669,12 +1668,12 @@ namespace Sass { bool parentheses = v->concrete_type() == Expression::MAP || v->concrete_type() == Expression::LIST; Sass_Output_Style old_style; - old_style = ctx.output_style; - ctx.output_style = SASS_STYLE_NESTED; + old_style = ctx.c_options->output_style; + ctx.c_options->output_style = SASS_STYLE_NESTED; To_String to_string(&ctx, false); std::string inspect = v->perform(&to_string); if (inspect.empty() && parentheses) inspect = "()"; - ctx.output_style = old_style; + ctx.c_options->output_style = old_style; return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, inspect); } // return v; diff --git a/src/inspect.cpp b/src/inspect.cpp index ac7f73220a..60d01edbd9 100644 --- a/src/inspect.cpp +++ b/src/inspect.cpp @@ -198,7 +198,7 @@ namespace Sass { append_indentation(); append_token("@import", import); append_mandatory_space(); - append_string(import->file_name()); + append_string(import->imp_path()); append_delimiter(); } @@ -459,8 +459,8 @@ namespace Sass { void Inspect::operator()(Number* n) { // use values to_string facility - bool compressed = ctx->output_style == SASS_STYLE_COMPRESSED; - std::string res(n->to_string(compressed, (int)ctx->precision)); + bool compressed = ctx->output_style() == SASS_STYLE_COMPRESSED; + std::string res(n->to_string(compressed, (int)ctx->c_options->precision)); // output the final token append_token(res, n); } @@ -468,8 +468,8 @@ namespace Sass { void Inspect::operator()(Color* c) { // use values to_string facility - bool compressed = ctx->output_style == SASS_STYLE_COMPRESSED; - std::string res(c->to_string(compressed, (int)ctx->precision)); + bool compressed = ctx->output_style() == SASS_STYLE_COMPRESSED; + std::string res(c->to_string(compressed, (int)ctx->c_options->precision)); // output the final token append_token(res, c); } @@ -477,8 +477,8 @@ namespace Sass { void Inspect::operator()(Boolean* b) { // use values to_string facility - bool compressed = ctx->output_style == SASS_STYLE_COMPRESSED; - std::string res(b->to_string(compressed, (int)ctx->precision)); + bool compressed = ctx->output_style() == SASS_STYLE_COMPRESSED; + std::string res(b->to_string(compressed, (int)ctx->c_options->precision)); // output the final token append_token(res, b); } @@ -497,8 +497,8 @@ namespace Sass { void Inspect::operator()(String_Constant* s) { // get options from optional? context - int precision = ctx ? (int)ctx->precision : 5; - bool compressed = ctx ? ctx->output_style == SASS_STYLE_COMPRESSED : false; + int precision = ctx ? (int)ctx->c_options->precision : 5; + bool compressed = ctx ? ctx->output_style() == SASS_STYLE_COMPRESSED : false; // use values to_string facility std::string res(s->to_string(compressed, precision)); // output the final token @@ -508,8 +508,8 @@ namespace Sass { void Inspect::operator()(String_Quoted* s) { // get options from optional? context - int precision = ctx ? (int)ctx->precision : 5; - bool compressed = ctx ? ctx->output_style == SASS_STYLE_COMPRESSED : false; + int precision = ctx ? (int)ctx->c_options->precision : 5; + bool compressed = ctx ? ctx->output_style() == SASS_STYLE_COMPRESSED : false; // use values to_string facility std::string res(s->to_string(compressed, precision)); // output the final token @@ -612,8 +612,8 @@ namespace Sass { void Inspect::operator()(Null* n) { // use values to_string facility - bool compressed = ctx->output_style == SASS_STYLE_COMPRESSED; - std::string res(n->to_string(compressed, (int)ctx->precision)); + bool compressed = output_style() == SASS_STYLE_COMPRESSED; + std::string res(n->to_string(compressed, (int)ctx->c_options->precision)); // output the final token append_token(res, n); } diff --git a/src/json.cpp b/src/json.cpp index dcb3e4beca..e0ef5968fa 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -802,7 +802,7 @@ static bool parse_object(const char **sp, JsonNode **out) bool parse_string(const char **sp, char **out) { const char *s = *sp; - SB sb = { 0, 0, 0}; + SB sb = { 0, 0, 0 }; char throwaway_buffer[4]; /* enough space for a UTF-8 character */ char *b; diff --git a/src/output.cpp b/src/output.cpp index 4f904732fd..d9d362fb62 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -132,10 +132,11 @@ namespace Sass { if (b->has_non_hoistable()) { decls = true; if (output_style() == SASS_STYLE_NESTED) indentation += r->tabs(); - if (ctx && ctx->source_comments) { + if (ctx && ctx->c_options->source_comments) { std::stringstream ss; append_indentation(); - ss << "/* line " << r->pstate().line+1 << ", " << r->pstate().path << " */"; + std::string path = Sass::File::abs2rel(r->pstate().path, ctx->cwd()); + ss << "/* line " << r->pstate().line + 1 << ", " << path << " */"; append_string(ss.str()); append_optional_linefeed(); } diff --git a/src/parser.cpp b/src/parser.cpp index 0ce4cefe50..9d1120d0e8 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -76,18 +76,10 @@ namespace Sass { Block* root = SASS_MEMORY_NEW(ctx.mem, Block, pstate, 0, true); read_bom(); - if (ctx.queue.size() == 1) { - is_root = true; - Import* pre = SASS_MEMORY_NEW(ctx.mem, Import, pstate); - std::string load_path(ctx.queue[0].load_path); - do_import(load_path, pre, ctx.c_headers, false); - ctx.head_imports = ctx.queue.size() - 1; - if (!pre->urls().empty()) (*root) << pre; - if (!pre->files().empty()) { - for (size_t i = 0, S = pre->files().size(); i < S; ++i) { - (*root) << SASS_MEMORY_NEW(ctx.mem, Import_Stub, pstate, pre->files()[i]); - } - } + // custom headers + if (ctx.resources.size() == 1) { + is_root = true; + ctx.apply_custom_headers(root, path, pstate); } block_stack.push_back(root); @@ -223,11 +215,9 @@ namespace Sass { Import* imp = parse_import(); // if it is a url, we only add the statement if (!imp->urls().empty()) (*block) << imp; - // if it is a file(s), we should process them - if (!imp->files().empty()) { - for (size_t i = 0, S = imp->files().size(); i < S; ++i) { - (*block) << SASS_MEMORY_NEW(ctx.mem, Import_Stub, pstate, imp->files()[i]); - } + // process all resources now (add Import_Stub nodes) + for (size_t i = 0, S = imp->incs().size(); i < S; ++i) { + (*block) << SASS_MEMORY_NEW(ctx.mem, Import_Stub, pstate, imp->incs()[i]); } } @@ -289,105 +279,7 @@ namespace Sass { } // EO parse_block_nodes - void Parser::add_single_file (Import* imp, std::string import_path) { - - std::string extension; - std::string unquoted(unquote(import_path)); - if (unquoted.length() > 4) { // 2 quote marks + the 4 chars in .css - // a string constant is guaranteed to end with a quote mark, so make sure to skip it when indexing from the end - extension = unquoted.substr(unquoted.length() - 4, 4); - } - - if (extension == ".css") { - String_Constant* loc = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, unquote(import_path)); - Argument* loc_arg = SASS_MEMORY_NEW(ctx.mem, Argument, pstate, loc); - Arguments* loc_args = SASS_MEMORY_NEW(ctx.mem, Arguments, pstate); - (*loc_args) << loc_arg; - Function_Call* new_url = SASS_MEMORY_NEW(ctx.mem, Function_Call, pstate, "url", loc_args); - imp->urls().push_back(new_url); - } - else { - std::string current_dir = File::dir_name(path); - std::string resolved(ctx.add_file(current_dir, unquoted, *this)); - if (resolved.empty()) error("file to import not found or unreadable: " + unquoted + "\nCurrent dir: " + current_dir, pstate); - imp->files().push_back(resolved); - } - - } - - void Parser::import_single_file (Import* imp, std::string import_path) { - - if (imp->media_queries() || - !unquote(import_path).substr(0, 7).compare("http://") || - !unquote(import_path).substr(0, 8).compare("https://") || - !unquote(import_path).substr(0, 2).compare("//")) - { - imp->urls().push_back(SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, import_path)); - } - else { - add_single_file(imp, import_path); - } - - } - - bool Parser::do_import(const std::string& import_path, Import* imp, std::vector importers, bool only_one) - { - size_t i = 0; - bool has_import = false; - std::string load_path = unquote(import_path); - // std::cerr << "-- " << load_path << "\n"; - for (Sass_Importer_Entry& importer : importers) { - // int priority = sass_importer_get_priority(importer); - Sass_Importer_Fn fn = sass_importer_get_function(importer); - if (Sass_Import_List includes = - fn(load_path.c_str(), importer, ctx.c_compiler) - ) { - Sass_Import_List list = includes; - while (*includes) { ++i; - std::string uniq_path = load_path; - if (!only_one && i) { - std::stringstream pathstrm; - pathstrm << uniq_path << ":" << i; - uniq_path = pathstrm.str(); - } - Sass_Import_Entry include = *includes; - const char *abs_path = sass_import_get_abs_path(include); - char* source = sass_import_take_source(include); - size_t line = sass_import_get_error_line(include); - size_t column = sass_import_get_error_column(include); - const char* message = sass_import_get_error_message(include); - if (message) { - if (line == std::string::npos && column == std::string::npos) error(message, pstate); - else error(message, ParserState(message, source, Position(line, column))); - } else if (source) { - if (abs_path) { - ctx.add_source(uniq_path, abs_path, source); - imp->files().push_back(uniq_path); - size_t i = ctx.queue.size() - 1; - ctx.process_queue_entry(ctx.queue[i], i); - } else { - ctx.add_source(uniq_path, uniq_path, source); - imp->files().push_back(uniq_path); - size_t i = ctx.queue.size() - 1; - ctx.process_queue_entry(ctx.queue[i], i); - } - } else if(abs_path) { - import_single_file(imp, abs_path); - } - ++includes; - } - // deallocate returned memory - sass_delete_import_list(list); - // set success flag - has_import = true; - // break import chain - if (only_one) return true; - } - } - // return result - return has_import; - } - + // parse imports inside the Import* Parser::parse_import() { Import* imp = SASS_MEMORY_NEW(ctx.mem, Import, pstate); @@ -396,11 +288,11 @@ namespace Sass { do { while (lex< block_comment >()); if (lex< quoted_string >()) { - if (!do_import(lexed, imp, ctx.c_importers, true)) + if (!ctx.call_importers(unquote(std::string(lexed)), path, pstate, imp)) { // push single file import // import_single_file(imp, lexed); - to_import.push_back(std::pair(std::string(lexed), (Function_Call*) 0)); + to_import.push_back(std::pair(std::string(lexed), 0)); } } else if (lex< uri_prefix >()) { @@ -422,8 +314,7 @@ namespace Sass { error("malformed URL", pstate); } if (!lex< exactly<')'> >()) error("URI is missing ')'", pstate); - // imp->urls().push_back(result); - to_import.push_back(std::pair("", result)); + to_import.push_back(std::pair("", result)); } else { if (first) error("@import directive requires a url or quoted path", pstate); @@ -441,7 +332,7 @@ namespace Sass { if (location.second) { imp->urls().push_back(location.second); } else { - import_single_file(imp, location.first); + ctx.import_url(imp, location.first, path); } } diff --git a/src/parser.hpp b/src/parser.hpp index 2e6256e2e6..199934f7dc 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -21,13 +21,9 @@ struct Lookahead { namespace Sass { class Parser : public ParserState { - private: - void add_single_file (Import* imp, std::string import_path); - void import_single_file (Import* imp, std::string import_path); public: enum Syntactic_Context { nothing, mixin_def, function_def }; - bool do_import(const std::string& import_path, Import* imp, std::vector importers, bool only_one = true); Context& ctx; std::vector block_stack; diff --git a/src/sass_context.cpp b/src/sass_context.cpp index 6d054a27a2..8c88a59155 100644 --- a/src/sass_context.cpp +++ b/src/sass_context.cpp @@ -23,10 +23,10 @@ extern "C" { #define IMPLEMENT_SASS_OPTION_ACCESSOR(type, option) \ type ADDCALL sass_option_get_##option (struct Sass_Options* options) { return options->option; } \ void ADDCALL sass_option_set_##option (struct Sass_Options* options, type option) { options->option = option; } - #define IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(type, option) \ - type ADDCALL sass_option_get_##option (struct Sass_Options* options) { return options->option; } \ + #define IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(type, option, def) \ + type ADDCALL sass_option_get_##option (struct Sass_Options* options) { return safe_str(options->option, def); } \ void ADDCALL sass_option_set_##option (struct Sass_Options* options, type option) \ - { free(options->option); options->option = option ? sass_strdup(option) : 0; } + { free(options->option); options->option = option || def ? sass_strdup(option ? option : def) : 0; } #define IMPLEMENT_SASS_CONTEXT_GETTER(type, option) \ type ADDCALL sass_context_get_##option (struct Sass_Context* ctx) { return ctx->option; } @@ -177,19 +177,10 @@ extern "C" { } // generic compilation function (not exported, use file/data compile instead) - static Sass_Compiler* sass_prepare_context (Sass_Context* c_ctx, Context::Data cpp_opt) throw() + static Sass_Compiler* sass_prepare_context (Sass_Context* c_ctx, Context* cpp_ctx) throw() { try { - // get input/output path from options - std::string input_path = safe_str(c_ctx->input_path); - std::string output_path = safe_str(c_ctx->output_path); - // maybe we can extract an output path from input path - if (output_path == "" && input_path != "") { - int lastindex = static_cast(input_path.find_last_of(".")); - output_path = (lastindex > -1 ? input_path.substr(0, lastindex) : input_path) + ".css"; - } - // convert include path linked list to static array struct string_list* inc = c_ctx->include_paths; // very poor loop to get the length of the linked list @@ -220,31 +211,6 @@ extern "C" { imp = imp->next; } - // transfer the options to c++ - cpp_opt.c_compiler(0) - .c_options(c_ctx) - .input_path(input_path) - .output_path(output_path) - .output_style((Sass_Output_Style) c_ctx->output_style) - .is_indented_syntax_src(c_ctx->is_indented_syntax_src) - .source_comments(c_ctx->source_comments) - .source_map_file(safe_str(c_ctx->source_map_file)) - .source_map_root(safe_str(c_ctx->source_map_root)) - .source_map_embed(c_ctx->source_map_embed) - .source_map_contents(c_ctx->source_map_contents) - .omit_source_map_url(c_ctx->omit_source_map_url) - .include_paths_c_str(c_ctx->include_path) - .plugin_paths_c_str(c_ctx->plugin_path) - // .include_paths_array(include_paths) - // .plugin_paths_array(plugin_paths) - .include_paths(std::vector()) - .plugin_paths(std::vector()) - .precision(c_ctx->precision) - .linefeed(c_ctx->linefeed) - .indent(c_ctx->indent); - - // create new c++ Context - Context* cpp_ctx = new Context(cpp_opt); // free intermediate data free(include_paths); free(plugin_paths); @@ -331,15 +297,12 @@ extern "C" { // maybe skip some entries of included files // we do not include stdin for data contexts - bool skip = false; - - // dispatch to the correct render function - if (c_ctx->type == SASS_CONTEXT_FILE) { - root = cpp_ctx->parse_file(); - } else if (c_ctx->type == SASS_CONTEXT_DATA) { - root = cpp_ctx->parse_string(); - skip = true; // skip first entry of includes - } + bool skip = c_ctx->type == SASS_CONTEXT_DATA; + + // dispatch parse call + root = cpp_ctx->parse(); + // abort on errors + if (!root) return 0; // skip all prefixed files? (ToDo: check srcmap) // IMO source-maps should point to headers already @@ -347,10 +310,9 @@ extern "C" { // remove completely once this is tested size_t headers = cpp_ctx->head_imports; - // copy the included files on to the context (dont forget to free) - if (root) - if (copy_strings(cpp_ctx->get_included_files(skip, headers), &c_ctx->included_files) == NULL) - throw(std::bad_alloc()); + // copy the included files on to the context (dont forget to free later) + if (copy_strings(cpp_ctx->get_included_files(skip, headers), &c_ctx->included_files) == NULL) + throw(std::bad_alloc()); // return parsed block return root; @@ -365,11 +327,11 @@ extern "C" { } // generic compilation function (not exported, use file/data compile instead) - static int sass_compile_context (Sass_Context* c_ctx, Context::Data cpp_opt) + static int sass_compile_context (Sass_Context* c_ctx, Context* cpp_ctx) { // prepare sass compiler with context and options - Sass_Compiler* compiler = sass_prepare_context(c_ctx, cpp_opt); + Sass_Compiler* compiler = sass_prepare_context(c_ctx, cpp_ctx); try { // call each compiler step @@ -431,54 +393,46 @@ extern "C" { return ctx; } - struct Sass_Compiler* ADDCALL sass_make_file_compiler (struct Sass_File_Context* c_ctx) + struct Sass_Compiler* ADDCALL sass_make_data_compiler (struct Sass_Data_Context* data_ctx) { - if (c_ctx == 0) return 0; - Context::Data cpp_opt = Context::Data(); - cpp_opt.entry_point(c_ctx->input_path); - return sass_prepare_context(c_ctx, cpp_opt); + if (data_ctx == 0) return 0; + Context* cpp_ctx = new Data_Context(data_ctx); + return sass_prepare_context(data_ctx, cpp_ctx); } - struct Sass_Compiler* ADDCALL sass_make_data_compiler (struct Sass_Data_Context* c_ctx) + struct Sass_Compiler* ADDCALL sass_make_file_compiler (struct Sass_File_Context* file_ctx) { - if (c_ctx == 0) return 0; - Context::Data cpp_opt = Context::Data(); - cpp_opt.source_c_str(c_ctx->source_string); - c_ctx->source_string = 0; // passed away - return sass_prepare_context(c_ctx, cpp_opt); + if (file_ctx == 0) return 0; + Context* cpp_ctx = new File_Context(file_ctx); + return sass_prepare_context(file_ctx, cpp_ctx); } int ADDCALL sass_compile_data_context(Sass_Data_Context* data_ctx) { if (data_ctx == 0) return 1; - Sass_Context* c_ctx = data_ctx; - if (c_ctx->error_status) - return c_ctx->error_status; - Context::Data cpp_opt = Context::Data(); + if (data_ctx->error_status) + return data_ctx->error_status; try { if (data_ctx->source_string == 0) { throw(std::runtime_error("Data context has no source string")); } if (*data_ctx->source_string == 0) { throw(std::runtime_error("Data context has empty source string")); } - cpp_opt.source_c_str(data_ctx->source_string); - data_ctx->source_string = 0; // passed away } - catch (...) { return handle_errors(c_ctx) | 1; } - return sass_compile_context(c_ctx, cpp_opt); + catch (...) { return handle_errors(data_ctx) | 1; } + Context* cpp_ctx = new Data_Context(data_ctx); + return sass_compile_context(data_ctx, cpp_ctx); } int ADDCALL sass_compile_file_context(Sass_File_Context* file_ctx) { if (file_ctx == 0) return 1; - Sass_Context* c_ctx = file_ctx; - if (c_ctx->error_status) - return c_ctx->error_status; - Context::Data cpp_opt = Context::Data(); + if (file_ctx->error_status) + return file_ctx->error_status; try { if (file_ctx->input_path == 0) { throw(std::runtime_error("File context has no input path")); } if (*file_ctx->input_path == 0) { throw(std::runtime_error("File context has empty input path")); } - cpp_opt.entry_point(file_ctx->input_path); } - catch (...) { return handle_errors(c_ctx) | 1; } - return sass_compile_context(c_ctx, cpp_opt); + catch (...) { return handle_errors(file_ctx) | 1; } + Context* cpp_ctx = new File_Context(file_ctx); + return sass_compile_context(file_ctx, cpp_ctx); } int ADDCALL sass_compiler_parse(struct Sass_Compiler* compiler) @@ -684,12 +638,12 @@ extern "C" { IMPLEMENT_SASS_OPTION_ACCESSOR(Sass_Importer_List, c_headers); IMPLEMENT_SASS_OPTION_ACCESSOR(const char*, indent); IMPLEMENT_SASS_OPTION_ACCESSOR(const char*, linefeed); - IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, input_path); - IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, output_path); - IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, plugin_path); - IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, include_path); - IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, source_map_file); - IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, source_map_root); + IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, input_path, 0); + IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, output_path, 0); + IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, plugin_path, 0); + IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, include_path, 0); + IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, source_map_file, 0); + IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, source_map_root, 0); // Create getter and setters for context IMPLEMENT_SASS_CONTEXT_GETTER(int, error_status); diff --git a/src/sass_context.hpp b/src/sass_context.hpp index c09e6d8c4a..a09086639e 100644 --- a/src/sass_context.hpp +++ b/src/sass_context.hpp @@ -2,6 +2,8 @@ #define SASS_SASS_CONTEXT_H #include "sass.h" +#include "context.hpp" +#include "ast_fwd_decl.hpp" // Input behaviours enum Sass_Input_Style { diff --git a/src/sass_interface.cpp b/src/sass_interface.cpp index e67d74dfde..9284386087 100644 --- a/src/sass_interface.cpp +++ b/src/sass_interface.cpp @@ -69,26 +69,8 @@ extern "C" { else { output_path = c_ctx->output_path; } - Context cpp_ctx( - Context::Data().source_c_str(c_ctx->source_string) - .output_path(output_path) - .output_style((Sass_Output_Style) c_ctx->options.output_style) - .is_indented_syntax_src(c_ctx->options.is_indented_syntax_src) - .source_comments(c_ctx->options.source_comments) - .source_map_file(safe_str(c_ctx->options.source_map_file)) - .source_map_root(safe_str(c_ctx->options.source_map_root)) - .source_map_embed(c_ctx->options.source_map_embed) - .source_map_contents(c_ctx->options.source_map_contents) - .omit_source_map_url(c_ctx->options.omit_source_map_url) - .include_paths_c_str(c_ctx->options.include_paths) - .plugin_paths_c_str(c_ctx->options.plugin_paths) - // .include_paths_array(0) - // .plugin_paths_array(0) - .include_paths(std::vector()) - .plugin_paths(std::vector()) - .precision(c_ctx->options.precision ? c_ctx->options.precision : 5) - .indent(c_ctx->options.indent ? c_ctx->options.indent : " ") - .linefeed(c_ctx->options.linefeed ? c_ctx->options.linefeed : LFEED) + Data_Context cpp_ctx( + (Sass_Data_Context*) 0 ); if (c_ctx->c_functions) { Sass_Function_List this_func_data = c_ctx->c_functions; @@ -97,7 +79,8 @@ extern "C" { ++this_func_data; } } - c_ctx->output_string = cpp_ctx.compile_string(); + Block* root = cpp_ctx.parse(); + c_ctx->output_string = cpp_ctx.render(root); c_ctx->source_map_string = cpp_ctx.render_srcmap(); c_ctx->error_message = 0; c_ctx->error_status = 0; @@ -162,26 +145,8 @@ extern "C" { else { output_path = c_ctx->output_path; } - Context cpp_ctx( - Context::Data().entry_point(input_path) - .output_path(output_path) - .output_style((Sass_Output_Style) c_ctx->options.output_style) - .is_indented_syntax_src(c_ctx->options.is_indented_syntax_src) - .source_comments(c_ctx->options.source_comments) - .source_map_file(safe_str(c_ctx->options.source_map_file)) - .source_map_root(safe_str(c_ctx->options.source_map_root)) - .source_map_embed(c_ctx->options.source_map_embed) - .source_map_contents(c_ctx->options.source_map_contents) - .omit_source_map_url(c_ctx->options.omit_source_map_url) - .include_paths_c_str(c_ctx->options.include_paths) - .plugin_paths_c_str(c_ctx->options.plugin_paths) - // .include_paths_array(0) - // .plugin_paths_array(0) - .include_paths(std::vector()) - .plugin_paths(std::vector()) - .precision(c_ctx->options.precision ? c_ctx->options.precision : 5) - .indent(c_ctx->options.indent ? c_ctx->options.indent : " ") - .linefeed(c_ctx->options.linefeed ? c_ctx->options.linefeed : LFEED) + File_Context cpp_ctx( + (Sass_File_Context*) 0 ); if (c_ctx->c_functions) { Sass_Function_List this_func_data = c_ctx->c_functions; @@ -190,7 +155,8 @@ extern "C" { ++this_func_data; } } - c_ctx->output_string = cpp_ctx.compile_file(); + Block* root = cpp_ctx.parse(); + c_ctx->output_string = cpp_ctx.render(root); c_ctx->source_map_string = cpp_ctx.render_srcmap(); c_ctx->error_message = 0; c_ctx->error_status = 0; diff --git a/src/source_map.cpp b/src/source_map.cpp index 524196869f..20e81b7603 100644 --- a/src/source_map.cpp +++ b/src/source_map.cpp @@ -16,9 +16,9 @@ namespace Sass { std::string SourceMap::render_srcmap(Context &ctx) { - const bool include_sources = ctx.source_map_contents; + const bool include_sources = ctx.c_options->source_map_contents; const std::vector links = ctx.srcmap_links; - const std::vector sources = ctx.sources; + const std::vector& sources(ctx.resources); JsonNode* json_srcmap = json_mkobject(); @@ -45,8 +45,8 @@ namespace Sass { if (include_sources) { JsonNode *json_contents = json_mkarray(); for (size_t i = 0; i < source_index.size(); ++i) { - const char *content = sources[source_index[i]]; - JsonNode *json_content = json_mkstring(content); + const Resource& resource(sources[source_index[i]]); + JsonNode *json_content = json_mkstring(resource.contents); json_append_element(json_contents, json_content); } if (json_contents->children.head) diff --git a/src/util.cpp b/src/util.cpp index 86ca2e24ca..13d19c8195 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -49,8 +49,8 @@ namespace Sass { } // helper for safe access to c_ctx - const char* safe_str (const char* str) { - return str == NULL ? "" : str; + const char* safe_str (const char* str, const char* alt) { + return str == NULL ? alt : str; } void free_string_array(char ** arr) { diff --git a/src/util.hpp b/src/util.hpp index 2c91d45b32..78f1f53ab1 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -13,7 +13,7 @@ namespace Sass { char* sass_strdup(const char* str); double sass_atof(const char* str); - const char* safe_str(const char *); + const char* safe_str(const char *, const char* = ""); void free_string_array(char **); char **copy_strings(const std::vector&, char ***, int = 0); std::string string_escape(const std::string& str);