Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 } | |
| OLD | NEW |