Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(302)

Side by Side Diff: tools/dart2js/sourceMapViewer/web/display.dart

Issue 280513002: Add tool for viewing source maps. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 import 'dart:html';
ricow1 2014/05/14 12:28:08 missing copyright header
zarah 2014/05/14 20:51:03 Done.
2 import 'dart:convert';
3 import 'dart:async';
4 import 'package:source_maps/source_maps.dart';
5
6 Element targetFileName = querySelector("#target_filename");
7 Element sourceFileName = querySelector("#source_filename");
8 DivElement generatedOutput = querySelector("#generated_output");
9 DivElement selectedSource = querySelector("#selected_source");
10 DivElement selectedOutputSpan = querySelector("#current_span");
11 DivElement decodedMap = querySelector("#decoded_map");
12 DivElement originalMap = querySelector("#original_map");
13
14 Map<TargetEntry, List<SpanElement>> targetEntryMap = {};
15 List<SpanElement> highlightedMapEntry = null;
16 List<String> target;
17 SingleMapping sourceMap;
18
19 void adjustDivHeightsToWindow() {
20 generatedOutput.style.height = "${window.innerHeight / 3 - 50}px";
21 selectedSource.style.height = "${window.innerHeight / 3 - 50}px";
22 decodedMap.style.height = "${window.innerHeight / 3 - 50}px";
23 originalMap.style.height = "${window.innerHeight / 3 - 50}px";
24 }
25
26 Future getMap() {
27 Completer c = new Completer();
28 HttpRequest httpRequest = new HttpRequest();
29 httpRequest
30 ..open('GET', 'http://127.0.0.1:9223/map')
31 ..onLoadEnd.listen((_) => c.complete(httpRequest.responseText))
32 ..send('');
33 return c.future;
34 }
35
36 Future fetchFile(String path) {
37 Completer c = new Completer();
38 HttpRequest httpRequest = new HttpRequest();
39 sourceFileName.text = path;
40 httpRequest
41 ..open('GET', path)
42 ..onLoadEnd.listen((_) => c.complete(httpRequest.responseText))
43 ..send('');
44 return c.future;
45 }
46
47 displaySource(String filename, List<String> source, TargetEntry entry) {
48 int line = entry.sourceLine;
49 int column = entry.sourceColumn;
50 int nameId = entry.sourceNameId;
51 String id = nameId == null ? null : sourceMap.names[nameId];
52 selectedSource.children.clear();
53 SpanElement marker = new SpanElement()
54 ..className = "marker"
55 ..appendText("*");
56 for (int pos = 0; pos < source.length; pos++) {
ricow1 2014/05/14 12:28:08 for (var l in source)
zarah 2014/05/14 20:51:03 The variable pos is needed below so this would not
57 String l = source[pos];
58 if (pos != line) {
59 selectedSource.children.add(l.isEmpty ? new BRElement() : new DivElement()
60 ..appendText(l));
61 } else {
62 selectedSource.children.add(new DivElement()
63 ..appendText(l.substring(0, column))
64 ..children.add(marker)
65 ..appendText(l.substring(column)));
66 }
67 }
68 sourceFileName.text = filename;
69 marker.scrollIntoView();
70 }
71
72 void highlightSelectedSpan(TargetEntry entry, TargetLineEntry lineEntry) {
73 selectedOutputSpan.children.clear();
74 String spanEndCol;
75 TargetEntry spanEnd;
76 bool nextEntryIsSpanEnd = false;
77 for (TargetEntry e in lineEntry.entries) {
78 if (nextEntryIsSpanEnd) {
79 spanEnd = e;
80 break;
81 }
82 if (e == entry) {
83 nextEntryIsSpanEnd = true;
84 }
85 }
86 if (spanEnd == null) {
87 spanEndCol = '${target[lineEntry.line].length} (EOL).';
88 } else {
89 spanEndCol = '${spanEnd.column}.';
90 }
91
92 String targetSpan =
93 'Target: Line ${lineEntry.line} Col. ${entry.column} - $spanEndCol';
94
95 if (entry.sourceUrlId == null) {
96 targetSpan += ' Source: unknown';
97 selectedOutputSpan.children.add(getTextElement(targetSpan));
98 return;
99 }
100
101 String source = sourceMap.urls[entry.sourceUrlId];
102 String sourceName = source.substring(source.lastIndexOf('/') + 1);
103 String sourcePoint =
104 'Source: Line ${entry.sourceLine} Col. ${entry.sourceColumn}';
105 sourcePoint +=
106 entry.sourceNameId == null ? ''
107 : ' (${sourceMap.names[entry.sourceNameId]})';
108 sourcePoint += ' in $sourceName';
109 selectedOutputSpan.children.add(getTextElement(targetSpan));
110 selectedOutputSpan.children.add(new BRElement());
111 selectedOutputSpan.children.add(getTextElement(sourcePoint));
112
113 if (highlightedMapEntry != null) {
114 highlightedMapEntry[0].style.background = 'white';
115 highlightedMapEntry[1].style.background = 'white';
116 }
117
118 String highlightColor = "#99ff99";
119 highlightedMapEntry = targetEntryMap[entry];
120 highlightedMapEntry[0]
121 ..scrollIntoView()
122 ..style.backgroundColor = highlightColor;
123 highlightedMapEntry[1]
124 ..scrollIntoView()
125 ..style.backgroundColor = highlightColor;
126 highlightedMapEntry[1].onMouseOver.listen((e) {
127 selectedOutputSpan.style.zIndex = "2";
128 selectedOutputSpan.style.visibility = "visible";
129 selectedOutputSpan.style.top = "${decodedMap.offsetTo(document.body).y +
130 decodedMap.clientHeight - 20}px";
131 selectedOutputSpan.style.left = "${decodedMap.offsetTo(document.body).x}px";
132 selectedOutputSpan.style.width= "${decodedMap.clientWidth}px";
133 });
134
135 highlightedMapEntry[1].onMouseOut.listen( (e) {
136 selectedOutputSpan.style.visibility = "hidden";
137 });
138
139 adjustDivHeightsToWindow();
140 }
141
142 void loadSource(TargetEntry entry) {
143 if (entry.sourceUrlId == null) {
144 return;
145 }
146
147 String source = sourceMap.urls[entry.sourceUrlId];
148 fetchFile(new Uri(path: "/file",
149 queryParameters: {"path": source}).toString()).then((text)
150 => displaySource(source, text.split("\n"), entry));
151 selectedSource.text = "loading";
152 }
153
154 SpanElement createSpan(String content, TargetEntry entry,
155 TargetLineEntry lineEntry) {
156 return new SpanElement()
157 ..addEventListener('click', (e) {
158 loadSource(entry);
159 highlightSelectedSpan(entry, lineEntry);
160 }, false)
161 ..className = "range${entry.sourceUrlId % 4}"
162 ..appendText(content);
163 }
164
165 Element getLineNumberElement(int line) {
166 SpanElement result = new SpanElement();
167 result.style.fontFamily = "Courier";
168 result.style.fontSize = "10pt";
169 result.appendText("${line} ");
170 return result;
171 }
172
173 Element getTextElement(String text) {
174 SpanElement result = new SpanElement();
175 result.text = text;
176 return result;
177 }
178
179 addTargetLine(int lineNumber, String content, TargetLineEntry lineEntry) {
180 if (content.isEmpty) {
181 generatedOutput.children.add(new BRElement());
Johnni Winther 2014/05/14 12:00:38 Replace `new BRElement()` by `new DivElement()..ch
zarah 2014/05/14 20:51:03 Done.
182 return;
183 }
184 if (lineEntry == null) {
185 generatedOutput.children.add(new DivElement()
186 ..children.add(getLineNumberElement(lineNumber))
187 ..children.add(getTextElement(content)));
188 return;
189 }
190 DivElement div = new DivElement();
191 div.children.add(getLineNumberElement(lineNumber));
192
193 int pos = 0;
194 TargetEntry previous = null;
195 for (TargetEntry next in lineEntry.entries) {
196 if (previous == null) {
197 if (pos < next.column) {
198 div.appendText(content.substring(pos, next.column));
199 }
200 if (content.length == next.column) {
201 div.children.add(createSpan(" ", next, lineEntry));
202 }
203 } else {
204 if (next.column <= content.length) {
205 String token = content.substring(pos, next.column);
206 div.children.add(createSpan(token, previous, lineEntry));
207 }
208 if (content.length == next.column) {
209 div.children.add(createSpan(" ", next, lineEntry));
210 }
211 }
212 pos = next.column;
213 previous = next;
214 }
215 String token = content.substring(pos);
216 if (previous == null) {
217 div.appendText(token);
218 } else {
219 div..appendText(" ")..children.add(createSpan(token, previous, lineEntry));
Johnni Winther 2014/05/14 12:00:38 Remove `..appendText(" ")`.
zarah 2014/05/14 20:51:03 Done.
220 }
221 generatedOutput.children.add(div);
222 }
223
224 // Display the target source in the HTML.
225 void displayTargetSource() {
226 List<TargetLineEntry> targetLines = sourceMap.lines;
227 int linesIndex = 0;
228 for (int line = 0; line < target.length; line++) {
229 TargetLineEntry entry = null;
230 if (linesIndex < targetLines.length
231 && targetLines[linesIndex].line == line) {
232 entry = targetLines[linesIndex];
233 linesIndex++;
234 }
235 if (entry != null) addTargetLine(line, target[line], entry); else
Johnni Winther 2014/05/14 12:00:38 Add {} to the branches.
zarah 2014/05/14 20:51:03 Done.
236 addTargetLine(line, target[line], null);
237 }
238 }
239
240 String getMappedData(String mapFileContent) {
241 // Source map contains mapping information in this format:
242 // "mappings": "A;A,yC;"
243 List<String> mapEntry = mapFileContent.split('mappings');
244 return mapEntry[mapEntry.length-1].split('"')[2];
245 }
246
247 SpanElement createMapSpan(String segment) {
248 return new SpanElement()..text = segment;
249 }
250
251 SpanElement createDecodedMapSpan(TargetEntry entry) {
252 return new SpanElement()..text = '(${entry.column}, ${entry.sourceUrlId},'
253 ' ${entry.sourceLine},'
254 ' ${entry.sourceColumn})';
255 }
256
257 displayMap(String mapFileContent) {
258 String mappedData = getMappedData(mapFileContent);
259 int sourceMapLine = 0;
260 for (String group in mappedData.split(';')) {
261 if (group.length == 0) continue;
262
263 List<String> segments = [];
264 if (!group.contains(',')) {
265 segments.add(group);
266 } else {
267 segments = group.split(',');
268 }
269
270 TargetLineEntry targetLineEntry = sourceMap.lines[sourceMapLine];
271 decodedMap.children.add(getLineNumberElement(targetLineEntry.line));
272 originalMap.children.add(getLineNumberElement(targetLineEntry.line));
273 bool first = true;
274 int entryNumber = 0;
275 for (String segment in segments) {
276 TargetEntry entry = targetLineEntry.entries[entryNumber];
277 SpanElement orignalMapSpan = createMapSpan(segment);
278 SpanElement decodedMapSpan = createDecodedMapSpan(entry);
279 if (first) {
280 first = false;
281 } else {
282 originalMap.children.add(getTextElement(', '));
283 decodedMap.children.add(getTextElement(', '));
284 }
285 originalMap.children.add(orignalMapSpan);
286 decodedMap.children.add(decodedMapSpan);
287 ++entryNumber;
288 targetEntryMap.putIfAbsent(entry, () => [orignalMapSpan, decodedMapSpan]);
289 }
290 originalMap.children.add(new BRElement());
291 decodedMap.children.add(new BRElement());
292 ++sourceMapLine;
293 }
294 }
295
296 void main() {
297 Future load(String q) => fetchFile(new Uri(path: "/file", queryParameters: {
298 "path": q
299 }).toString());
300
301 getMap().then((mapFileName) {
302 load(mapFileName).then((mapFileContent) {
303 sourceMap = new SingleMapping.fromJson(JSON.decode(mapFileContent));
304 displayMap(mapFileContent);
305 targetFileName.text = sourceMap.targetUrl;
306 load(targetFileName.text).then((targetFileContent) {
307 target = targetFileContent.split('\n');
308 displayTargetSource();
309 adjustDivHeightsToWindow();
310 });
311 });
312 });
313
314 sourceFileName.text = "<source not selected>";
315 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698