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'; |
| 11 import 'dart:math' as Math; |
11 | 12 |
12 import 'package:compiler/src/io/source_file.dart'; | 13 import 'package:compiler/src/io/source_file.dart'; |
13 import 'package:compiler/src/io/source_information.dart'; | 14 import 'package:compiler/src/io/source_information.dart'; |
14 import 'package:compiler/src/js/js.dart' as js; | 15 import 'package:compiler/src/js/js.dart' as js; |
15 | 16 |
16 import 'colors.dart'; | 17 import 'colors.dart'; |
17 import 'sourcemap_helper.dart'; | 18 import 'sourcemap_helper.dart'; |
18 import 'sourcemap_html_templates.dart'; | 19 import 'sourcemap_html_templates.dart'; |
19 | 20 |
20 /// Returns the [index]th color for visualization. | 21 /// Returns the [index]th color for visualization. |
(...skipping 12 matching lines...) Expand all Loading... |
33 | 34 |
34 /// Return the CSS color value for the [index]th span. | 35 /// Return the CSS color value for the [index]th span. |
35 String toPattern(int index) { | 36 String toPattern(int index) { |
36 /// Use gradient on spans to visually identify consecutive spans mapped to the | 37 /// Use gradient on spans to visually identify consecutive spans mapped to the |
37 /// same source location. | 38 /// same source location. |
38 HSV startColor = toColor(index); | 39 HSV startColor = toColor(index); |
39 HSV endColor = new HSV(startColor.h, startColor.s + 0.4, startColor.v - 0.2); | 40 HSV endColor = new HSV(startColor.h, startColor.s + 0.4, startColor.v - 0.2); |
40 return 'linear-gradient(to right, ${startColor.toCss}, ${endColor.toCss})'; | 41 return 'linear-gradient(to right, ${startColor.toCss}, ${endColor.toCss})'; |
41 } | 42 } |
42 | 43 |
43 /// Return the html for the [index] line number. | 44 /// Return the html for the [index] line number. If [width] is provided, shorter |
44 String lineNumber(int index) { | 45 /// line numbers will be prefixed with spaces to match the width. |
45 return '<span class="lineNumber">${index + 1} </span>'; | 46 String lineNumber(int index, [int width]) { |
| 47 String text = '${index + 1}'; |
| 48 if (width != null && text.length < width) { |
| 49 text = (' ' * (width - text.length)) + text; |
| 50 } |
| 51 return '<span class="lineNumber">$text </span>'; |
46 } | 52 } |
47 | 53 |
48 /// Return the html escaped [text]. | 54 /// Return the html escaped [text]. |
49 String escape(String text) { | 55 String escape(String text) { |
50 return const HtmlEscape().convert(text); | 56 return const HtmlEscape().convert(text); |
51 } | 57 } |
52 | 58 |
53 /// Information needed to generate HTML for a single [SourceMapInfo]. | 59 /// Information needed to generate HTML for a single [SourceMapInfo]. |
54 class SourceMapHtmlInfo { | 60 class SourceMapHtmlInfo { |
55 final SourceMapInfo sourceMapInfo; | 61 final SourceMapInfo sourceMapInfo; |
56 final CodeProcessor codeProcessor; | 62 final CodeProcessor codeProcessor; |
57 final SourceLocationCollection sourceLocationCollection; | 63 final SourceLocationCollection sourceLocationCollection; |
58 | 64 |
59 SourceMapHtmlInfo(this.sourceMapInfo, | 65 SourceMapHtmlInfo(this.sourceMapInfo, |
60 this.codeProcessor, | 66 this.codeProcessor, |
61 this.sourceLocationCollection); | 67 this.sourceLocationCollection); |
| 68 |
| 69 String toString() { |
| 70 return sourceMapInfo.toString(); |
| 71 } |
62 } | 72 } |
63 | 73 |
64 /// A collection of source locations. | 74 /// A collection of source locations. |
65 /// | 75 /// |
66 /// Used to index source locations for visualization and linking. | 76 /// Used to index source locations for visualization and linking. |
67 class SourceLocationCollection { | 77 class SourceLocationCollection { |
68 List<SourceLocation> sourceLocations = []; | 78 List<SourceLocation> sourceLocations = []; |
69 Map<SourceLocation, int> sourceLocationIndexMap; | 79 Map<SourceLocation, int> sourceLocationIndexMap; |
70 | 80 |
71 SourceLocationCollection([SourceLocationCollection parent]) | 81 SourceLocationCollection([SourceLocationCollection parent]) |
72 : sourceLocationIndexMap = | 82 : sourceLocationIndexMap = |
73 parent == null ? {} : parent.sourceLocationIndexMap; | 83 parent == null ? {} : parent.sourceLocationIndexMap; |
74 | 84 |
75 int registerSourceLocation(SourceLocation sourceLocation) { | 85 int registerSourceLocation(SourceLocation sourceLocation) { |
76 return sourceLocationIndexMap.putIfAbsent(sourceLocation, () { | 86 return sourceLocationIndexMap.putIfAbsent(sourceLocation, () { |
77 sourceLocations.add(sourceLocation); | 87 sourceLocations.add(sourceLocation); |
78 return sourceLocationIndexMap.length; | 88 return sourceLocationIndexMap.length; |
79 }); | 89 }); |
80 } | 90 } |
81 | 91 |
82 int getIndex(SourceLocation sourceLocation) { | 92 int getIndex(SourceLocation sourceLocation) { |
83 return sourceLocationIndexMap[sourceLocation]; | 93 return sourceLocationIndexMap[sourceLocation]; |
84 } | 94 } |
85 } | 95 } |
86 | 96 |
| 97 abstract class CssColorScheme { |
| 98 String singleLocationToCssColor(var id); |
| 99 |
| 100 String multiLocationToCssColor(List ids); |
| 101 |
| 102 bool get showLocationAsSpan; |
| 103 } |
| 104 |
| 105 class CustomColorScheme implements CssColorScheme { |
| 106 final bool showLocationAsSpan; |
| 107 final Function single; |
| 108 final Function multi; |
| 109 |
| 110 CustomColorScheme( |
| 111 {this.showLocationAsSpan: false, |
| 112 String this.single(var id), |
| 113 String this.multi(List ids)}); |
| 114 |
| 115 String singleLocationToCssColor(var id) => single != null ? single(id) : null; |
| 116 |
| 117 String multiLocationToCssColor(List ids) => multi != null ? multi(ids) : null; |
| 118 } |
| 119 |
| 120 class PatternCssColorScheme implements CssColorScheme { |
| 121 const PatternCssColorScheme(); |
| 122 |
| 123 bool get showLocationAsSpan => true; |
| 124 |
| 125 String singleLocationToCssColor(int index) { |
| 126 return "background:${toPattern(index)};"; |
| 127 } |
| 128 |
| 129 String multiLocationToCssColor(List<int> indices) { |
| 130 |
| 131 StringBuffer sb = new StringBuffer(); |
| 132 double delta = 100.0 / (indices.length); |
| 133 double position = 0.0; |
| 134 |
| 135 void addColor(String color) { |
| 136 sb.write(', ${color} ${position.toInt()}%'); |
| 137 position += delta; |
| 138 sb.write(', ${color} ${position.toInt()}%'); |
| 139 } |
| 140 |
| 141 for (int index in indices) { |
| 142 addColor('${toColorCss(index)}'); |
| 143 } |
| 144 return 'background: linear-gradient(to right${sb}); ' |
| 145 'background-size: 10px 10px;'; |
| 146 } |
| 147 } |
| 148 |
| 149 class SingleColorScheme implements CssColorScheme { |
| 150 const SingleColorScheme(); |
| 151 |
| 152 bool get showLocationAsSpan => false; |
| 153 |
| 154 String singleLocationToCssColor(int index) { |
| 155 return "background:${toColorCss(index)};"; |
| 156 } |
| 157 |
| 158 String multiLocationToCssColor(List<int> indices) { |
| 159 StringBuffer sb = new StringBuffer(); |
| 160 double delta = 100.0 / (indices.length); |
| 161 double position = 0.0; |
| 162 |
| 163 void addColor(String color) { |
| 164 sb.write(', ${color} ${position.toInt()}%'); |
| 165 position += delta; |
| 166 sb.write(', ${color} ${position.toInt()}%'); |
| 167 } |
| 168 |
| 169 for (int index in indices) { |
| 170 addColor('${toColorCss(index)}'); |
| 171 } |
| 172 return 'background: linear-gradient(to bottom${sb}); ' |
| 173 'background-size: 10px 3px;'; |
| 174 } |
| 175 } |
| 176 |
87 /// Processor that computes the HTML representation of a block of JavaScript | 177 /// Processor that computes the HTML representation of a block of JavaScript |
88 /// code and collects the source locations mapped in the code. | 178 /// code and collects the source locations mapped in the code. |
89 class CodeProcessor { | 179 class CodeProcessor { |
90 int lineIndex = 0; | 180 int lineIndex = 0; |
91 final String onclick; | 181 final String name; |
92 int currentJsSourceOffset = 0; | 182 int currentJsSourceOffset = 0; |
93 final SourceLocationCollection collection; | 183 final SourceLocationCollection collection; |
94 final Map<int, List<SourceLocation>> codeLocations = {}; | 184 final Map<int, List<SourceLocation>> codeLocations = {}; |
| 185 final CssColorScheme colorScheme; |
95 | 186 |
96 CodeProcessor(this.onclick, this.collection); | 187 CodeProcessor(this.name, this.collection, |
| 188 {this.colorScheme: const PatternCssColorScheme()}); |
97 | 189 |
98 void addSourceLocation(int targetOffset, SourceLocation sourceLocation) { | 190 void addSourceLocation(int targetOffset, SourceLocation sourceLocation) { |
99 codeLocations.putIfAbsent(targetOffset, () => []).add(sourceLocation); | 191 codeLocations.putIfAbsent(targetOffset, () => []).add(sourceLocation); |
100 collection.registerSourceLocation(sourceLocation); | 192 collection.registerSourceLocation(sourceLocation); |
101 } | 193 } |
102 | 194 |
103 String convertToHtml(String text) { | 195 String convertToHtml(String text) { |
104 StringBuffer htmlBuffer = new StringBuffer(); | 196 List<Annotation> annotations = <Annotation>[]; |
105 int offset = 0; | 197 codeLocations.forEach((int codeOffset, List<SourceLocation> locations) { |
106 int lineIndex = 0; | 198 for (SourceLocation location in locations) { |
107 bool pendingSourceLocationsEnd = false; | 199 if (location != null) { |
108 htmlBuffer.write(lineNumber(lineIndex)); | 200 annotations.add(new Annotation( |
109 SourceLocation currentLocation; | 201 collection.getIndex(location), |
110 | 202 codeOffset, |
111 void endCurrentLocation() { | 203 location.shortText)); |
112 if (currentLocation != null) { | 204 } |
113 htmlBuffer.write('</a>'); | 205 } |
114 } | 206 }); |
115 currentLocation = null; | 207 return convertAnnotatedCodeToHtml( |
116 } | 208 text, annotations, colorScheme: colorScheme, |
117 | 209 elementScheme: new HighlightLinkScheme(name), |
118 void addSubstring(int until) { | 210 windowSize: 3); |
119 if (until <= offset) return; | 211 } |
120 | 212 } |
121 String substring = text.substring(offset, until); | 213 |
122 offset = until; | 214 class Annotation { |
123 bool first = true; | 215 final id; |
124 for (String line in substring.split('\n')) { | 216 final int codeOffset; |
125 if (!first) { | 217 final String title; |
| 218 |
| 219 Annotation(this.id, this.codeOffset, this.title); |
| 220 } |
| 221 |
| 222 class ElementScheme { |
| 223 const ElementScheme(); |
| 224 |
| 225 String getName(var id, Set ids) => null; |
| 226 String getHref(var id, Set ids) => null; |
| 227 String onClick(var id, Set ids) => null; |
| 228 String onMouseOver(var id, Set ids) => null; |
| 229 String onMouseOut(var id, Set ids) => null; |
| 230 } |
| 231 |
| 232 class HighlightLinkScheme implements ElementScheme { |
| 233 final String name; |
| 234 |
| 235 HighlightLinkScheme(this.name); |
| 236 |
| 237 @override |
| 238 String getName(int id, Set<int> indices) { |
| 239 return 'js$id'; |
| 240 } |
| 241 |
| 242 @override |
| 243 String getHref(int id, Set<int> indices) { |
| 244 return "#${id}"; |
| 245 } |
| 246 |
| 247 @override |
| 248 String onClick(int id, Set<int> indices) { |
| 249 return "show(\'$name\');"; |
| 250 } |
| 251 |
| 252 @override |
| 253 String onMouseOut(int id, Set<int> indices) { |
| 254 String onmouseover = indices.map((i) => '\'$i\'').join(','); |
| 255 return "highlight([${onmouseover}]);"; |
| 256 } |
| 257 |
| 258 @override |
| 259 String onMouseOver(int id, Set<int> indices) { |
| 260 return "highlight([]);"; |
| 261 } |
| 262 } |
| 263 |
| 264 String convertAnnotatedCodeToHtml( |
| 265 String code, |
| 266 Iterable<Annotation> annotations, |
| 267 {CssColorScheme colorScheme: const SingleColorScheme(), |
| 268 ElementScheme elementScheme: const ElementScheme(), |
| 269 int windowSize}) { |
| 270 StringBuffer htmlBuffer = new StringBuffer(); |
| 271 List<CodeLine> lines = convertAnnotatedCodeToCodeLines( |
| 272 code, annotations, |
| 273 colorScheme: colorScheme, |
| 274 elementScheme: elementScheme, |
| 275 windowSize: windowSize); |
| 276 int lineNoWidth; |
| 277 if (lines.isNotEmpty) { |
| 278 lineNoWidth = '${lines.last.lineNo + 1}'.length; |
| 279 } |
| 280 for (CodeLine line in lines) { |
| 281 line.printHtmlOn(htmlBuffer, lineNoWidth); |
| 282 } |
| 283 return htmlBuffer.toString(); |
| 284 } |
| 285 |
| 286 List<CodeLine> convertAnnotatedCodeToCodeLines( |
| 287 String code, |
| 288 Iterable<Annotation> annotations, |
| 289 {CssColorScheme colorScheme: const SingleColorScheme(), |
| 290 ElementScheme elementScheme: const ElementScheme(), |
| 291 int windowSize}) { |
| 292 |
| 293 List<CodeLine> lines = <CodeLine>[]; |
| 294 CodeLine currentLine; |
| 295 int offset = 0; |
| 296 int lineIndex = 0; |
| 297 int firstLine; |
| 298 int lastLine; |
| 299 bool pendingSourceLocationsEnd = false; |
| 300 |
| 301 void write(String code, String html) { |
| 302 if (currentLine != null) { |
| 303 currentLine.codeBuffer.write(code); |
| 304 currentLine.htmlParts.add(html); |
| 305 } |
| 306 } |
| 307 |
| 308 void startLine() { |
| 309 lines.add(currentLine = new CodeLine(lines.length)); |
| 310 } |
| 311 |
| 312 void endCurrentLocation() { |
| 313 if (pendingSourceLocationsEnd) { |
| 314 write('', '</a>'); |
| 315 } |
| 316 pendingSourceLocationsEnd = false; |
| 317 } |
| 318 |
| 319 void addSubstring(int until, {bool isFirst: false, bool isLast: false}) { |
| 320 if (until <= offset) return; |
| 321 if (offset >= code.length) return; |
| 322 |
| 323 String substring = code.substring(offset, until); |
| 324 offset = until; |
| 325 bool first = true; |
| 326 |
| 327 if (isLast) { |
| 328 lastLine = lineIndex; |
| 329 } |
| 330 if (isFirst) { |
| 331 startLine(); |
| 332 } |
| 333 for (String line in substring.split('\n')) { |
| 334 if (!first) { |
| 335 endCurrentLocation(); |
| 336 write('', '\n'); |
| 337 lineIndex++; |
| 338 startLine(); |
| 339 } |
| 340 if (pendingSourceLocationsEnd && !colorScheme.showLocationAsSpan) { |
| 341 if (line.isNotEmpty) { |
| 342 String before = line.substring(0, 1); |
| 343 write(before, escape(before)); |
126 endCurrentLocation(); | 344 endCurrentLocation(); |
127 htmlBuffer.write('\n'); | 345 String after = line.substring(1); |
128 lineIndex++; | 346 write(after, escape(after)); |
129 htmlBuffer.write(lineNumber(lineIndex)); | |
130 } | |
131 htmlBuffer.write(escape(line)); | |
132 first = false; | |
133 } | |
134 } | |
135 | |
136 void insertSourceLocations(List<SourceLocation> lastSourceLocations) { | |
137 endCurrentLocation(); | |
138 | |
139 String color; | |
140 int index; | |
141 String title; | |
142 if (lastSourceLocations.length == 1) { | |
143 SourceLocation sourceLocation = lastSourceLocations.single; | |
144 if (sourceLocation != null) { | |
145 index = collection.getIndex(sourceLocation); | |
146 color = "background:${toPattern(index)};"; | |
147 title = sourceLocation.shortText; | |
148 currentLocation = sourceLocation; | |
149 } | 347 } |
150 } else { | 348 } else { |
151 | 349 write(line, escape(line)); |
152 index = collection.getIndex(lastSourceLocations.first); | 350 } |
153 StringBuffer sb = new StringBuffer(); | 351 first = false; |
154 double delta = 100.0 / (lastSourceLocations.length); | 352 } |
155 double position = 0.0; | 353 if (isFirst) { |
156 | 354 firstLine = lineIndex; |
157 void addColor(String color) { | 355 } |
158 sb.write(', ${color} ${position.toInt()}%'); | 356 } |
159 position += delta; | 357 |
160 sb.write(', ${color} ${position.toInt()}%'); | 358 void insertAnnotations(List<Annotation> annotations) { |
161 } | |
162 | |
163 for (SourceLocation sourceLocation in lastSourceLocations) { | |
164 if (sourceLocation == null) continue; | |
165 int colorIndex = collection.getIndex(sourceLocation); | |
166 addColor('${toColorCss(colorIndex)}'); | |
167 currentLocation = sourceLocation; | |
168 } | |
169 color = 'background: linear-gradient(to right${sb}); ' | |
170 'background-size: 10px 10px;'; | |
171 title = lastSourceLocations.map((l) => l.shortText).join(','); | |
172 } | |
173 if (index != null) { | |
174 Set<int> indices = | |
175 lastSourceLocations.map((l) => collection.getIndex(l)).toSet(); | |
176 String onmouseover = indices.map((i) => '\'$i\'').join(','); | |
177 htmlBuffer.write( | |
178 '<a name="js$index" href="#${index}" style="$color" title="$title" ' | |
179 'onclick="${onclick}" onmouseover="highlight([${onmouseover}]);"' | |
180 'onmouseout="highlight([]);">'); | |
181 pendingSourceLocationsEnd = true; | |
182 } | |
183 if (lastSourceLocations.last == null) { | |
184 endCurrentLocation(); | |
185 } | |
186 } | |
187 | |
188 for (int targetOffset in codeLocations.keys.toList()..sort()) { | |
189 List<SourceLocation> sourceLocations = codeLocations[targetOffset]; | |
190 addSubstring(targetOffset); | |
191 insertSourceLocations(sourceLocations); | |
192 } | |
193 | |
194 addSubstring(text.length); | |
195 endCurrentLocation(); | 359 endCurrentLocation(); |
196 return htmlBuffer.toString(); | 360 |
197 } | 361 String color; |
198 } | 362 var id; |
| 363 String title; |
| 364 if (annotations.length == 1) { |
| 365 Annotation annotation = annotations.single; |
| 366 if (annotation != null) { |
| 367 id = annotation.id; |
| 368 color = colorScheme.singleLocationToCssColor(id); |
| 369 title = annotation.title; |
| 370 } |
| 371 } else { |
| 372 id = annotations.first.id; |
| 373 List ids = []; |
| 374 for (Annotation annotation in annotations) { |
| 375 ids.add(annotation.id); |
| 376 } |
| 377 color = colorScheme.multiLocationToCssColor(ids); |
| 378 title = annotations.map((l) => l.title).join(','); |
| 379 } |
| 380 if (id != null) { |
| 381 Set ids = annotations.map((l) => l.id).toSet(); |
| 382 String name = elementScheme.getName(id, ids); |
| 383 String href = elementScheme.getHref(id, ids); |
| 384 String onclick = elementScheme.onClick(id, ids); |
| 385 String onmouseover = elementScheme.onMouseOver(id, ids); |
| 386 String onmouseout = elementScheme.onMouseOut(id, ids); |
| 387 write('', '<a'); |
| 388 if (href != null) { |
| 389 write('', ' href="${href}"'); |
| 390 } |
| 391 if (name != null) { |
| 392 write('', ' name="${name}"'); |
| 393 } |
| 394 if (title != null) { |
| 395 write('', ' title="${escape(title)}"'); |
| 396 } |
| 397 write('', ' style="${color}"'); |
| 398 if (onclick != null) { |
| 399 write('', ' onclick="${onclick}"'); |
| 400 } |
| 401 if (onmouseover != null) { |
| 402 write('', ' onmouseover="${onmouseover}"'); |
| 403 } |
| 404 if (onmouseout != null) { |
| 405 write('', ' onmouseout="${onmouseout}"'); |
| 406 } |
| 407 write('', '>'); |
| 408 pendingSourceLocationsEnd = true; |
| 409 } |
| 410 if (annotations.last == null) { |
| 411 endCurrentLocation(); |
| 412 } |
| 413 } |
| 414 |
| 415 Map<int, List<Annotation>> annotationMap = <int, List<Annotation>>{}; |
| 416 for (Annotation annotation in annotations) { |
| 417 annotationMap.putIfAbsent(annotation.codeOffset, () => <Annotation>[]) |
| 418 .add(annotation); |
| 419 } |
| 420 |
| 421 bool first = true; |
| 422 for (int codeOffset in annotationMap.keys.toList()..sort()) { |
| 423 List<Annotation> annotationList = annotationMap[codeOffset]; |
| 424 addSubstring(codeOffset, isFirst: first); |
| 425 insertAnnotations(annotationList); |
| 426 first = false; |
| 427 } |
| 428 |
| 429 addSubstring(code.length, isFirst: first, isLast: true); |
| 430 endCurrentLocation(); |
| 431 |
| 432 int start = 0; |
| 433 int end = lines.length - 1; |
| 434 if (windowSize != null) { |
| 435 start = Math.max(firstLine - windowSize, start); |
| 436 end = Math.min(lastLine + windowSize, end); |
| 437 } |
| 438 return lines.sublist(start, end); |
| 439 } |
| 440 |
| 441 class CodeLine { |
| 442 final int lineNo; |
| 443 final StringBuffer codeBuffer = new StringBuffer(); |
| 444 final List<String> htmlParts = <String>[]; |
| 445 String _code; |
| 446 |
| 447 CodeLine(this.lineNo); |
| 448 |
| 449 String get code { |
| 450 if (_code == null) { |
| 451 _code = codeBuffer.toString(); |
| 452 } |
| 453 return _code; |
| 454 } |
| 455 |
| 456 void printHtmlOn(StringBuffer htmlBuffer, [int lineNoWidth]) { |
| 457 htmlBuffer.write(lineNumber(lineNo, lineNoWidth)); |
| 458 for (String part in htmlParts) { |
| 459 htmlBuffer.write(part); |
| 460 } |
| 461 } |
| 462 } |
| 463 |
199 | 464 |
200 /// Computes the HTML representation for a collection of JavaScript code blocks. | 465 /// Computes the HTML representation for a collection of JavaScript code blocks. |
201 String computeJsHtml(Iterable<SourceMapHtmlInfo> infoList) { | 466 String computeJsHtml(Iterable<SourceMapHtmlInfo> infoList) { |
202 | 467 |
203 StringBuffer jsCodeBuffer = new StringBuffer(); | 468 StringBuffer jsCodeBuffer = new StringBuffer(); |
204 for (SourceMapHtmlInfo info in infoList) { | 469 for (SourceMapHtmlInfo info in infoList) { |
205 String name = info.sourceMapInfo.name; | 470 String name = info.sourceMapInfo.name; |
206 String html = info.codeProcessor.convertToHtml(info.sourceMapInfo.code); | 471 String html = info.codeProcessor.convertToHtml(info.sourceMapInfo.code); |
207 String onclick = 'show(\'$name\');'; | 472 String onclick = 'show(\'$name\');'; |
208 jsCodeBuffer.write( | 473 jsCodeBuffer.write( |
(...skipping 24 matching lines...) Expand all Loading... |
233 } | 498 } |
234 return jsTraceBuffer.toString(); | 499 return jsTraceBuffer.toString(); |
235 } | 500 } |
236 | 501 |
237 /// Computes the HTML information for the [info]. | 502 /// Computes the HTML information for the [info]. |
238 SourceMapHtmlInfo createHtmlInfo(SourceLocationCollection collection, | 503 SourceMapHtmlInfo createHtmlInfo(SourceLocationCollection collection, |
239 SourceMapInfo info) { | 504 SourceMapInfo info) { |
240 js.Node node = info.node; | 505 js.Node node = info.node; |
241 String code = info.code; | 506 String code = info.code; |
242 String name = info.name; | 507 String name = info.name; |
243 String onclick = 'show(\'$name\');'; | |
244 SourceLocationCollection subcollection = | 508 SourceLocationCollection subcollection = |
245 new SourceLocationCollection(collection); | 509 new SourceLocationCollection(collection); |
246 CodeProcessor codeProcessor = new CodeProcessor(onclick, subcollection); | 510 CodeProcessor codeProcessor = new CodeProcessor(name, subcollection); |
| 511 //print('${info.element}:${info.nodeMap.nodes.length}'); |
247 for (js.Node node in info.nodeMap.nodes) { | 512 for (js.Node node in info.nodeMap.nodes) { |
248 info.nodeMap[node].forEach( | 513 info.nodeMap[node].forEach( |
249 (int targetOffset, List<SourceLocation> sourceLocations) { | 514 (int targetOffset, List<SourceLocation> sourceLocations) { |
250 for (SourceLocation sourceLocation in sourceLocations) { | 515 for (SourceLocation sourceLocation in sourceLocations) { |
251 codeProcessor.addSourceLocation(targetOffset, sourceLocation); | 516 codeProcessor.addSourceLocation(targetOffset, sourceLocation); |
252 } | 517 } |
253 }); | 518 }); |
254 } | 519 } |
255 return new SourceMapHtmlInfo(info, codeProcessor, subcollection); | 520 return new SourceMapHtmlInfo(info, codeProcessor, subcollection); |
256 } | 521 } |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
305 List<SourceLocation> lineList = | 570 List<SourceLocation> lineList = |
306 uriMap.putIfAbsent(sourceLocation.line, () => []); | 571 uriMap.putIfAbsent(sourceLocation.line, () => []); |
307 lineList.add(sourceLocation); | 572 lineList.add(sourceLocation); |
308 }); | 573 }); |
309 sourceLocationMap.forEach((Uri uri, Map<int, List<SourceLocation>> uriMap) { | 574 sourceLocationMap.forEach((Uri uri, Map<int, List<SourceLocation>> uriMap) { |
310 SourceFile sourceFile = sourceFileManager.getSourceFile(uri); | 575 SourceFile sourceFile = sourceFileManager.getSourceFile(uri); |
311 StringBuffer codeBuffer = new StringBuffer(); | 576 StringBuffer codeBuffer = new StringBuffer(); |
312 | 577 |
313 int firstLineIndex; | 578 int firstLineIndex; |
314 int lastLineIndex; | 579 int lastLineIndex; |
| 580 List<int> lineIndices = uriMap.keys.toList()..sort(); |
| 581 int lineNoWidth; |
| 582 if (lineIndices.isNotEmpty) { |
| 583 lineNoWidth = '${lineIndices.last + windowSize + 1}'.length; |
| 584 } |
315 | 585 |
316 void flush() { | 586 void flush() { |
317 if (firstLineIndex != null && lastLineIndex != null) { | 587 if (firstLineIndex != null && lastLineIndex != null) { |
318 dartCodeBuffer.write( | 588 dartCodeBuffer.write( |
319 '<h4>${uri.pathSegments.last}, ' | 589 '<h4>${uri.pathSegments.last}, ' |
320 '${firstLineIndex - windowSize + 1}-' | 590 '${firstLineIndex - windowSize + 1}-' |
321 '${lastLineIndex + windowSize + 1}' | 591 '${lastLineIndex + windowSize + 1}' |
322 '</h4>\n'); | 592 '</h4>\n'); |
323 dartCodeBuffer.write('<pre>\n'); | 593 dartCodeBuffer.write('<pre>\n'); |
324 for (int line = firstLineIndex - windowSize; | 594 for (int line = firstLineIndex - windowSize; |
325 line < firstLineIndex; | 595 line < firstLineIndex; |
326 line++) { | 596 line++) { |
327 if (line >= 0) { | 597 if (line >= 0) { |
328 dartCodeBuffer.write(lineNumber(line)); | 598 dartCodeBuffer.write(lineNumber(line, lineNoWidth)); |
329 dartCodeBuffer.write(sourceFile.getLineText(line)); | 599 dartCodeBuffer.write(sourceFile.getLineText(line)); |
330 } | 600 } |
331 } | 601 } |
332 dartCodeBuffer.write(codeBuffer); | 602 dartCodeBuffer.write(codeBuffer); |
333 for (int line = lastLineIndex + 1; | 603 for (int line = lastLineIndex + 1; |
334 line <= lastLineIndex + windowSize; | 604 line <= lastLineIndex + windowSize; |
335 line++) { | 605 line++) { |
336 if (line < sourceFile.lines) { | 606 if (line < sourceFile.lines) { |
337 dartCodeBuffer.write(lineNumber(line)); | 607 dartCodeBuffer.write(lineNumber(line, lineNoWidth)); |
338 dartCodeBuffer.write(sourceFile.getLineText(line)); | 608 dartCodeBuffer.write(sourceFile.getLineText(line)); |
339 } | 609 } |
340 } | 610 } |
341 dartCodeBuffer.write('</pre>\n'); | 611 dartCodeBuffer.write('</pre>\n'); |
342 firstLineIndex = null; | 612 firstLineIndex = null; |
343 lastLineIndex = null; | 613 lastLineIndex = null; |
344 } | 614 } |
345 codeBuffer.clear(); | 615 codeBuffer.clear(); |
346 } | 616 } |
347 | 617 |
348 List<int> lineIndices = uriMap.keys.toList()..sort(); | |
349 lineIndices.forEach((int lineIndex) { | 618 lineIndices.forEach((int lineIndex) { |
350 List<SourceLocation> locations = uriMap[lineIndex]; | 619 List<SourceLocation> locations = uriMap[lineIndex]; |
351 if (lastLineIndex != null && | 620 if (lastLineIndex != null && |
352 lastLineIndex + windowSize * 4 < lineIndex) { | 621 lastLineIndex + windowSize * 4 < lineIndex) { |
353 flush(); | 622 flush(); |
354 } | 623 } |
355 if (firstLineIndex == null) { | 624 if (firstLineIndex == null) { |
356 firstLineIndex = lineIndex; | 625 firstLineIndex = lineIndex; |
357 } else { | 626 } else { |
358 for (int line = lastLineIndex + 1; line < lineIndex; line++) { | 627 for (int line = lastLineIndex + 1; line < lineIndex; line++) { |
359 codeBuffer.write(lineNumber(line)); | 628 codeBuffer.write(lineNumber(line, lineNoWidth)); |
360 codeBuffer.write(sourceFile.getLineText(line)); | 629 codeBuffer.write(sourceFile.getLineText(line)); |
361 } | 630 } |
362 } | 631 } |
363 String line = sourceFile.getLineText(lineIndex); | 632 String line = sourceFile.getLineText(lineIndex); |
364 locations.sort((a, b) => a.offset.compareTo(b.offset)); | 633 locations.sort((a, b) => a.offset.compareTo(b.offset)); |
365 for (int i = 0; i < locations.length; i++) { | 634 for (int i = 0; i < locations.length; i++) { |
366 SourceLocation sourceLocation = locations[i]; | 635 SourceLocation sourceLocation = locations[i]; |
367 int index = collection.getIndex(sourceLocation); | 636 int index = collection.getIndex(sourceLocation); |
368 int start = sourceLocation.column; | 637 int start = sourceLocation.column; |
369 int end = line.length; | 638 int end = line.length; |
370 if (i + 1 < locations.length) { | 639 if (i + 1 < locations.length) { |
371 end = locations[i + 1].column; | 640 end = locations[i + 1].column; |
372 } | 641 } |
373 if (i == 0) { | 642 if (i == 0) { |
374 codeBuffer.write(lineNumber(lineIndex)); | 643 codeBuffer.write(lineNumber(lineIndex, lineNoWidth)); |
375 codeBuffer.write(line.substring(0, start)); | 644 codeBuffer.write(line.substring(0, start)); |
376 } | 645 } |
377 codeBuffer.write( | 646 codeBuffer.write( |
378 '<a name="${index}" style="background:${toPattern(index)};" ' | 647 '<a name="${index}" style="background:${toPattern(index)};" ' |
379 'title="[${lineIndex + 1},${start + 1}]" ' | 648 'title="[${lineIndex + 1},${start + 1}]" ' |
380 'onmouseover="highlight(\'$index\');" ' | 649 'onmouseover="highlight(\'$index\');" ' |
381 'onmouseout="highlight();">'); | 650 'onmouseout="highlight();">'); |
382 codeBuffer.write(line.substring(start, end)); | 651 codeBuffer.write(line.substring(start, end)); |
383 codeBuffer.write('</a>'); | 652 codeBuffer.write('</a>'); |
384 } | 653 } |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
433 } else { | 702 } else { |
434 buffer.write('<td class="code">${codePoint.dartCode}</td>'); | 703 buffer.write('<td class="code">${codePoint.dartCode}</td>'); |
435 buffer.write('<td>${escape(codePoint.sourceLocation.shortText)}</td>'); | 704 buffer.write('<td>${escape(codePoint.sourceLocation.shortText)}</td>'); |
436 } | 705 } |
437 buffer.write('</tr>'); | 706 buffer.write('</tr>'); |
438 }); | 707 }); |
439 buffer.write('</table>'); | 708 buffer.write('</table>'); |
440 | 709 |
441 return buffer.toString(); | 710 return buffer.toString(); |
442 } | 711 } |
OLD | NEW |