OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 /// Helper for creating HTML visualization of the source map information | 5 /// Helper for creating HTML visualization of the source map information |
6 /// generated by a [SourceMapProcessor]. | 6 /// generated by a [SourceMapProcessor]. |
7 | 7 |
8 library sourcemap.html.helper; | 8 library sourcemap.html.helper; |
9 | 9 |
10 import 'dart:convert'; | 10 import 'dart:convert'; |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
47 /// Use gradient on spans to visually identify consecutive spans mapped to the | 47 /// Use gradient on spans to visually identify consecutive spans mapped to the |
48 /// same source location. | 48 /// same source location. |
49 HSV startColor = toColor(index); | 49 HSV startColor = toColor(index); |
50 HSV endColor = new HSV(startColor.h, startColor.s + 0.4, startColor.v - 0.2); | 50 HSV endColor = new HSV(startColor.h, startColor.s + 0.4, startColor.v - 0.2); |
51 return 'linear-gradient(to right, ${startColor.toCss}, ${endColor.toCss})'; | 51 return 'linear-gradient(to right, ${startColor.toCss}, ${endColor.toCss})'; |
52 } | 52 } |
53 | 53 |
54 /// Return the html for the [index] line number. If [width] is provided, shorter | 54 /// Return the html for the [index] line number. If [width] is provided, shorter |
55 /// line numbers will be prefixed with spaces to match the width. | 55 /// line numbers will be prefixed with spaces to match the width. |
56 String lineNumber(int index, | 56 String lineNumber(int index, |
57 {int width, | 57 {int width, bool useNbsp: false, String className}) { |
58 bool useNbsp: false, | |
59 String className}) { | |
60 if (className == null) { | 58 if (className == null) { |
61 className = 'lineNumber'; | 59 className = 'lineNumber'; |
62 } | 60 } |
63 String text = '${index + 1}'; | 61 String text = '${index + 1}'; |
64 String padding = useNbsp ? ' ' : ' '; | 62 String padding = useNbsp ? ' ' : ' '; |
65 if (width != null && text.length < width) { | 63 if (width != null && text.length < width) { |
66 text = (padding * (width - text.length)) + text; | 64 text = (padding * (width - text.length)) + text; |
67 } | 65 } |
68 return '<span class="$className">$text$padding</span>'; | 66 return '<span class="$className">$text$padding</span>'; |
69 } | 67 } |
70 | 68 |
71 /// Return the html escaped [text]. | 69 /// Return the html escaped [text]. |
72 String escape(String text) { | 70 String escape(String text) { |
73 return const HtmlEscape().convert(text); | 71 return const HtmlEscape().convert(text); |
74 } | 72 } |
75 | 73 |
76 /// Information needed to generate HTML for a single [SourceMapInfo]. | 74 /// Information needed to generate HTML for a single [SourceMapInfo]. |
77 class SourceMapHtmlInfo { | 75 class SourceMapHtmlInfo { |
78 final SourceMapInfo sourceMapInfo; | 76 final SourceMapInfo sourceMapInfo; |
79 final CodeProcessor codeProcessor; | 77 final CodeProcessor codeProcessor; |
80 final SourceLocationCollection sourceLocationCollection; | 78 final SourceLocationCollection sourceLocationCollection; |
81 | 79 |
82 SourceMapHtmlInfo(this.sourceMapInfo, | 80 SourceMapHtmlInfo( |
83 this.codeProcessor, | 81 this.sourceMapInfo, this.codeProcessor, this.sourceLocationCollection); |
84 this.sourceLocationCollection); | |
85 | 82 |
86 String toString() { | 83 String toString() { |
87 return sourceMapInfo.toString(); | 84 return sourceMapInfo.toString(); |
88 } | 85 } |
89 } | 86 } |
90 | 87 |
91 /// A collection of source locations. | 88 /// A collection of source locations. |
92 /// | 89 /// |
93 /// Used to index source locations for visualization and linking. | 90 /// Used to index source locations for visualization and linking. |
94 class SourceLocationCollection { | 91 class SourceLocationCollection { |
(...skipping 24 matching lines...) Expand all Loading... |
119 bool get showLocationAsSpan; | 116 bool get showLocationAsSpan; |
120 } | 117 } |
121 | 118 |
122 class CustomColorScheme implements CssColorScheme { | 119 class CustomColorScheme implements CssColorScheme { |
123 final bool showLocationAsSpan; | 120 final bool showLocationAsSpan; |
124 final Function single; | 121 final Function single; |
125 final Function multi; | 122 final Function multi; |
126 | 123 |
127 CustomColorScheme( | 124 CustomColorScheme( |
128 {this.showLocationAsSpan: false, | 125 {this.showLocationAsSpan: false, |
129 String this.single(var id), | 126 String this.single(var id), |
130 String this.multi(List ids)}); | 127 String this.multi(List ids)}); |
131 | 128 |
132 String singleLocationToCssColor(var id) => single != null ? single(id) : null; | 129 String singleLocationToCssColor(var id) => single != null ? single(id) : null; |
133 | 130 |
134 String multiLocationToCssColor(List ids) => multi != null ? multi(ids) : null; | 131 String multiLocationToCssColor(List ids) => multi != null ? multi(ids) : null; |
135 } | 132 } |
136 | 133 |
137 class PatternCssColorScheme implements CssColorScheme { | 134 class PatternCssColorScheme implements CssColorScheme { |
138 const PatternCssColorScheme(); | 135 const PatternCssColorScheme(); |
139 | 136 |
140 bool get showLocationAsSpan => true; | 137 bool get showLocationAsSpan => true; |
141 | 138 |
142 String singleLocationToCssColor(int index) { | 139 String singleLocationToCssColor(int index) { |
143 return "background:${toPattern(index)};"; | 140 return "background:${toPattern(index)};"; |
144 } | 141 } |
145 | 142 |
146 String multiLocationToCssColor(List<int> indices) { | 143 String multiLocationToCssColor(List<int> indices) { |
147 | |
148 StringBuffer sb = new StringBuffer(); | 144 StringBuffer sb = new StringBuffer(); |
149 double delta = 100.0 / (indices.length); | 145 double delta = 100.0 / (indices.length); |
150 double position = 0.0; | 146 double position = 0.0; |
151 | 147 |
152 void addColor(String color) { | 148 void addColor(String color) { |
153 sb.write(', ${color} ${position.toInt()}%'); | 149 sb.write(', ${color} ${position.toInt()}%'); |
154 position += delta; | 150 position += delta; |
155 sb.write(', ${color} ${position.toInt()}%'); | 151 sb.write(', ${color} ${position.toInt()}%'); |
156 } | 152 } |
157 | 153 |
158 for (int index in indices) { | 154 for (int index in indices) { |
159 addColor('${toColorCss(index)}'); | 155 addColor('${toColorCss(index)}'); |
160 } | 156 } |
161 return 'background: linear-gradient(to right${sb}); ' | 157 return 'background: linear-gradient(to right${sb}); ' |
162 'background-size: 10px 10px;'; | 158 'background-size: 10px 10px;'; |
163 } | 159 } |
164 } | 160 } |
165 | 161 |
166 class SingleColorScheme implements CssColorScheme { | 162 class SingleColorScheme implements CssColorScheme { |
167 const SingleColorScheme(); | 163 const SingleColorScheme(); |
168 | 164 |
169 bool get showLocationAsSpan => false; | 165 bool get showLocationAsSpan => false; |
170 | 166 |
171 String singleLocationToCssColor(int index) { | 167 String singleLocationToCssColor(int index) { |
172 return "background:${toColorCss(index)};"; | 168 return "background:${toColorCss(index)};"; |
173 } | 169 } |
174 | 170 |
175 String multiLocationToCssColor(List<int> indices) { | 171 String multiLocationToCssColor(List<int> indices) { |
176 StringBuffer sb = new StringBuffer(); | 172 StringBuffer sb = new StringBuffer(); |
177 double delta = 100.0 / (indices.length); | 173 double delta = 100.0 / (indices.length); |
178 double position = 0.0; | 174 double position = 0.0; |
179 | 175 |
180 void addColor(String color) { | 176 void addColor(String color) { |
181 sb.write(', ${color} ${position.toInt()}%'); | 177 sb.write(', ${color} ${position.toInt()}%'); |
182 position += delta; | 178 position += delta; |
183 sb.write(', ${color} ${position.toInt()}%'); | 179 sb.write(', ${color} ${position.toInt()}%'); |
184 } | 180 } |
185 | 181 |
186 for (int index in indices) { | 182 for (int index in indices) { |
187 addColor('${toColorCss(index)}'); | 183 addColor('${toColorCss(index)}'); |
188 } | 184 } |
189 return 'background: linear-gradient(to bottom${sb}); ' | 185 return 'background: linear-gradient(to bottom${sb}); ' |
190 'background-size: 10px 3px;'; | 186 'background-size: 10px 3px;'; |
191 } | 187 } |
192 } | 188 } |
193 | 189 |
194 /// Processor that computes the HTML representation of a block of JavaScript | 190 /// Processor that computes the HTML representation of a block of JavaScript |
195 /// code and collects the source locations mapped in the code. | 191 /// code and collects the source locations mapped in the code. |
196 class CodeProcessor { | 192 class CodeProcessor { |
197 int lineIndex = 0; | 193 int lineIndex = 0; |
198 final String name; | 194 final String name; |
199 int currentJsSourceOffset = 0; | 195 int currentJsSourceOffset = 0; |
200 final SourceLocationCollection collection; | 196 final SourceLocationCollection collection; |
201 final Map<int, List<SourceLocation>> codeLocations = {}; | 197 final Map<int, List<SourceLocation>> codeLocations = {}; |
202 final CssColorScheme colorScheme; | 198 final CssColorScheme colorScheme; |
203 | 199 |
204 CodeProcessor(this.name, this.collection, | 200 CodeProcessor(this.name, this.collection, |
205 {this.colorScheme: const PatternCssColorScheme()}); | 201 {this.colorScheme: const PatternCssColorScheme()}); |
206 | 202 |
207 void addSourceLocation(int targetOffset, SourceLocation sourceLocation) { | 203 void addSourceLocation(int targetOffset, SourceLocation sourceLocation) { |
208 codeLocations.putIfAbsent(targetOffset, () => []).add(sourceLocation); | 204 codeLocations.putIfAbsent(targetOffset, () => []).add(sourceLocation); |
209 collection.registerSourceLocation(sourceLocation); | 205 collection.registerSourceLocation(sourceLocation); |
210 } | 206 } |
211 | 207 |
212 String convertToHtml(String text) { | 208 String convertToHtml(String text) { |
213 List<Annotation> annotations = <Annotation>[]; | 209 List<Annotation> annotations = <Annotation>[]; |
214 codeLocations.forEach((int codeOffset, List<SourceLocation> locations) { | 210 codeLocations.forEach((int codeOffset, List<SourceLocation> locations) { |
215 for (SourceLocation location in locations) { | 211 for (SourceLocation location in locations) { |
216 if (location != null) { | 212 if (location != null) { |
217 annotations.add(new Annotation( | 213 annotations.add(new Annotation( |
218 collection.getIndex(location), | 214 collection.getIndex(location), codeOffset, location.shortText)); |
219 codeOffset, | |
220 location.shortText)); | |
221 } | 215 } |
222 } | 216 } |
223 }); | 217 }); |
224 return convertAnnotatedCodeToHtml( | 218 return convertAnnotatedCodeToHtml(text, annotations, |
225 text, annotations, colorScheme: colorScheme, | 219 colorScheme: colorScheme, |
226 elementScheme: new HighlightLinkScheme(name), | 220 elementScheme: new HighlightLinkScheme(name), |
227 windowSize: 3); | 221 windowSize: 3); |
228 } | 222 } |
229 } | 223 } |
230 | 224 |
231 class ElementScheme { | 225 class ElementScheme { |
232 const ElementScheme(); | 226 const ElementScheme(); |
233 | 227 |
234 String getName(var id, Set ids) => null; | 228 String getName(var id, Set ids) => null; |
235 String getHref(var id, Set ids) => null; | 229 String getHref(var id, Set ids) => null; |
(...skipping 27 matching lines...) Expand all Loading... |
263 String onmouseover = indices.map((i) => '\'$i\'').join(','); | 257 String onmouseover = indices.map((i) => '\'$i\'').join(','); |
264 return "highlight([${onmouseover}]);"; | 258 return "highlight([${onmouseover}]);"; |
265 } | 259 } |
266 | 260 |
267 @override | 261 @override |
268 String onMouseOver(int id, Set<int> indices) { | 262 String onMouseOver(int id, Set<int> indices) { |
269 return "highlight([]);"; | 263 return "highlight([]);"; |
270 } | 264 } |
271 } | 265 } |
272 | 266 |
273 String convertAnnotatedCodeToHtml( | 267 String convertAnnotatedCodeToHtml(String code, Iterable<Annotation> annotations, |
274 String code, | |
275 Iterable<Annotation> annotations, | |
276 {CssColorScheme colorScheme: const SingleColorScheme(), | 268 {CssColorScheme colorScheme: const SingleColorScheme(), |
277 ElementScheme elementScheme: const ElementScheme(), | 269 ElementScheme elementScheme: const ElementScheme(), |
278 int windowSize}) { | 270 int windowSize}) { |
279 StringBuffer htmlBuffer = new StringBuffer(); | 271 StringBuffer htmlBuffer = new StringBuffer(); |
280 List<CodeLine> lines = convertAnnotatedCodeToCodeLines( | 272 List<CodeLine> lines = convertAnnotatedCodeToCodeLines(code, annotations, |
281 code, annotations, | |
282 windowSize: windowSize); | 273 windowSize: windowSize); |
283 int lineNoWidth; | 274 int lineNoWidth; |
284 if (lines.isNotEmpty) { | 275 if (lines.isNotEmpty) { |
285 lineNoWidth = '${lines.last.lineNo + 1}'.length; | 276 lineNoWidth = '${lines.last.lineNo + 1}'.length; |
286 } | 277 } |
287 HtmlPrintContext context = new HtmlPrintContext( | 278 HtmlPrintContext context = new HtmlPrintContext( |
288 lineNoWidth: lineNoWidth, | 279 lineNoWidth: lineNoWidth, |
289 getAnnotationData: createAnnotationDataFunction( | 280 getAnnotationData: createAnnotationDataFunction( |
290 colorScheme: colorScheme, | 281 colorScheme: colorScheme, elementScheme: elementScheme)); |
291 elementScheme: elementScheme)); | |
292 for (CodeLine line in lines) { | 282 for (CodeLine line in lines) { |
293 line.printHtmlOn(htmlBuffer, context); | 283 line.printHtmlOn(htmlBuffer, context); |
294 } | 284 } |
295 return htmlBuffer.toString(); | 285 return htmlBuffer.toString(); |
296 } | 286 } |
297 | 287 |
298 List<CodeLine> convertAnnotatedCodeToCodeLines( | 288 List<CodeLine> convertAnnotatedCodeToCodeLines( |
299 String code, | 289 String code, Iterable<Annotation> annotations, |
300 Iterable<Annotation> annotations, | 290 {int startLine, int endLine, int windowSize, Uri uri}) { |
301 {int startLine, | |
302 int endLine, | |
303 int windowSize, | |
304 Uri uri}) { | |
305 | |
306 List<CodeLine> lines = <CodeLine>[]; | 291 List<CodeLine> lines = <CodeLine>[]; |
307 CodeLine currentLine; | 292 CodeLine currentLine; |
308 final List<Annotation> currentAnnotations = <Annotation>[]; | 293 final List<Annotation> currentAnnotations = <Annotation>[]; |
309 int offset = 0; | 294 int offset = 0; |
310 int lineIndex = 0; | 295 int lineIndex = 0; |
311 int firstLine; | 296 int firstLine; |
312 int lastLine; | 297 int lastLine; |
313 | 298 |
314 void addCode(String code) { | 299 void addCode(String code) { |
315 if (currentLine != null) { | 300 if (currentLine != null) { |
316 currentLine.codeBuffer.write(code); | 301 currentLine.codeBuffer.write(code); |
317 currentLine.codeParts.add( | 302 currentLine.codeParts |
318 new CodePart(currentAnnotations.toList(), code)); | 303 .add(new CodePart(currentAnnotations.toList(), code)); |
319 currentAnnotations.clear(); | 304 currentAnnotations.clear(); |
320 } | 305 } |
321 } | 306 } |
322 | 307 |
323 void addAnnotations(List<Annotation> annotations) { | 308 void addAnnotations(List<Annotation> annotations) { |
324 currentAnnotations.addAll(annotations); | 309 currentAnnotations.addAll(annotations); |
325 currentLine.annotations.addAll(annotations); | 310 currentLine.annotations.addAll(annotations); |
326 } | 311 } |
327 | 312 |
328 void beginLine(int currentOffset) { | 313 void beginLine(int currentOffset) { |
329 lines.add(currentLine = | 314 lines |
330 new CodeLine(lines.length, currentOffset, uri: uri)); | 315 .add(currentLine = new CodeLine(lines.length, currentOffset, uri: uri)); |
331 } | 316 } |
332 | 317 |
333 void endCurrentLocation() { | 318 void endCurrentLocation() { |
334 if (currentAnnotations.isNotEmpty) { | 319 if (currentAnnotations.isNotEmpty) { |
335 addCode(''); | 320 addCode(''); |
336 } | 321 } |
337 } | 322 } |
338 | 323 |
339 void addSubstring(int until, {bool isFirst: false, bool isLast: false}) { | 324 void addSubstring(int until, {bool isFirst: false, bool isLast: false}) { |
340 if (until <= offset) return; | 325 if (until <= offset) return; |
(...skipping 28 matching lines...) Expand all Loading... |
369 void insertAnnotations(List<Annotation> annotations) { | 354 void insertAnnotations(List<Annotation> annotations) { |
370 endCurrentLocation(); | 355 endCurrentLocation(); |
371 addAnnotations(annotations); | 356 addAnnotations(annotations); |
372 if (annotations.last == null) { | 357 if (annotations.last == null) { |
373 endCurrentLocation(); | 358 endCurrentLocation(); |
374 } | 359 } |
375 } | 360 } |
376 | 361 |
377 Map<int, List<Annotation>> annotationMap = <int, List<Annotation>>{}; | 362 Map<int, List<Annotation>> annotationMap = <int, List<Annotation>>{}; |
378 for (Annotation annotation in annotations) { | 363 for (Annotation annotation in annotations) { |
379 annotationMap.putIfAbsent(annotation.codeOffset, () => <Annotation>[]) | 364 annotationMap |
380 .add(annotation); | 365 .putIfAbsent(annotation.codeOffset, () => <Annotation>[]) |
| 366 .add(annotation); |
381 } | 367 } |
382 | 368 |
383 bool first = true; | 369 bool first = true; |
384 for (int codeOffset in annotationMap.keys.toList()..sort()) { | 370 for (int codeOffset in annotationMap.keys.toList()..sort()) { |
385 List<Annotation> annotationList = annotationMap[codeOffset]; | 371 List<Annotation> annotationList = annotationMap[codeOffset]; |
386 addSubstring(codeOffset, isFirst: first); | 372 addSubstring(codeOffset, isFirst: first); |
387 insertAnnotations(annotationList); | 373 insertAnnotations(annotationList); |
388 first = false; | 374 first = false; |
389 } | 375 } |
390 | 376 |
391 addSubstring(code.length, isFirst: first, isLast: true); | 377 addSubstring(code.length, isFirst: first, isLast: true); |
392 endCurrentLocation(); | 378 endCurrentLocation(); |
393 | 379 |
394 int start = startLine ?? 0; | 380 int start = startLine ?? 0; |
395 int end = endLine ?? lines.length - 1; | 381 int end = endLine ?? lines.length - 1; |
396 if (windowSize != null) { | 382 if (windowSize != null) { |
397 start = Math.max(firstLine - windowSize, start); | 383 start = Math.max(firstLine - windowSize, start); |
398 end = Math.min(lastLine + windowSize, end); | 384 end = Math.min(lastLine + windowSize, end); |
399 } | 385 } |
400 return lines.sublist(start, end); | 386 return lines.sublist(start, end); |
401 } | 387 } |
402 | 388 |
403 /// Computes the HTML representation for a collection of JavaScript code blocks. | 389 /// Computes the HTML representation for a collection of JavaScript code blocks. |
404 String computeJsHtml(Iterable<SourceMapHtmlInfo> infoList) { | 390 String computeJsHtml(Iterable<SourceMapHtmlInfo> infoList) { |
405 | |
406 StringBuffer jsCodeBuffer = new StringBuffer(); | 391 StringBuffer jsCodeBuffer = new StringBuffer(); |
407 for (SourceMapHtmlInfo info in infoList) { | 392 for (SourceMapHtmlInfo info in infoList) { |
408 String name = info.sourceMapInfo.name; | 393 String name = info.sourceMapInfo.name; |
409 String html = info.codeProcessor.convertToHtml(info.sourceMapInfo.code); | 394 String html = info.codeProcessor.convertToHtml(info.sourceMapInfo.code); |
410 String onclick = 'show(\'$name\');'; | 395 String onclick = 'show(\'$name\');'; |
411 jsCodeBuffer.write( | 396 jsCodeBuffer |
412 '<h3 onclick="$onclick">JS code for: ${escape(name)}</h3>\n'); | 397 .write('<h3 onclick="$onclick">JS code for: ${escape(name)}</h3>\n'); |
413 jsCodeBuffer.write(''' | 398 jsCodeBuffer.write(''' |
414 <pre> | 399 <pre> |
415 $html | 400 $html |
416 </pre> | 401 </pre> |
417 '''); | 402 '''); |
418 } | 403 } |
419 return jsCodeBuffer.toString(); | 404 return jsCodeBuffer.toString(); |
420 } | 405 } |
421 | 406 |
422 /// Computes the HTML representation of the source mapping information for a | 407 /// Computes the HTML representation of the source mapping information for a |
423 /// collection of JavaScript code blocks. | 408 /// collection of JavaScript code blocks. |
424 String computeJsTraceHtml(Iterable<SourceMapHtmlInfo> infoList) { | 409 String computeJsTraceHtml(Iterable<SourceMapHtmlInfo> infoList) { |
425 StringBuffer jsTraceBuffer = new StringBuffer(); | 410 StringBuffer jsTraceBuffer = new StringBuffer(); |
426 for (SourceMapHtmlInfo info in infoList) { | 411 for (SourceMapHtmlInfo info in infoList) { |
427 String name = info.sourceMapInfo.name; | 412 String name = info.sourceMapInfo.name; |
428 String jsTrace = computeJsTraceHtmlPart( | 413 String jsTrace = computeJsTraceHtmlPart( |
429 info.sourceMapInfo.codePoints, info.sourceLocationCollection); | 414 info.sourceMapInfo.codePoints, info.sourceLocationCollection); |
430 jsTraceBuffer.write(''' | 415 jsTraceBuffer.write(''' |
431 <div name="$name" class="js-trace-buffer" style="display:none;"> | 416 <div name="$name" class="js-trace-buffer" style="display:none;"> |
432 <h3>Trace for: ${escape(name)}</h3> | 417 <h3>Trace for: ${escape(name)}</h3> |
433 $jsTrace | 418 $jsTrace |
434 </div> | 419 </div> |
435 '''); | 420 '''); |
436 } | 421 } |
437 return jsTraceBuffer.toString(); | 422 return jsTraceBuffer.toString(); |
438 } | 423 } |
439 | 424 |
440 /// Computes the HTML information for the [info]. | 425 /// Computes the HTML information for the [info]. |
441 SourceMapHtmlInfo createHtmlInfo(SourceLocationCollection collection, | 426 SourceMapHtmlInfo createHtmlInfo( |
442 SourceMapInfo info) { | 427 SourceLocationCollection collection, SourceMapInfo info) { |
443 js.Node node = info.node; | 428 js.Node node = info.node; |
444 String code = info.code; | 429 String code = info.code; |
445 String name = info.name; | 430 String name = info.name; |
446 SourceLocationCollection subcollection = | 431 SourceLocationCollection subcollection = |
447 new SourceLocationCollection(collection); | 432 new SourceLocationCollection(collection); |
448 CodeProcessor codeProcessor = new CodeProcessor(name, subcollection); | 433 CodeProcessor codeProcessor = new CodeProcessor(name, subcollection); |
449 for (js.Node node in info.nodeMap.nodes) { | 434 for (js.Node node in info.nodeMap.nodes) { |
450 info.nodeMap[node].forEach( | 435 info.nodeMap[node] |
451 (int targetOffset, List<SourceLocation> sourceLocations) { | 436 .forEach((int targetOffset, List<SourceLocation> sourceLocations) { |
452 for (SourceLocation sourceLocation in sourceLocations) { | 437 for (SourceLocation sourceLocation in sourceLocations) { |
453 codeProcessor.addSourceLocation(targetOffset, sourceLocation); | 438 codeProcessor.addSourceLocation(targetOffset, sourceLocation); |
454 } | 439 } |
455 }); | 440 }); |
456 } | 441 } |
457 return new SourceMapHtmlInfo(info, codeProcessor, subcollection); | 442 return new SourceMapHtmlInfo(info, codeProcessor, subcollection); |
458 } | 443 } |
459 | 444 |
460 /// Outputs a HTML file in [jsMapHtmlUri] containing an interactive | 445 /// Outputs a HTML file in [jsMapHtmlUri] containing an interactive |
461 /// visualization of the source mapping information in [infoList] computed | 446 /// visualization of the source mapping information in [infoList] computed |
462 /// with the [sourceMapProcessor]. | 447 /// with the [sourceMapProcessor]. |
463 void createTraceSourceMapHtml(Uri jsMapHtmlUri, | 448 void createTraceSourceMapHtml(Uri jsMapHtmlUri, |
464 SourceMapProcessor sourceMapProcessor, | 449 SourceMapProcessor sourceMapProcessor, Iterable<SourceMapInfo> infoList) { |
465 Iterable<SourceMapInfo> infoList) { | |
466 SourceFileManager sourceFileManager = sourceMapProcessor.sourceFileManager; | 450 SourceFileManager sourceFileManager = sourceMapProcessor.sourceFileManager; |
467 SourceLocationCollection collection = new SourceLocationCollection(); | 451 SourceLocationCollection collection = new SourceLocationCollection(); |
468 List<SourceMapHtmlInfo> htmlInfoList = <SourceMapHtmlInfo>[]; | 452 List<SourceMapHtmlInfo> htmlInfoList = <SourceMapHtmlInfo>[]; |
469 for (SourceMapInfo info in infoList) { | 453 for (SourceMapInfo info in infoList) { |
470 htmlInfoList.add(createHtmlInfo(collection, info)); | 454 htmlInfoList.add(createHtmlInfo(collection, info)); |
471 } | 455 } |
472 | 456 |
473 String jsCode = computeJsHtml(htmlInfoList); | 457 String jsCode = computeJsHtml(htmlInfoList); |
474 String dartCode = computeDartHtml(sourceFileManager, htmlInfoList); | 458 String dartCode = computeDartHtml(sourceFileManager, htmlInfoList); |
475 | 459 |
476 String jsTraceHtml = computeJsTraceHtml(htmlInfoList); | 460 String jsTraceHtml = computeJsTraceHtml(htmlInfoList); |
477 outputJsDartTrace(jsMapHtmlUri, jsCode, dartCode, jsTraceHtml); | 461 outputJsDartTrace(jsMapHtmlUri, jsCode, dartCode, jsTraceHtml); |
478 print('Trace source map html generated: $jsMapHtmlUri'); | 462 print('Trace source map html generated: $jsMapHtmlUri'); |
479 } | 463 } |
480 | 464 |
481 /// Computes the HTML representation for the Dart code snippets referenced in | 465 /// Computes the HTML representation for the Dart code snippets referenced in |
482 /// [infoList]. | 466 /// [infoList]. |
483 String computeDartHtml( | 467 String computeDartHtml( |
484 SourceFileManager sourceFileManager, | 468 SourceFileManager sourceFileManager, Iterable<SourceMapHtmlInfo> infoList) { |
485 Iterable<SourceMapHtmlInfo> infoList) { | |
486 | |
487 StringBuffer dartCodeBuffer = new StringBuffer(); | 469 StringBuffer dartCodeBuffer = new StringBuffer(); |
488 for (SourceMapHtmlInfo info in infoList) { | 470 for (SourceMapHtmlInfo info in infoList) { |
489 dartCodeBuffer.write(computeDartHtmlPart(info.sourceMapInfo.name, | 471 dartCodeBuffer.write(computeDartHtmlPart(info.sourceMapInfo.name, |
490 sourceFileManager, info.sourceLocationCollection)); | 472 sourceFileManager, info.sourceLocationCollection)); |
491 } | 473 } |
492 return dartCodeBuffer.toString(); | 474 return dartCodeBuffer.toString(); |
493 | |
494 } | 475 } |
495 | 476 |
496 /// Computes the HTML representation for the Dart code snippets in [collection]. | 477 /// Computes the HTML representation for the Dart code snippets in [collection]. |
497 String computeDartHtmlPart(String name, | 478 String computeDartHtmlPart(String name, SourceFileManager sourceFileManager, |
498 SourceFileManager sourceFileManager, | 479 SourceLocationCollection collection, |
499 SourceLocationCollection collection, | 480 {bool showAsBlock: false}) { |
500 {bool showAsBlock: false}) { | |
501 const int windowSize = 3; | 481 const int windowSize = 3; |
502 StringBuffer dartCodeBuffer = new StringBuffer(); | 482 StringBuffer dartCodeBuffer = new StringBuffer(); |
503 Map<Uri, Map<int, List<SourceLocation>>> sourceLocationMap = {}; | 483 Map<Uri, Map<int, List<SourceLocation>>> sourceLocationMap = {}; |
504 collection.sourceLocations.forEach((SourceLocation sourceLocation) { | 484 collection.sourceLocations.forEach((SourceLocation sourceLocation) { |
505 Map<int, List<SourceLocation>> uriMap = | 485 Map<int, List<SourceLocation>> uriMap = |
506 sourceLocationMap.putIfAbsent(sourceLocation.sourceUri, () => {}); | 486 sourceLocationMap.putIfAbsent(sourceLocation.sourceUri, () => {}); |
507 List<SourceLocation> lineList = | 487 List<SourceLocation> lineList = |
508 uriMap.putIfAbsent(sourceLocation.line, () => []); | 488 uriMap.putIfAbsent(sourceLocation.line, () => []); |
509 lineList.add(sourceLocation); | 489 lineList.add(sourceLocation); |
510 }); | 490 }); |
511 sourceLocationMap.forEach((Uri uri, Map<int, List<SourceLocation>> uriMap) { | 491 sourceLocationMap.forEach((Uri uri, Map<int, List<SourceLocation>> uriMap) { |
512 SourceFile sourceFile = sourceFileManager.getSourceFile(uri); | 492 SourceFile sourceFile = sourceFileManager.getSourceFile(uri); |
513 if (sourceFile == null) return; | 493 if (sourceFile == null) return; |
514 StringBuffer codeBuffer = new StringBuffer(); | 494 StringBuffer codeBuffer = new StringBuffer(); |
515 | 495 |
516 int firstLineIndex; | 496 int firstLineIndex; |
517 int lastLineIndex; | 497 int lastLineIndex; |
518 List<int> lineIndices = uriMap.keys.toList()..sort(); | 498 List<int> lineIndices = uriMap.keys.toList()..sort(); |
519 int lineNoWidth; | 499 int lineNoWidth; |
520 if (lineIndices.isNotEmpty) { | 500 if (lineIndices.isNotEmpty) { |
521 lineNoWidth = '${lineIndices.last + windowSize + 1}'.length; | 501 lineNoWidth = '${lineIndices.last + windowSize + 1}'.length; |
522 } | 502 } |
523 | 503 |
524 void flush() { | 504 void flush() { |
525 if (firstLineIndex != null && lastLineIndex != null) { | 505 if (firstLineIndex != null && lastLineIndex != null) { |
526 dartCodeBuffer.write( | 506 dartCodeBuffer.write('<h4>${uri.pathSegments.last}, ' |
527 '<h4>${uri.pathSegments.last}, ' | |
528 '${firstLineIndex - windowSize + 1}-' | 507 '${firstLineIndex - windowSize + 1}-' |
529 '${lastLineIndex + windowSize + 1}' | 508 '${lastLineIndex + windowSize + 1}' |
530 '</h4>\n'); | 509 '</h4>\n'); |
531 dartCodeBuffer.write('<pre>\n'); | 510 dartCodeBuffer.write('<pre>\n'); |
532 for (int line = firstLineIndex - windowSize; | 511 for (int line = firstLineIndex - windowSize; |
533 line < firstLineIndex; | 512 line < firstLineIndex; |
534 line++) { | 513 line++) { |
535 if (line >= 0) { | 514 if (line >= 0) { |
536 dartCodeBuffer.write(lineNumber(line, width: lineNoWidth)); | 515 dartCodeBuffer.write(lineNumber(line, width: lineNoWidth)); |
537 dartCodeBuffer.write(sourceFile.getLineText(line)); | 516 dartCodeBuffer.write(sourceFile.getLineText(line)); |
538 } | 517 } |
539 } | 518 } |
540 dartCodeBuffer.write(codeBuffer); | 519 dartCodeBuffer.write(codeBuffer); |
541 for (int line = lastLineIndex + 1; | 520 for (int line = lastLineIndex + 1; |
542 line <= lastLineIndex + windowSize; | 521 line <= lastLineIndex + windowSize; |
543 line++) { | 522 line++) { |
544 if (line < sourceFile.lines) { | 523 if (line < sourceFile.lines) { |
545 dartCodeBuffer.write(lineNumber(line, width: lineNoWidth)); | 524 dartCodeBuffer.write(lineNumber(line, width: lineNoWidth)); |
546 dartCodeBuffer.write(sourceFile.getLineText(line)); | 525 dartCodeBuffer.write(sourceFile.getLineText(line)); |
547 } | 526 } |
548 } | 527 } |
549 dartCodeBuffer.write('</pre>\n'); | 528 dartCodeBuffer.write('</pre>\n'); |
550 firstLineIndex = null; | 529 firstLineIndex = null; |
551 lastLineIndex = null; | 530 lastLineIndex = null; |
552 } | 531 } |
553 codeBuffer.clear(); | 532 codeBuffer.clear(); |
554 } | 533 } |
555 | 534 |
556 lineIndices.forEach((int lineIndex) { | 535 lineIndices.forEach((int lineIndex) { |
557 List<SourceLocation> locations = uriMap[lineIndex]; | 536 List<SourceLocation> locations = uriMap[lineIndex]; |
558 if (lastLineIndex != null && | 537 if (lastLineIndex != null && lastLineIndex + windowSize * 4 < lineIndex) { |
559 lastLineIndex + windowSize * 4 < lineIndex) { | |
560 flush(); | 538 flush(); |
561 } | 539 } |
562 if (firstLineIndex == null) { | 540 if (firstLineIndex == null) { |
563 firstLineIndex = lineIndex; | 541 firstLineIndex = lineIndex; |
564 } else { | 542 } else { |
565 for (int line = lastLineIndex + 1; line < lineIndex; line++) { | 543 for (int line = lastLineIndex + 1; line < lineIndex; line++) { |
566 codeBuffer.write(lineNumber(line, width: lineNoWidth)); | 544 codeBuffer.write(lineNumber(line, width: lineNoWidth)); |
567 codeBuffer.write(sourceFile.getLineText(line)); | 545 codeBuffer.write(sourceFile.getLineText(line)); |
568 } | 546 } |
569 } | 547 } |
570 String line = sourceFile.getLineText(lineIndex); | 548 String line = sourceFile.getLineText(lineIndex); |
571 locations.sort((a, b) => a.offset.compareTo(b.offset)); | 549 locations.sort((a, b) => a.offset.compareTo(b.offset)); |
572 for (int i = 0; i < locations.length; i++) { | 550 for (int i = 0; i < locations.length; i++) { |
573 SourceLocation sourceLocation = locations[i]; | 551 SourceLocation sourceLocation = locations[i]; |
574 int index = collection.getIndex(sourceLocation); | 552 int index = collection.getIndex(sourceLocation); |
575 int start = sourceLocation.column; | 553 int start = sourceLocation.column; |
576 int end = line.length; | 554 int end = line.length; |
577 if (i + 1 < locations.length) { | 555 if (i + 1 < locations.length) { |
578 end = locations[i + 1].column; | 556 end = locations[i + 1].column; |
579 } | 557 } |
580 if (i == 0) { | 558 if (i == 0) { |
581 codeBuffer.write(lineNumber(lineIndex, width: lineNoWidth)); | 559 codeBuffer.write(lineNumber(lineIndex, width: lineNoWidth)); |
582 codeBuffer.write(line.substring(0, start)); | 560 codeBuffer.write(line.substring(0, start)); |
583 } | 561 } |
584 codeBuffer.write( | 562 codeBuffer |
585 '<a name="${index}" style="background:${toPattern(index)};" ' | 563 .write('<a name="${index}" style="background:${toPattern(index)};" ' |
586 'title="[${lineIndex + 1},${start + 1}]" ' | 564 'title="[${lineIndex + 1},${start + 1}]" ' |
587 'onmouseover="highlight(\'$index\');" ' | 565 'onmouseover="highlight(\'$index\');" ' |
588 'onmouseout="highlight();">'); | 566 'onmouseout="highlight();">'); |
589 codeBuffer.write(line.substring(start, end)); | 567 codeBuffer.write(line.substring(start, end)); |
590 codeBuffer.write('</a>'); | 568 codeBuffer.write('</a>'); |
591 } | 569 } |
592 lastLineIndex = lineIndex; | 570 lastLineIndex = lineIndex; |
593 }); | 571 }); |
594 | 572 |
595 flush(); | 573 flush(); |
596 }); | 574 }); |
597 String display = showAsBlock ? 'block' : 'none'; | 575 String display = showAsBlock ? 'block' : 'none'; |
598 return ''' | 576 return ''' |
599 <div name="$name" class="dart-buffer" style="display:$display;"> | 577 <div name="$name" class="dart-buffer" style="display:$display;"> |
600 <h3>Dart code for: ${escape(name)}</h3> | 578 <h3>Dart code for: ${escape(name)}</h3> |
601 ${dartCodeBuffer} | 579 ${dartCodeBuffer} |
602 </div>'''; | 580 </div>'''; |
603 } | 581 } |
604 | 582 |
605 /// Computes a HTML visualization of the [codePoints]. | 583 /// Computes a HTML visualization of the [codePoints]. |
606 String computeJsTraceHtmlPart(List<CodePoint> codePoints, | 584 String computeJsTraceHtmlPart( |
607 SourceLocationCollection collection) { | 585 List<CodePoint> codePoints, SourceLocationCollection collection) { |
608 StringBuffer buffer = new StringBuffer(); | 586 StringBuffer buffer = new StringBuffer(); |
609 buffer.write('<table style="width:100%;">'); | 587 buffer.write('<table style="width:100%;">'); |
610 buffer.write( | 588 buffer.write('<tr><th>Node kind</th><th>JS code @ offset</th>' |
611 '<tr><th>Node kind</th><th>JS code @ offset</th>' | |
612 '<th>Dart code @ mapped location</th><th>file:position:name</th></tr>'); | 589 '<th>Dart code @ mapped location</th><th>file:position:name</th></tr>'); |
613 codePoints.forEach((CodePoint codePoint) { | 590 codePoints.forEach((CodePoint codePoint) { |
614 String jsCode = truncate(codePoint.jsCode, 50); | 591 String jsCode = truncate(codePoint.jsCode, 50); |
615 if (codePoint.sourceLocation != null) { | 592 if (codePoint.sourceLocation != null) { |
616 int index = collection.getIndex(codePoint.sourceLocation); | 593 int index = collection.getIndex(codePoint.sourceLocation); |
617 if (index != null) { | 594 if (index != null) { |
618 String style = ''; | 595 String style = ''; |
619 if (!codePoint.isMissing) { | 596 if (!codePoint.isMissing) { |
620 style = 'style="background:${toColorCss(index)};" '; | 597 style = 'style="background:${toColorCss(index)};" '; |
621 } | 598 } |
622 buffer.write('<tr $style' | 599 buffer.write('<tr $style' |
623 'name="trace$index" ' | 600 'name="trace$index" ' |
624 'onmouseover="highlight([${index}]);"' | 601 'onmouseover="highlight([${index}]);"' |
625 'onmouseout="highlight([]);">'); | 602 'onmouseout="highlight([]);">'); |
626 } else { | 603 } else { |
627 buffer.write('<tr>'); | 604 buffer.write('<tr>'); |
628 print('${codePoint.sourceLocation} not found in '); | 605 print('${codePoint.sourceLocation} not found in '); |
629 collection.sourceLocationIndexMap.keys | 606 collection.sourceLocationIndexMap.keys |
630 .where((l) => l.sourceUri == codePoint.sourceLocation.sourceUri) | 607 .where((l) => l.sourceUri == codePoint.sourceLocation.sourceUri) |
631 .forEach((l) => print(' $l')); | 608 .forEach((l) => print(' $l')); |
632 } | 609 } |
633 } else { | 610 } else { |
634 buffer.write('<tr>'); | 611 buffer.write('<tr>'); |
635 } | 612 } |
636 buffer.write('<td>${codePoint.kind}</td>'); | 613 buffer.write('<td>${codePoint.kind}</td>'); |
637 buffer.write('<td class="code">${jsCode}</td>'); | 614 buffer.write('<td class="code">${jsCode}</td>'); |
638 if (codePoint.sourceLocation == null) { | 615 if (codePoint.sourceLocation == null) { |
639 //buffer.write('<td></td>'); | 616 //buffer.write('<td></td>'); |
640 } else { | 617 } else { |
641 String dartCode = truncate(codePoint.dartCode, 50); | 618 String dartCode = truncate(codePoint.dartCode, 50); |
642 buffer.write('<td class="code">${dartCode}</td>'); | 619 buffer.write('<td class="code">${dartCode}</td>'); |
643 buffer.write('<td>${escape(codePoint.sourceLocation.shortText)}</td>'); | 620 buffer.write('<td>${escape(codePoint.sourceLocation.shortText)}</td>'); |
644 } | 621 } |
645 buffer.write('</tr>'); | 622 buffer.write('</tr>'); |
646 }); | 623 }); |
647 buffer.write('</table>'); | 624 buffer.write('</table>'); |
648 | 625 |
649 return buffer.toString(); | 626 return buffer.toString(); |
650 } | 627 } |
OLD | NEW |