Initial commit
[yaffs-website] / node_modules / node-sass / src / libsass / src / source_map.cpp
1 #include "sass.hpp"
2 #include <string>
3 #include <sstream>
4 #include <iostream>
5 #include <iomanip>
6
7 #include "ast.hpp"
8 #include "json.hpp"
9 #include "context.hpp"
10 #include "position.hpp"
11 #include "source_map.hpp"
12
13 namespace Sass {
14   SourceMap::SourceMap() : current_position(0, 0, 0), file("stdin") { }
15   SourceMap::SourceMap(const std::string& file) : current_position(0, 0, 0), file(file) { }
16
17   std::string SourceMap::render_srcmap(Context &ctx) {
18
19     const bool include_sources = ctx.c_options.source_map_contents;
20     const std::vector<std::string> links = ctx.srcmap_links;
21     const std::vector<Resource>& sources(ctx.resources);
22
23     JsonNode* json_srcmap = json_mkobject();
24
25     json_append_member(json_srcmap, "version", json_mknumber(3));
26
27     const char *include = file.c_str();
28     JsonNode *json_include = json_mkstring(include);
29     json_append_member(json_srcmap, "file", json_include);
30
31     // pass-through sourceRoot option
32     if (!ctx.source_map_root.empty()) {
33       JsonNode* root = json_mkstring(ctx.source_map_root.c_str());
34       json_append_member(json_srcmap, "sourceRoot", root);
35     }
36
37     JsonNode *json_includes = json_mkarray();
38     for (size_t i = 0; i < source_index.size(); ++i) {
39       std::string include(links[source_index[i]]);
40       if (ctx.c_options.source_map_file_urls) {
41         include = File::rel2abs(include);
42         // check for windows abs path
43         if (include[0] == '/') {
44           // ends up with three slashes
45           include = "file://" + include;
46         } else {
47           // needs an additional slash
48           include = "file:///" + include;
49         }
50       }
51       const char* inc = include.c_str();
52       JsonNode *json_include = json_mkstring(inc);
53       json_append_element(json_includes, json_include);
54     }
55     json_append_member(json_srcmap, "sources", json_includes);
56
57     if (include_sources) {
58       JsonNode *json_contents = json_mkarray();
59       for (size_t i = 0; i < source_index.size(); ++i) {
60         const Resource& resource(sources[source_index[i]]);
61         JsonNode *json_content = json_mkstring(resource.contents);
62         json_append_element(json_contents, json_content);
63       }
64       if (json_contents->children.head)
65         json_append_member(json_srcmap, "sourcesContent", json_contents);
66     }
67
68     JsonNode *json_names = json_mkarray();
69     // so far we have no implementation for names
70     // no problem as we do not alter any identifiers
71     json_append_member(json_srcmap, "names", json_names);
72
73     std::string mappings = serialize_mappings();
74     JsonNode *json_mappings = json_mkstring(mappings.c_str());
75     json_append_member(json_srcmap, "mappings", json_mappings);
76
77     char *str = json_stringify(json_srcmap, "\t");
78     std::string result = std::string(str);
79     free(str);
80     json_delete(json_srcmap);
81     return result;
82   }
83
84   std::string SourceMap::serialize_mappings() {
85     std::string result = "";
86
87     size_t previous_generated_line = 0;
88     size_t previous_generated_column = 0;
89     size_t previous_original_line = 0;
90     size_t previous_original_column = 0;
91     size_t previous_original_file = 0;
92     for (size_t i = 0; i < mappings.size(); ++i) {
93       const size_t generated_line = mappings[i].generated_position.line;
94       const size_t generated_column = mappings[i].generated_position.column;
95       const size_t original_line = mappings[i].original_position.line;
96       const size_t original_column = mappings[i].original_position.column;
97       const size_t original_file = mappings[i].original_position.file;
98
99       if (generated_line != previous_generated_line) {
100         previous_generated_column = 0;
101         if (generated_line > previous_generated_line) {
102           result += std::string(generated_line - previous_generated_line, ';');
103           previous_generated_line = generated_line;
104         }
105       }
106       else if (i > 0) {
107         result += ",";
108       }
109
110       // generated column
111       result += base64vlq.encode(static_cast<int>(generated_column) - static_cast<int>(previous_generated_column));
112       previous_generated_column = generated_column;
113       // file
114       result += base64vlq.encode(static_cast<int>(original_file) - static_cast<int>(previous_original_file));
115       previous_original_file = original_file;
116       // source line
117       result += base64vlq.encode(static_cast<int>(original_line) - static_cast<int>(previous_original_line));
118       previous_original_line = original_line;
119       // source column
120       result += base64vlq.encode(static_cast<int>(original_column) - static_cast<int>(previous_original_column));
121       previous_original_column = original_column;
122     }
123
124     return result;
125   }
126
127   void SourceMap::prepend(const OutputBuffer& out)
128   {
129     Offset size(out.smap.current_position);
130     for (Mapping mapping : out.smap.mappings) {
131       if (mapping.generated_position.line > size.line) {
132         throw(std::runtime_error("prepend sourcemap has illegal line"));
133       }
134       if (mapping.generated_position.line == size.line) {
135         if (mapping.generated_position.column > size.column) {
136           throw(std::runtime_error("prepend sourcemap has illegal column"));
137         }
138       }
139     }
140     // will adjust the offset
141     prepend(Offset(out.buffer));
142     // now add the new mappings
143     VECTOR_UNSHIFT(mappings, out.smap.mappings);
144   }
145
146   void SourceMap::append(const OutputBuffer& out)
147   {
148     append(Offset(out.buffer));
149   }
150
151   void SourceMap::prepend(const Offset& offset)
152   {
153     if (offset.line != 0 || offset.column != 0) {
154       for (Mapping& mapping : mappings) {
155         // move stuff on the first old line
156         if (mapping.generated_position.line == 0) {
157           mapping.generated_position.column += offset.column;
158         }
159         // make place for the new lines
160         mapping.generated_position.line += offset.line;
161       }
162     }
163     if (current_position.line == 0) {
164       current_position.column += offset.column;
165     }
166     current_position.line += offset.line;
167   }
168
169   void SourceMap::append(const Offset& offset)
170   {
171     current_position += offset;
172   }
173
174   void SourceMap::add_open_mapping(const AST_Node_Ptr node)
175   {
176     mappings.push_back(Mapping(node->pstate(), current_position));
177   }
178
179   void SourceMap::add_close_mapping(const AST_Node_Ptr node)
180   {
181     mappings.push_back(Mapping(node->pstate() + node->pstate().offset, current_position));
182   }
183
184   ParserState SourceMap::remap(const ParserState& pstate) {
185     for (size_t i = 0; i < mappings.size(); ++i) {
186       if (
187         mappings[i].generated_position.file == pstate.file &&
188         mappings[i].generated_position.line == pstate.line &&
189         mappings[i].generated_position.column == pstate.column
190       ) return ParserState(pstate.path, pstate.src, mappings[i].original_position, pstate.offset);
191     }
192     return ParserState(pstate.path, pstate.src, Position(-1, -1, -1), Offset(0, 0));
193
194   }
195
196 }