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