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

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

Powered by Google App Engine
This is Rietveld 408576698