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

source code of gtk/subprojects/libsass/src/source_map.cpp