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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: tools/dart2js/sourceMapViewer/web/display.dart
diff --git a/tools/dart2js/sourceMapViewer/web/display.dart b/tools/dart2js/sourceMapViewer/web/display.dart
new file mode 100644
index 0000000000000000000000000000000000000000..a6d02bec24852d68244abe8d3efb98fb3444cabb
--- /dev/null
+++ b/tools/dart2js/sourceMapViewer/web/display.dart
@@ -0,0 +1,323 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:html';
+import 'dart:convert';
+import 'dart:async';
+import 'package:source_maps/source_maps.dart';
+
+Element targetFileName = querySelector("#target_filename");
+Element sourceFileName = querySelector("#source_filename");
+DivElement generatedOutput = querySelector("#generated_output");
+DivElement selectedSource = querySelector("#selected_source");
+DivElement selectedOutputSpan = querySelector("#current_span");
+DivElement decodedMap = querySelector("#decoded_map");
+DivElement originalMap = querySelector("#original_map");
+
+Map<TargetEntry, List<SpanElement>> targetEntryMap = {};
+List<SpanElement> highlightedMapEntry = null;
+List<String> target;
+SingleMapping sourceMap;
+
+void adjustDivHeightsToWindow() {
+ generatedOutput.style.height = "${window.innerHeight / 3 - 50}px";
+ selectedSource.style.height = "${window.innerHeight / 3 - 50}px";
+ decodedMap.style.height = "${window.innerHeight / 3 - 50}px";
+ originalMap.style.height = "${window.innerHeight / 3 - 50}px";
+}
+
+Future getMap() {
+ Completer c = new Completer();
+ HttpRequest httpRequest = new HttpRequest();
+ httpRequest
+ ..open('GET', '/map')
+ ..onLoadEnd.listen((_) => c.complete(httpRequest.responseText))
+ ..send('');
+ return c.future;
+}
+
+Future fetchFile(String path) {
+ Completer c = new Completer();
+ HttpRequest httpRequest = new HttpRequest();
+ sourceFileName.text = path;
+ httpRequest
+ ..open('GET', path)
+ ..onLoadEnd.listen((_) => c.complete(httpRequest.responseText))
+ ..send('');
+ return c.future;
+}
+
+displaySource(String filename, List<String> source, TargetEntry entry) {
+ int line = entry.sourceLine;
+ int column = entry.sourceColumn;
+ int nameId = entry.sourceNameId;
+ String id = nameId == null ? null : sourceMap.names[nameId];
+ selectedSource.children.clear();
+ SpanElement marker = new SpanElement()
+ ..className = "marker"
+ ..appendText("*");
+ for (int pos = 0; pos < source.length; pos++) {
+ String l = source[pos];
+ if (pos != line) {
+ selectedSource.children.add(l.isEmpty ? new BRElement() : new DivElement()
+ ..appendText(l));
+ } else {
+ selectedSource.children.add(new DivElement()
+ ..appendText(l.substring(0, column))
+ ..children.add(marker)
+ ..appendText(l.substring(column)));
+ }
+ }
+ sourceFileName.text = filename;
+ marker.scrollIntoView();
+}
+
+void highlightSelectedSpan(TargetEntry entry, TargetLineEntry lineEntry) {
+ selectedOutputSpan.children.clear();
+ String spanEndCol;
+ TargetEntry spanEnd;
+ bool nextEntryIsSpanEnd = false;
+ for (TargetEntry e in lineEntry.entries) {
+ if (nextEntryIsSpanEnd) {
+ spanEnd = e;
+ break;
+ }
+ if (e == entry) {
+ nextEntryIsSpanEnd = true;
+ }
+ }
+ if (spanEnd == null) {
+ spanEndCol = '${target[lineEntry.line].length} (EOL).';
+ } else {
+ spanEndCol = '${spanEnd.column}.';
+ }
+
+ String targetSpan =
+ 'Target: Line ${lineEntry.line} Col. ${entry.column} - $spanEndCol';
+
+ if (entry.sourceUrlId == null) {
+ targetSpan += ' Source: unknown';
+ selectedOutputSpan.children.add(getTextElement(targetSpan));
+ return;
+ }
+
+ String source = sourceMap.urls[entry.sourceUrlId];
+ String sourceName = source.substring(source.lastIndexOf('/') + 1);
+ String sourcePoint =
+ 'Source: Line ${entry.sourceLine} Col. ${entry.sourceColumn}';
+ sourcePoint +=
+ entry.sourceNameId == null ? ''
+ : ' (${sourceMap.names[entry.sourceNameId]})';
+ sourcePoint += ' in $sourceName';
+ selectedOutputSpan.children.add(getTextElement(targetSpan));
+ selectedOutputSpan.children.add(new BRElement());
+ selectedOutputSpan.children.add(getTextElement(sourcePoint));
+
+ if (highlightedMapEntry != null) {
+ highlightedMapEntry[0].style.background = 'white';
+ highlightedMapEntry[1].style.background = 'white';
+ }
+
+ String highlightColor = "#99ff99";
+ highlightedMapEntry = targetEntryMap[entry];
+ highlightedMapEntry[0]
+ ..scrollIntoView()
+ ..style.backgroundColor = highlightColor;
+ highlightedMapEntry[1]
+ ..scrollIntoView()
+ ..style.backgroundColor = highlightColor;
+ highlightedMapEntry[1].onMouseOver.listen((e) {
+ selectedOutputSpan.style.zIndex = "2";
+ selectedOutputSpan.style.visibility = "visible";
+ selectedOutputSpan.style.top = "${decodedMap.offsetTo(document.body).y +
+ decodedMap.clientHeight - 20}px";
+ selectedOutputSpan.style.left = "${decodedMap.offsetTo(document.body).x}px";
+ selectedOutputSpan.style.width= "${decodedMap.clientWidth}px";
+ });
+
+ highlightedMapEntry[1].onMouseOut.listen( (e) {
+ selectedOutputSpan.style.visibility = "hidden";
+ });
+
+ adjustDivHeightsToWindow();
+}
+
+void loadSource(TargetEntry entry) {
+ if (entry.sourceUrlId == null) {
+ return;
+ }
+
+ String source = sourceMap.urls[entry.sourceUrlId];
+ fetchFile(new Uri(path: "/file",
+ queryParameters: {"path": source}).toString()).then((text)
+ => displaySource(source, text.split("\n"), entry));
+ selectedSource.text = "loading";
+}
+
+SpanElement createSpan(String content, TargetEntry entry,
+ TargetLineEntry lineEntry) {
+ return new SpanElement()
+ ..addEventListener('click', (e) {
+ loadSource(entry);
+ highlightSelectedSpan(entry, lineEntry);
+ }, false)
+ ..className = "range${entry.sourceUrlId % 4}"
+ ..appendText(content);
+}
+
+Element getLineNumberElement(int line) {
+ SpanElement result = new SpanElement();
+ result.style.fontFamily = "Courier";
+ result.style.fontSize = "10pt";
+ result.appendText("${line} ");
+ return result;
+}
+
+Element getTextElement(String text) {
+ SpanElement result = new SpanElement();
+ result.text = text;
+ return result;
+}
+
+addTargetLine(int lineNumber, String content, TargetLineEntry lineEntry) {
+ if (content.isEmpty) {
+ generatedOutput.children.add(new DivElement()
+ ..children.add(getLineNumberElement(lineNumber)));
+ return;
+ }
+ if (lineEntry == null) {
+ generatedOutput.children.add(new DivElement()
+ ..children.add(getLineNumberElement(lineNumber))
+ ..children.add(getTextElement(content)));
+ return;
+ }
+ DivElement div = new DivElement();
+ div.children.add(getLineNumberElement(lineNumber));
+
+ int pos = 0;
+ TargetEntry previous = null;
+ for (TargetEntry next in lineEntry.entries) {
+ if (previous == null) {
+ if (pos < next.column) {
+ div.appendText(content.substring(pos, next.column));
+ }
+ if (content.length == next.column) {
+ div.children.add(createSpan(" ", next, lineEntry));
+ }
+ } else {
+ if (next.column <= content.length) {
+ String token = content.substring(pos, next.column);
+ div.children.add(createSpan(token, previous, lineEntry));
+ }
+ if (content.length == next.column) {
+ div.children.add(createSpan(" ", next, lineEntry));
+ }
+ }
+ pos = next.column;
+ previous = next;
+ }
+ String token = content.substring(pos);
+ if (previous == null) {
+ div.appendText(token);
+ } else {
+ div..children.add(createSpan(token, previous, lineEntry));
+ }
+ generatedOutput.children.add(div);
+}
+
+// Display the target source in the HTML.
+void displayTargetSource() {
+ List<TargetLineEntry> targetLines = sourceMap.lines;
+ int linesIndex = 0;
+ for (int line = 0; line < target.length; line++) {
+ TargetLineEntry entry = null;
+ if (linesIndex < targetLines.length
+ && targetLines[linesIndex].line == line) {
+ entry = targetLines[linesIndex];
+ linesIndex++;
+ }
+ if (entry != null) {
+ addTargetLine(line, target[line], entry);
+ } else {
+ addTargetLine(line, target[line], null);
+ }
+ }
+}
+
+String getMappedData(String mapFileContent) {
+ // Source map contains mapping information in this format:
+ // "mappings": "A;A,yC;"
+ List<String> mapEntry = mapFileContent.split('mappings');
+ return mapEntry[mapEntry.length-1].split('"')[2];
+}
+
+SpanElement createMapSpan(String segment) {
+ return new SpanElement()..text = segment;
+}
+
+SpanElement createDecodedMapSpan(TargetEntry entry) {
+ return new SpanElement()..text = '(${entry.column}, ${entry.sourceUrlId},'
+ ' ${entry.sourceLine},'
+ ' ${entry.sourceColumn})';
+}
+
+displayMap(String mapFileContent) {
+ String mappedData = getMappedData(mapFileContent);
+ int sourceMapLine = 0;
+ for (String group in mappedData.split(';')) {
+ if (group.length == 0) continue;
+
+ List<String> segments = [];
+ if (!group.contains(',')) {
+ segments.add(group);
+ } else {
+ segments = group.split(',');
+ }
+
+ TargetLineEntry targetLineEntry = sourceMap.lines[sourceMapLine];
+ decodedMap.children.add(getLineNumberElement(targetLineEntry.line));
+ originalMap.children.add(getLineNumberElement(targetLineEntry.line));
+ bool first = true;
+ int entryNumber = 0;
+ for (String segment in segments) {
+ TargetEntry entry = targetLineEntry.entries[entryNumber];
+ SpanElement orignalMapSpan = createMapSpan(segment);
+ SpanElement decodedMapSpan = createDecodedMapSpan(entry);
+ if (first) {
+ first = false;
+ } else {
+ originalMap.children.add(getTextElement(', '));
+ decodedMap.children.add(getTextElement(', '));
+ }
+ originalMap.children.add(orignalMapSpan);
+ decodedMap.children.add(decodedMapSpan);
+ ++entryNumber;
+ targetEntryMap.putIfAbsent(entry, () => [orignalMapSpan, decodedMapSpan]);
+ }
+ originalMap.children.add(new BRElement());
+ decodedMap.children.add(new BRElement());
+ ++sourceMapLine;
+ }
+}
+
+void main() {
+ Future load(String q) => fetchFile(new Uri(path: "/file", queryParameters: {
+ "path": q
+ }).toString());
+
+ getMap().then((mapFileName) {
+ load(mapFileName).then((mapFileContent) {
+ sourceMap = new SingleMapping.fromJson(JSON.decode(mapFileContent));
+ displayMap(mapFileContent);
+ targetFileName.text = sourceMap.targetUrl;
+ load(targetFileName.text).then((targetFileContent) {
+ target = targetFileContent.split('\n');
+ displayTargetSource();
+ adjustDivHeightsToWindow();
+ });
+ });
+ });
+
+ sourceFileName.text = "<source not selected>";
+}

Powered by Google App Engine
This is Rietveld 408576698