1 // sass.hpp must go before all system headers to get the 2 // __EXTENSIONS__ fix on Solaris. 3 #include "sass.hpp" 4 #include "ast.hpp" 5 6 #include "remove_placeholders.hpp" 7 #include "sass_functions.hpp" 8 #include "check_nesting.hpp" 9 #include "fn_selectors.hpp" 10 #include "fn_strings.hpp" 11 #include "fn_numbers.hpp" 12 #include "fn_colors.hpp" 13 #include "fn_miscs.hpp" 14 #include "fn_lists.hpp" 15 #include "fn_maps.hpp" 16 #include "context.hpp" 17 #include "expand.hpp" 18 #include "parser.hpp" 19 #include "cssize.hpp" 20 #include "source.hpp" 21 22 namespace Sass { 23 using namespace Constants; 24 using namespace File; 25 using namespace Sass; 26 27 inline bool sort_importers (const Sass_Importer_Entry& i, const Sass_Importer_Entry& j) 28 { return sass_importer_get_priority(i) > sass_importer_get_priority(j); } 29 30 static sass::string safe_input(const char* in_path) 31 { 32 if (in_path == nullptr || in_path[0] == '\0') return "stdin"; 33 return in_path; 34 } 35 36 static sass::string safe_output(const char* out_path, sass::string input_path) 37 { 38 if (out_path == nullptr || out_path[0] == '\0') { 39 if (input_path.empty()) return "stdout"; 40 return input_path.substr(0, input_path.find_last_of(".")) + ".css"; 41 } 42 return out_path; 43 } 44 45 Context::Context(struct Sass_Context& c_ctx) 46 : CWD(File::get_cwd()), 47 c_options(c_ctx), 48 entry_path(""), 49 head_imports(0), 50 plugins(), 51 emitter(c_options), 52 53 ast_gc(), 54 strings(), 55 resources(), 56 sheets(), 57 import_stack(), 58 callee_stack(), 59 traces(), 60 extender(Extender::NORMAL, traces), 61 c_compiler(NULL), 62 63 c_headers (sass::vector<Sass_Importer_Entry>()), 64 c_importers (sass::vector<Sass_Importer_Entry>()), 65 c_functions (sass::vector<Sass_Function_Entry>()), 66 67 indent (safe_str(c_options.indent, " ")), 68 linefeed (safe_str(c_options.linefeed, "\n")), 69 70 input_path (make_canonical_path(safe_input(c_options.input_path))), 71 output_path (make_canonical_path(safe_output(c_options.output_path, input_path))), 72 source_map_file (make_canonical_path(safe_str(c_options.source_map_file, ""))), 73 source_map_root (make_canonical_path(safe_str(c_options.source_map_root, ""))) 74 75 { 76 77 // Sass 3.4: The current working directory will no longer be placed onto the Sass load path by default. 78 // If you need the current working directory to be available, set SASS_PATH=. in your shell's environment. 79 // include_paths.push_back(CWD); 80 81 // collect more paths from different options 82 collect_include_paths(c_options.include_path); 83 collect_include_paths(c_options.include_paths); 84 collect_plugin_paths(c_options.plugin_path); 85 collect_plugin_paths(c_options.plugin_paths); 86 87 // load plugins and register custom behaviors 88 for(auto plug : plugin_paths) plugins.load_plugins(plug); 89 for(auto fn : plugins.get_headers()) c_headers.push_back(fn); 90 for(auto fn : plugins.get_importers()) c_importers.push_back(fn); 91 for(auto fn : plugins.get_functions()) c_functions.push_back(fn); 92 93 // sort the items by priority (lowest first) 94 sort (c_headers.begin(), c_headers.end(), sort_importers); 95 sort (c_importers.begin(), c_importers.end(), sort_importers); 96 97 emitter.set_filename(abs2rel(output_path, source_map_file, CWD)); 98 99 } 100 101 void Context::add_c_function(Sass_Function_Entry function) 102 { 103 c_functions.push_back(function); 104 } 105 void Context::add_c_header(Sass_Importer_Entry header) 106 { 107 c_headers.push_back(header); 108 // need to sort the array afterwards (no big deal) 109 sort (c_headers.begin(), c_headers.end(), sort_importers); 110 } 111 void Context::add_c_importer(Sass_Importer_Entry importer) 112 { 113 c_importers.push_back(importer); 114 // need to sort the array afterwards (no big deal) 115 sort (c_importers.begin(), c_importers.end(), sort_importers); 116 } 117 118 Context::~Context() 119 { 120 // resources were allocated by malloc 121 for (size_t i = 0; i < resources.size(); ++i) { 122 free(resources[i].contents); 123 free(resources[i].srcmap); 124 } 125 // free all strings we kept alive during compiler execution 126 for (size_t n = 0; n < strings.size(); ++n) free(strings[n]); 127 // everything that gets put into sources will be freed by us 128 // this shouldn't have anything in it anyway!? 129 for (size_t m = 0; m < import_stack.size(); ++m) { 130 sass_import_take_source(import_stack[m]); 131 sass_import_take_srcmap(import_stack[m]); 132 sass_delete_import(import_stack[m]); 133 } 134 // clear inner structures (vectors) and input source 135 resources.clear(); import_stack.clear(); 136 sheets.clear(); 137 } 138 139 Data_Context::~Data_Context() 140 { 141 // --> this will be freed by resources 142 // make sure we free the source even if not processed! 143 // if (resources.size() == 0 && source_c_str) free(source_c_str); 144 // if (resources.size() == 0 && srcmap_c_str) free(srcmap_c_str); 145 // source_c_str = 0; srcmap_c_str = 0; 146 } 147 148 File_Context::~File_Context() 149 { 150 } 151 152 void Context::collect_include_paths(const char* paths_str) 153 { 154 if (paths_str) { 155 const char* beg = paths_str; 156 const char* end = Prelexer::find_first<PATH_SEP>(beg); 157 158 while (end) { 159 sass::string path(beg, end - beg); 160 if (!path.empty()) { 161 if (*path.rbegin() != '/') path += '/'; 162 include_paths.push_back(path); 163 } 164 beg = end + 1; 165 end = Prelexer::find_first<PATH_SEP>(beg); 166 } 167 168 sass::string path(beg); 169 if (!path.empty()) { 170 if (*path.rbegin() != '/') path += '/'; 171 include_paths.push_back(path); 172 } 173 } 174 } 175 176 void Context::collect_include_paths(string_list* paths_array) 177 { 178 while (paths_array) 179 { 180 collect_include_paths(paths_array->string); 181 paths_array = paths_array->next; 182 } 183 } 184 185 void Context::collect_plugin_paths(const char* paths_str) 186 { 187 if (paths_str) { 188 const char* beg = paths_str; 189 const char* end = Prelexer::find_first<PATH_SEP>(beg); 190 191 while (end) { 192 sass::string path(beg, end - beg); 193 if (!path.empty()) { 194 if (*path.rbegin() != '/') path += '/'; 195 plugin_paths.push_back(path); 196 } 197 beg = end + 1; 198 end = Prelexer::find_first<PATH_SEP>(beg); 199 } 200 201 sass::string path(beg); 202 if (!path.empty()) { 203 if (*path.rbegin() != '/') path += '/'; 204 plugin_paths.push_back(path); 205 } 206 } 207 } 208 209 void Context::collect_plugin_paths(string_list* paths_array) 210 { 211 while (paths_array) 212 { 213 collect_plugin_paths(paths_array->string); 214 paths_array = paths_array->next; 215 } 216 } 217 218 // resolve the imp_path in base_path or include_paths 219 // looks for alternatives and returns a list from one directory 220 sass::vector<Include> Context::find_includes(const Importer& import) 221 { 222 // make sure we resolve against an absolute path 223 sass::string base_path(rel2abs(import.base_path)); 224 // first try to resolve the load path relative to the base path 225 sass::vector<Include> vec(resolve_includes(base_path, import.imp_path)); 226 // then search in every include path (but only if nothing found yet) 227 for (size_t i = 0, S = include_paths.size(); vec.size() == 0 && i < S; ++i) 228 { 229 // call resolve_includes and individual base path and append all results 230 sass::vector<Include> resolved(resolve_includes(include_paths[i], import.imp_path)); 231 if (resolved.size()) vec.insert(vec.end(), resolved.begin(), resolved.end()); 232 } 233 // return vector 234 return vec; 235 } 236 237 // register include with resolved path and its content 238 // memory of the resources will be freed by us on exit 239 void Context::register_resource(const Include& inc, const Resource& res) 240 { 241 242 // do not parse same resource twice 243 // maybe raise an error in this case 244 // if (sheets.count(inc.abs_path)) { 245 // free(res.contents); free(res.srcmap); 246 // throw std::runtime_error("duplicate resource registered"); 247 // return; 248 // } 249 250 // get index for this resource 251 size_t idx = resources.size(); 252 253 // tell emitter about new resource 254 emitter.add_source_index(idx); 255 256 // put resources under our control 257 // the memory will be freed later 258 resources.push_back(res); 259 260 // add a relative link to the working directory 261 included_files.push_back(inc.abs_path); 262 // add a relative link to the source map output file 263 srcmap_links.push_back(abs2rel(inc.abs_path, source_map_file, CWD)); 264 265 // get pointer to the loaded content 266 Sass_Import_Entry import = sass_make_import( 267 inc.imp_path.c_str(), 268 inc.abs_path.c_str(), 269 res.contents, 270 res.srcmap 271 ); 272 // add the entry to the stack 273 import_stack.push_back(import); 274 275 // get pointer to the loaded content 276 const char* contents = resources[idx].contents; 277 SourceFileObj source = SASS_MEMORY_NEW(SourceFile, 278 inc.abs_path.c_str(), contents, idx); 279 280 // create the initial parser state from resource 281 SourceSpan pstate(source); 282 283 // check existing import stack for possible recursion 284 for (size_t i = 0; i < import_stack.size() - 2; ++i) { 285 auto parent = import_stack[i]; 286 if (std::strcmp(parent->abs_path, import->abs_path) == 0) { 287 sass::string cwd(File::get_cwd()); 288 // make path relative to the current directory 289 sass::string stack("An @import loop has been found:"); 290 for (size_t n = 1; n < i + 2; ++n) { 291 stack += "\n " + sass::string(File::abs2rel(import_stack[n]->abs_path, cwd, cwd)) + 292 " imports " + sass::string(File::abs2rel(import_stack[n+1]->abs_path, cwd, cwd)); 293 } 294 // implement error throw directly until we 295 // decided how to handle full stack traces 296 throw Exception::InvalidSyntax(pstate, traces, stack); 297 // error(stack, prstate ? *prstate : pstate, import_stack); 298 } 299 } 300 301 // create a parser instance from the given c_str buffer 302 Parser p(source, *this, traces); 303 // do not yet dispose these buffers 304 sass_import_take_source(import); 305 sass_import_take_srcmap(import); 306 // then parse the root block 307 Block_Obj root = p.parse(); 308 // delete memory of current stack frame 309 sass_delete_import(import_stack.back()); 310 // remove current stack frame 311 import_stack.pop_back(); 312 // create key/value pair for ast node 313 std::pair<const sass::string, StyleSheet> 314 ast_pair(inc.abs_path, { res, root }); 315 // register resulting resource 316 sheets.insert(ast_pair); 317 } 318 319 // register include with resolved path and its content 320 // memory of the resources will be freed by us on exit 321 void Context::register_resource(const Include& inc, const Resource& res, SourceSpan& prstate) 322 { 323 traces.push_back(Backtrace(prstate)); 324 register_resource(inc, res); 325 traces.pop_back(); 326 } 327 328 // Add a new import to the context (called from `import_url`) 329 Include Context::load_import(const Importer& imp, SourceSpan pstate) 330 { 331 332 // search for valid imports (ie. partials) on the filesystem 333 // this may return more than one valid result (ambiguous imp_path) 334 const sass::vector<Include> resolved(find_includes(imp)); 335 336 // error nicely on ambiguous imp_path 337 if (resolved.size() > 1) { 338 sass::ostream msg_stream; 339 msg_stream << "It's not clear which file to import for "; 340 msg_stream << "'@import \"" << imp.imp_path << "\"'." << "\n"; 341 msg_stream << "Candidates:" << "\n"; 342 for (size_t i = 0, L = resolved.size(); i < L; ++i) 343 { msg_stream << " " << resolved[i].imp_path << "\n"; } 344 msg_stream << "Please delete or rename all but one of these files." << "\n"; 345 error(msg_stream.str(), pstate, traces); 346 } 347 348 // process the resolved entry 349 else if (resolved.size() == 1) { 350 bool use_cache = c_importers.size() == 0; 351 // use cache for the resource loading 352 if (use_cache && sheets.count(resolved[0].abs_path)) return resolved[0]; 353 // try to read the content of the resolved file entry 354 // the memory buffer returned must be freed by us! 355 if (char* contents = read_file(resolved[0].abs_path)) { 356 // register the newly resolved file resource 357 register_resource(resolved[0], { contents, 0 }, pstate); 358 // return resolved entry 359 return resolved[0]; 360 } 361 } 362 363 // nothing found 364 return { imp, "" }; 365 366 } 367 368 void Context::import_url (Import* imp, sass::string load_path, const sass::string& ctx_path) { 369 370 SourceSpan pstate(imp->pstate()); 371 sass::string imp_path(unquote(load_path)); 372 sass::string protocol("file"); 373 374 using namespace Prelexer; 375 if (const char* proto = sequence< identifier, exactly<':'>, exactly<'/'>, exactly<'/'> >(imp_path.c_str())) { 376 377 protocol = sass::string(imp_path.c_str(), proto - 3); 378 // if (protocol.compare("file") && true) { } 379 } 380 381 // add urls (protocol other than file) and urls without protocol to `urls` member 382 // ToDo: if ctx_path is already a file resource, we should not add it here? 383 if (imp->import_queries() || protocol != "file" || imp_path.substr(0, 2) == "//") { 384 imp->urls().push_back(SASS_MEMORY_NEW(String_Quoted, imp->pstate(), load_path)); 385 } 386 else if (imp_path.length() > 4 && imp_path.substr(imp_path.length() - 4, 4) == ".css") { 387 String_Constant* loc = SASS_MEMORY_NEW(String_Constant, pstate, unquote(load_path)); 388 Argument_Obj loc_arg = SASS_MEMORY_NEW(Argument, pstate, loc); 389 Arguments_Obj loc_args = SASS_MEMORY_NEW(Arguments, pstate); 390 loc_args->append(loc_arg); 391 Function_Call* new_url = SASS_MEMORY_NEW(Function_Call, pstate, sass::string("url"), loc_args); 392 imp->urls().push_back(new_url); 393 } 394 else { 395 const Importer importer(imp_path, ctx_path); 396 Include include(load_import(importer, pstate)); 397 if (include.abs_path.empty()) { 398 error("File to import not found or unreadable: " + imp_path + ".", pstate, traces); 399 } 400 imp->incs().push_back(include); 401 } 402 403 } 404 405 406 // call custom importers on the given (unquoted) load_path and eventually parse the resulting style_sheet 407 bool Context::call_loader(const sass::string& load_path, const char* ctx_path, SourceSpan& pstate, Import* imp, sass::vector<Sass_Importer_Entry> importers, bool only_one) 408 { 409 // unique counter 410 size_t count = 0; 411 // need one correct import 412 bool has_import = false; 413 // process all custom importers (or custom headers) 414 for (Sass_Importer_Entry& importer_ent : importers) { 415 // int priority = sass_importer_get_priority(importer); 416 Sass_Importer_Fn fn = sass_importer_get_function(importer_ent); 417 // skip importer if it returns NULL 418 if (Sass_Import_List includes = 419 fn(load_path.c_str(), importer_ent, c_compiler) 420 ) { 421 // get c pointer copy to iterate over 422 Sass_Import_List it_includes = includes; 423 while (*it_includes) { ++count; 424 // create unique path to use as key 425 sass::string uniq_path = load_path; 426 if (!only_one && count) { 427 sass::ostream path_strm; 428 path_strm << uniq_path << ":" << count; 429 uniq_path = path_strm.str(); 430 } 431 // create the importer struct 432 Importer importer(uniq_path, ctx_path); 433 // query data from the current include 434 Sass_Import_Entry include_ent = *it_includes; 435 char* source = sass_import_take_source(include_ent); 436 char* srcmap = sass_import_take_srcmap(include_ent); 437 size_t line = sass_import_get_error_line(include_ent); 438 size_t column = sass_import_get_error_column(include_ent); 439 const char *abs_path = sass_import_get_abs_path(include_ent); 440 // handle error message passed back from custom importer 441 // it may (or may not) override the line and column info 442 if (const char* err_message = sass_import_get_error_message(include_ent)) { 443 if (source || srcmap) register_resource({ importer, uniq_path }, { source, srcmap }, pstate); 444 if (line == sass::string::npos && column == sass::string::npos) error(err_message, pstate, traces); 445 else { error(err_message, { pstate.source, { line, column } }, traces); } 446 } 447 // content for import was set 448 else if (source) { 449 // resolved abs_path should be set by custom importer 450 // use the created uniq_path as fallback (maybe enforce) 451 sass::string path_key(abs_path ? abs_path : uniq_path); 452 // create the importer struct 453 Include include(importer, path_key); 454 // attach information to AST node 455 imp->incs().push_back(include); 456 // register the resource buffers 457 register_resource(include, { source, srcmap }, pstate); 458 } 459 // only a path was retuned 460 // try to load it like normal 461 else if(abs_path) { 462 // checks some urls to preserve 463 // `http://`, `https://` and `//` 464 // or dispatchs to `import_file` 465 // which will check for a `.css` extension 466 // or resolves the file on the filesystem 467 // added and resolved via `add_file` 468 // finally stores everything on `imp` 469 import_url(imp, abs_path, ctx_path); 470 } 471 // move to next 472 ++it_includes; 473 } 474 // deallocate the returned memory 475 sass_delete_import_list(includes); 476 // set success flag 477 has_import = true; 478 // break out of loop 479 if (only_one) break; 480 } 481 } 482 // return result 483 return has_import; 484 } 485 486 void register_function(Context&, Signature sig, Native_Function f, Env* env); 487 void register_function(Context&, Signature sig, Native_Function f, size_t arity, Env* env); 488 void register_overload_stub(Context&, sass::string name, Env* env); 489 void register_built_in_functions(Context&, Env* env); 490 void register_c_functions(Context&, Env* env, Sass_Function_List); 491 void register_c_function(Context&, Env* env, Sass_Function_Entry); 492 493 char* Context::render(Block_Obj root) 494 { 495 // check for valid block 496 if (!root) return 0; 497 // start the render process 498 root->perform(&emitter); 499 // finish emitter stream 500 emitter.finalize(); 501 // get the resulting buffer from stream 502 OutputBuffer emitted = emitter.get_buffer(); 503 // should we append a source map url? 504 if (!c_options.omit_source_map_url) { 505 // generate an embedded source map 506 if (c_options.source_map_embed) { 507 emitted.buffer += linefeed; 508 emitted.buffer += format_embedded_source_map(); 509 } 510 // or just link the generated one 511 else if (source_map_file != "") { 512 emitted.buffer += linefeed; 513 emitted.buffer += format_source_mapping_url(source_map_file); 514 } 515 } 516 // create a copy of the resulting buffer string 517 // this must be freed or taken over by implementor 518 return sass_copy_c_string(emitted.buffer.c_str()); 519 } 520 521 void Context::apply_custom_headers(Block_Obj root, const char* ctx_path, SourceSpan pstate) 522 { 523 // create a custom import to resolve headers 524 Import_Obj imp = SASS_MEMORY_NEW(Import, pstate); 525 // dispatch headers which will add custom functions 526 // custom headers are added to the import instance 527 call_headers(entry_path, ctx_path, pstate, imp); 528 // increase head count to skip later 529 head_imports += resources.size() - 1; 530 // add the statement if we have urls 531 if (!imp->urls().empty()) root->append(imp); 532 // process all other resources (add Import_Stub nodes) 533 for (size_t i = 0, S = imp->incs().size(); i < S; ++i) { 534 root->append(SASS_MEMORY_NEW(Import_Stub, pstate, imp->incs()[i])); 535 } 536 } 537 538 Block_Obj File_Context::parse() 539 { 540 541 // check if entry file is given 542 if (input_path.empty()) return {}; 543 544 // create absolute path from input filename 545 // ToDo: this should be resolved via custom importers 546 sass::string abs_path(rel2abs(input_path, CWD)); 547 548 // try to load the entry file 549 char* contents = read_file(abs_path); 550 551 // alternatively also look inside each include path folder 552 // I think this differs from ruby sass (IMO too late to remove) 553 for (size_t i = 0, S = include_paths.size(); contents == 0 && i < S; ++i) { 554 // build absolute path for this include path entry 555 abs_path = rel2abs(input_path, include_paths[i]); 556 // try to load the resulting path 557 contents = read_file(abs_path); 558 } 559 560 // abort early if no content could be loaded (various reasons) 561 if (!contents) throw std::runtime_error( 562 "File to read not found or unreadable: " 563 + std::string(input_path.c_str())); 564 565 // store entry path 566 entry_path = abs_path; 567 568 // create entry only for import stack 569 Sass_Import_Entry import = sass_make_import( 570 input_path.c_str(), 571 entry_path.c_str(), 572 contents, 573 0 574 ); 575 // add the entry to the stack 576 import_stack.push_back(import); 577 578 // create the source entry for file entry 579 register_resource({{ input_path, "." }, abs_path }, { contents, 0 }); 580 581 // create root ast tree node 582 return compile(); 583 584 } 585 586 Block_Obj Data_Context::parse() 587 { 588 589 // check if source string is given 590 if (!source_c_str) return {}; 591 592 // convert indented sass syntax 593 if(c_options.is_indented_syntax_src) { 594 // call sass2scss to convert the string 595 char * converted = sass2scss(source_c_str, 596 // preserve the structure as much as possible 597 SASS2SCSS_PRETTIFY_1 | SASS2SCSS_KEEP_COMMENT); 598 // replace old source_c_str with converted 599 free(source_c_str); source_c_str = converted; 600 } 601 602 // remember entry path (defaults to stdin for string) 603 entry_path = input_path.empty() ? "stdin" : input_path; 604 605 // ToDo: this may be resolved via custom importers 606 sass::string abs_path(rel2abs(entry_path)); 607 char* abs_path_c_str = sass_copy_c_string(abs_path.c_str()); 608 strings.push_back(abs_path_c_str); 609 610 // create entry only for the import stack 611 Sass_Import_Entry import = sass_make_import( 612 entry_path.c_str(), 613 abs_path_c_str, 614 source_c_str, 615 srcmap_c_str 616 ); 617 // add the entry to the stack 618 import_stack.push_back(import); 619 620 // register a synthetic resource (path does not really exist, skip in includes) 621 register_resource({{ input_path, "." }, input_path }, { source_c_str, srcmap_c_str }); 622 623 // create root ast tree node 624 return compile(); 625 } 626 627 // parse root block from includes 628 Block_Obj Context::compile() 629 { 630 // abort if there is no data 631 if (resources.size() == 0) return {}; 632 // get root block from the first style sheet 633 Block_Obj root = sheets.at(entry_path).root; 634 // abort on invalid root 635 if (root.isNull()) return {}; 636 Env global; // create root environment 637 // register built-in functions on env 638 register_built_in_functions(*this, &global); 639 // register custom functions (defined via C-API) 640 for (size_t i = 0, S = c_functions.size(); i < S; ++i) 641 { register_c_function(*this, &global, c_functions[i]); } 642 // create initial backtrace entry 643 // create crtp visitor objects 644 Expand expand(*this, &global); 645 Cssize cssize(*this); 646 CheckNesting check_nesting; 647 // check nesting in all files 648 for (auto sheet : sheets) { 649 auto styles = sheet.second; 650 check_nesting(styles.root); 651 } 652 // expand and eval the tree 653 root = expand(root); 654 655 Extension unsatisfied; 656 // check that all extends were used 657 if (extender.checkForUnsatisfiedExtends(unsatisfied)) { 658 throw Exception::UnsatisfiedExtend(traces, unsatisfied); 659 } 660 661 // check nesting 662 check_nesting(root); 663 // merge and bubble certain rules 664 root = cssize(root); 665 666 // clean up by removing empty placeholders 667 // ToDo: maybe we can do this somewhere else? 668 Remove_Placeholders remove_placeholders; 669 root->perform(&remove_placeholders); 670 671 // return processed tree 672 return root; 673 } 674 // EO compile 675 676 sass::string Context::format_embedded_source_map() 677 { 678 sass::string map = emitter.render_srcmap(*this); 679 sass::istream is( map.c_str() ); 680 sass::ostream buffer; 681 base64::encoder E; 682 E.encode(is, buffer); 683 sass::string url = "data:application/json;base64," + buffer.str(); 684 url.erase(url.size() - 1); 685 return "/*# sourceMappingURL=" + url + " */"; 686 } 687 688 sass::string Context::format_source_mapping_url(const sass::string& file) 689 { 690 sass::string url = abs2rel(file, output_path, CWD); 691 return "/*# sourceMappingURL=" + url + " */"; 692 } 693 694 char* Context::render_srcmap() 695 { 696 if (source_map_file == "") return 0; 697 sass::string map = emitter.render_srcmap(*this); 698 return sass_copy_c_string(map.c_str()); 699 } 700 701 702 // for data context we want to start after "stdin" 703 // we probably always want to skip the header includes? 704 sass::vector<sass::string> Context::get_included_files(bool skip, size_t headers) 705 { 706 // create a copy of the vector for manipulations 707 sass::vector<sass::string> includes = included_files; 708 if (includes.size() == 0) return includes; 709 if (skip) { includes.erase( includes.begin(), includes.begin() + 1 + headers); } 710 else { includes.erase( includes.begin() + 1, includes.begin() + 1 + headers); } 711 includes.erase( std::unique( includes.begin(), includes.end() ), includes.end() ); 712 std::sort( includes.begin() + (skip ? 0 : 1), includes.end() ); 713 return includes; 714 } 715 716 void register_function(Context& ctx, Signature sig, Native_Function f, Env* env) 717 { 718 Definition* def = make_native_function(sig, f, ctx); 719 def->environment(env); 720 (*env)[def->name() + "[f]"] = def; 721 } 722 723 void register_function(Context& ctx, Signature sig, Native_Function f, size_t arity, Env* env) 724 { 725 Definition* def = make_native_function(sig, f, ctx); 726 sass::ostream ss; 727 ss << def->name() << "[f]" << arity; 728 def->environment(env); 729 (*env)[ss.str()] = def; 730 } 731 732 void register_overload_stub(Context& ctx, sass::string name, Env* env) 733 { 734 Definition* stub = SASS_MEMORY_NEW(Definition, 735 SourceSpan{ "[built-in function]" }, 736 nullptr, 737 name, 738 Parameters_Obj{}, 739 nullptr, 740 true); 741 (*env)[name + "[f]"] = stub; 742 } 743 744 745 void register_built_in_functions(Context& ctx, Env* env) 746 { 747 using namespace Functions; 748 // RGB Functions 749 register_function(ctx, rgb_sig, rgb, env); 750 register_overload_stub(ctx, "rgba", env); 751 register_function(ctx, rgba_4_sig, rgba_4, 4, env); 752 register_function(ctx, rgba_2_sig, rgba_2, 2, env); 753 register_function(ctx, red_sig, red, env); 754 register_function(ctx, green_sig, green, env); 755 register_function(ctx, blue_sig, blue, env); 756 register_function(ctx, mix_sig, mix, env); 757 // HSL Functions 758 register_function(ctx, hsl_sig, hsl, env); 759 register_function(ctx, hsla_sig, hsla, env); 760 register_function(ctx, hue_sig, hue, env); 761 register_function(ctx, saturation_sig, saturation, env); 762 register_function(ctx, lightness_sig, lightness, env); 763 register_function(ctx, adjust_hue_sig, adjust_hue, env); 764 register_function(ctx, lighten_sig, lighten, env); 765 register_function(ctx, darken_sig, darken, env); 766 register_function(ctx, saturate_sig, saturate, env); 767 register_function(ctx, desaturate_sig, desaturate, env); 768 register_function(ctx, grayscale_sig, grayscale, env); 769 register_function(ctx, complement_sig, complement, env); 770 register_function(ctx, invert_sig, invert, env); 771 // Opacity Functions 772 register_function(ctx, alpha_sig, alpha, env); 773 register_function(ctx, opacity_sig, alpha, env); 774 register_function(ctx, opacify_sig, opacify, env); 775 register_function(ctx, fade_in_sig, opacify, env); 776 register_function(ctx, transparentize_sig, transparentize, env); 777 register_function(ctx, fade_out_sig, transparentize, env); 778 // Other Color Functions 779 register_function(ctx, adjust_color_sig, adjust_color, env); 780 register_function(ctx, scale_color_sig, scale_color, env); 781 register_function(ctx, change_color_sig, change_color, env); 782 register_function(ctx, ie_hex_str_sig, ie_hex_str, env); 783 // String Functions 784 register_function(ctx, unquote_sig, sass_unquote, env); 785 register_function(ctx, quote_sig, sass_quote, env); 786 register_function(ctx, str_length_sig, str_length, env); 787 register_function(ctx, str_insert_sig, str_insert, env); 788 register_function(ctx, str_index_sig, str_index, env); 789 register_function(ctx, str_slice_sig, str_slice, env); 790 register_function(ctx, to_upper_case_sig, to_upper_case, env); 791 register_function(ctx, to_lower_case_sig, to_lower_case, env); 792 // Number Functions 793 register_function(ctx, percentage_sig, percentage, env); 794 register_function(ctx, round_sig, round, env); 795 register_function(ctx, ceil_sig, ceil, env); 796 register_function(ctx, floor_sig, floor, env); 797 register_function(ctx, abs_sig, abs, env); 798 register_function(ctx, min_sig, min, env); 799 register_function(ctx, max_sig, max, env); 800 register_function(ctx, random_sig, random, env); 801 // List Functions 802 register_function(ctx, length_sig, length, env); 803 register_function(ctx, nth_sig, nth, env); 804 register_function(ctx, set_nth_sig, set_nth, env); 805 register_function(ctx, index_sig, index, env); 806 register_function(ctx, join_sig, join, env); 807 register_function(ctx, append_sig, append, env); 808 register_function(ctx, zip_sig, zip, env); 809 register_function(ctx, list_separator_sig, list_separator, env); 810 register_function(ctx, is_bracketed_sig, is_bracketed, env); 811 // Map Functions 812 register_function(ctx, map_get_sig, map_get, env); 813 register_function(ctx, map_merge_sig, map_merge, env); 814 register_function(ctx, map_remove_sig, map_remove, env); 815 register_function(ctx, map_keys_sig, map_keys, env); 816 register_function(ctx, map_values_sig, map_values, env); 817 register_function(ctx, map_has_key_sig, map_has_key, env); 818 register_function(ctx, keywords_sig, keywords, env); 819 // Introspection Functions 820 register_function(ctx, type_of_sig, type_of, env); 821 register_function(ctx, unit_sig, unit, env); 822 register_function(ctx, unitless_sig, unitless, env); 823 register_function(ctx, comparable_sig, comparable, env); 824 register_function(ctx, variable_exists_sig, variable_exists, env); 825 register_function(ctx, global_variable_exists_sig, global_variable_exists, env); 826 register_function(ctx, function_exists_sig, function_exists, env); 827 register_function(ctx, mixin_exists_sig, mixin_exists, env); 828 register_function(ctx, feature_exists_sig, feature_exists, env); 829 register_function(ctx, call_sig, call, env); 830 register_function(ctx, content_exists_sig, content_exists, env); 831 register_function(ctx, get_function_sig, get_function, env); 832 // Boolean Functions 833 register_function(ctx, not_sig, sass_not, env); 834 register_function(ctx, if_sig, sass_if, env); 835 // Misc Functions 836 register_function(ctx, inspect_sig, inspect, env); 837 register_function(ctx, unique_id_sig, unique_id, env); 838 // Selector functions 839 register_function(ctx, selector_nest_sig, selector_nest, env); 840 register_function(ctx, selector_append_sig, selector_append, env); 841 register_function(ctx, selector_extend_sig, selector_extend, env); 842 register_function(ctx, selector_replace_sig, selector_replace, env); 843 register_function(ctx, selector_unify_sig, selector_unify, env); 844 register_function(ctx, is_superselector_sig, is_superselector, env); 845 register_function(ctx, simple_selectors_sig, simple_selectors, env); 846 register_function(ctx, selector_parse_sig, selector_parse, env); 847 } 848 849 void register_c_functions(Context& ctx, Env* env, Sass_Function_List descrs) 850 { 851 while (descrs && *descrs) { 852 register_c_function(ctx, env, *descrs); 853 ++descrs; 854 } 855 } 856 void register_c_function(Context& ctx, Env* env, Sass_Function_Entry descr) 857 { 858 Definition* def = make_c_function(descr, ctx); 859 def->environment(env); 860 (*env)[def->name() + "[f]"] = def; 861 } 862 863 } 864