OLD | NEW |
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, 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 library sourcemap.diff; | 5 library sourcemap.diff; |
6 | 6 |
7 import 'package:compiler/src/io/source_file.dart'; | 7 import 'package:compiler/src/io/source_file.dart'; |
8 | 8 |
9 import 'html_parts.dart'; | 9 import 'html_parts.dart'; |
10 import 'output_structure.dart'; | 10 import 'output_structure.dart'; |
11 import 'sourcemap_helper.dart'; | 11 import 'sourcemap_helper.dart'; |
| 12 import 'sourcemap_html_helper.dart'; |
12 | 13 |
13 enum DiffKind { | 14 enum DiffKind { |
14 UNMATCHED, | 15 UNMATCHED, |
15 MATCHING, | 16 MATCHING, |
16 IDENTICAL, | 17 IDENTICAL, |
17 } | 18 } |
18 | 19 |
| 20 /// Id for an output column. |
| 21 class DiffColumn { |
| 22 final String type; |
| 23 final int index; |
| 24 |
| 25 const DiffColumn(this.type, [this.index]); |
| 26 |
| 27 int get hashCode => type.hashCode * 19 + index.hashCode * 23; |
| 28 |
| 29 bool operator ==(other) { |
| 30 if (identical(this, other)) return true; |
| 31 if (other is! DiffColumn) return false; |
| 32 return type == other.type && index == other.index; |
| 33 } |
| 34 |
| 35 String toString() => '$type${index != null ? index : ''}'; |
| 36 } |
| 37 |
| 38 /// A block of code in an output column. |
| 39 abstract class DiffColumnBlock { |
| 40 void printHtmlOn(StringBuffer htmlBuffer, HtmlPrintContext context); |
| 41 } |
| 42 |
| 43 /// A block consisting of pure HTML parts. |
| 44 class PartsColumnBlock extends DiffColumnBlock { |
| 45 final List<HtmlPart> parts; |
| 46 |
| 47 PartsColumnBlock(this.parts); |
| 48 |
| 49 void printHtmlOn(StringBuffer htmlBuffer, HtmlPrintContext context) { |
| 50 if (parts.isNotEmpty) { |
| 51 for (HtmlPart part in parts) { |
| 52 part.printHtmlOn(htmlBuffer, context); |
| 53 } |
| 54 } |
| 55 } |
| 56 } |
| 57 |
| 58 /// A block consisting of line-per-line JavaScript and source mapped Dart code. |
| 59 class CodeLinesColumnBlock extends DiffColumnBlock { |
| 60 final List<CodeLine> jsCodeLines; |
| 61 final Map<CodeLine, List<CodeLine>> jsToDartMap; |
| 62 |
| 63 CodeLinesColumnBlock(this.jsCodeLines, this.jsToDartMap); |
| 64 |
| 65 void printHtmlOn(StringBuffer htmlBuffer, HtmlPrintContext context) { |
| 66 if (jsCodeLines.isNotEmpty) { |
| 67 htmlBuffer.write('<table style="width:100%">'); |
| 68 for (CodeLine codeLine in jsCodeLines) { |
| 69 htmlBuffer.write('<tr><td class="${ClassNames.innerCell}">'); |
| 70 codeLine.printHtmlOn(htmlBuffer, context); |
| 71 htmlBuffer.write( |
| 72 '</td><td ' |
| 73 'class="${ClassNames.innerCell} ${ClassNames.sourceMapped}">'); |
| 74 List<CodeLine> lines = jsToDartMap[codeLine]; |
| 75 if (lines != null) { |
| 76 for (CodeLine line in lines) { |
| 77 line.printHtmlOn(htmlBuffer, |
| 78 context.from(includeAnnotation: (a) { |
| 79 CodeLineAnnotation annotation = a.data; |
| 80 return annotation.annotationType.isSourceMapped; |
| 81 })); |
| 82 } |
| 83 } |
| 84 htmlBuffer.write('</td></tr>'); |
| 85 } |
| 86 htmlBuffer.write('</table>'); |
| 87 } |
| 88 } |
| 89 } |
| 90 |
19 /// A list of columns that should align in output. | 91 /// A list of columns that should align in output. |
20 class DiffBlock { | 92 class DiffBlock { |
21 final DiffKind kind; | 93 final DiffKind kind; |
22 List<List<HtmlPart>> columns = <List<HtmlPart>>[]; | 94 Map<DiffColumn, DiffColumnBlock> _columns = <DiffColumn, DiffColumnBlock>{}; |
23 | 95 |
24 DiffBlock(this.kind); | 96 DiffBlock(this.kind); |
25 | 97 |
26 void addColumn(int index, List<HtmlPart> lines) { | 98 void addColumnBlock(DiffColumn column, DiffColumnBlock block) { |
27 if (index >= columns.length) { | 99 _columns[column] = block; |
28 columns.length = index + 1; | |
29 } | |
30 columns[index] = lines; | |
31 } | 100 } |
32 | 101 |
33 List<HtmlPart> getColumn(int index) { | 102 Iterable<DiffColumn> get columns => _columns.keys; |
34 List<HtmlPart> lines; | 103 |
35 if (index < columns.length) { | 104 void printHtmlOn(DiffColumn column, |
36 lines = columns[index]; | 105 StringBuffer htmlBuffer, |
| 106 HtmlPrintContext context) { |
| 107 DiffColumnBlock block = _columns[column]; |
| 108 if (block != null) { |
| 109 block.printHtmlOn(htmlBuffer, context); |
37 } | 110 } |
38 return lines != null ? lines : const <HtmlPart>[]; | |
39 } | 111 } |
40 } | 112 } |
41 | 113 |
42 | 114 |
43 /// Align the content of [list1] and [list2]. | 115 /// Align the content of [list1] and [list2]. |
44 /// | 116 /// |
45 /// If provided, [range1] and [range2] aligned the subranges of [list1] and | 117 /// If provided, [range1] and [range2] aligned the subranges of [list1] and |
46 /// [list2], otherwise the whole lists are aligned. | 118 /// [list2], otherwise the whole lists are aligned. |
47 /// | 119 /// |
48 /// If provided, [match] determines the equality between members of [list1] and | 120 /// If provided, [match] determines the equality between members of [list1] and |
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
169 List<List<CodeLine>> inputLines; | 241 List<List<CodeLine>> inputLines; |
170 | 242 |
171 List<int> nextInputLine = [0, 0]; | 243 List<int> nextInputLine = [0, 0]; |
172 | 244 |
173 List<DiffBlock> blocks = <DiffBlock>[]; | 245 List<DiffBlock> blocks = <DiffBlock>[]; |
174 | 246 |
175 DiffCreator(List<OutputStructure> structures, this.sourceFileManager) | 247 DiffCreator(List<OutputStructure> structures, this.sourceFileManager) |
176 : this.structures = structures, | 248 : this.structures = structures, |
177 this.inputLines = structures.map((s) => s.lines).toList(); | 249 this.inputLines = structures.map((s) => s.lines).toList(); |
178 | 250 |
179 CodeSource codeSourceFromEntities(Iterable<OutputEntity> entities) { | 251 /// Compute [CodeSource]s defined by [entities]. |
| 252 Iterable<CodeSource> codeSourceFromEntities(Iterable<OutputEntity> entities) { |
| 253 Set<CodeSource> sources = new Set<CodeSource>(); |
180 for (OutputEntity entity in entities) { | 254 for (OutputEntity entity in entities) { |
181 if (entity.codeSource != null) { | 255 if (entity.codeSource != null) { |
182 return entity.codeSource; | 256 sources.add(entity.codeSource); |
183 } | 257 } |
184 } | 258 } |
185 return null; | 259 return sources; |
| 260 } |
| 261 |
| 262 /// Create a block with the code from [codeSources]. The [CodeSource]s in |
| 263 /// [mainSources] are tagged as original code sources, the rest as inlined |
| 264 /// code sources. |
| 265 DiffColumnBlock codeLinesFromCodeSources( |
| 266 Iterable<CodeSource> mainSources, |
| 267 Iterable<CodeSource> codeSources) { |
| 268 List<HtmlPart> parts = <HtmlPart>[]; |
| 269 for (CodeSource codeSource in codeSources) { |
| 270 //parts.addAll(codeLinesFromCodeSource(codeSource)); |
| 271 String className = |
| 272 mainSources.contains(codeSource) |
| 273 ? ClassNames.originalDart : ClassNames.inlinedDart; |
| 274 parts.add( |
| 275 new TagPart('div', |
| 276 properties: {'class': className}, |
| 277 content: codeLinesFromCodeSource(codeSource))); |
| 278 } |
| 279 return new PartsColumnBlock(parts); |
| 280 } |
| 281 |
| 282 /// Adds all [CodeSource]s used in [dartCodeLines] to [codeSourceSet]. |
| 283 void collectCodeSources(Set<CodeSource> codeSourceSet, |
| 284 Map<CodeLine, List<CodeLine>> dartCodeLines) { |
| 285 for (List<CodeLine> codeLines in dartCodeLines.values) { |
| 286 for (CodeLine dartCodeLine in codeLines) { |
| 287 if (dartCodeLine.lineAnnotation != null) { |
| 288 codeSourceSet.add(dartCodeLine.lineAnnotation); |
| 289 } |
| 290 } |
| 291 } |
186 } | 292 } |
187 | 293 |
188 /// Checks that lines are added in sequence without gaps or duplicates. | 294 /// Checks that lines are added in sequence without gaps or duplicates. |
189 void checkLineInvariant(int index, Interval range) { | 295 void checkLineInvariant(int index, Interval range) { |
190 int expectedLineNo = nextInputLine[index]; | 296 int expectedLineNo = nextInputLine[index]; |
191 if (range.from != expectedLineNo) { | 297 if (range.from != expectedLineNo) { |
192 print('Expected line no $expectedLineNo, found ${range.from}'); | 298 print('Expected line no $expectedLineNo, found ${range.from}'); |
193 if (range.from < expectedLineNo) { | 299 if (range.from < expectedLineNo) { |
194 print('Duplicate lines:'); | 300 print('Duplicate lines:'); |
195 int i = range.from; | 301 int i = range.from; |
196 while (i <= expectedLineNo) { | 302 while (i <= expectedLineNo) { |
197 print(inputLines[index][i++].code); | 303 print(inputLines[index][i++].code); |
198 } | 304 } |
199 } else { | 305 } else { |
200 print('Missing lines:'); | 306 print('Missing lines:'); |
201 int i = expectedLineNo; | 307 int i = expectedLineNo; |
202 while (i <= range.from) { | 308 while (i <= range.from) { |
203 print(inputLines[index][i++].code); | 309 print(inputLines[index][i++].code); |
204 } | 310 } |
205 } | 311 } |
206 } | 312 } |
207 nextInputLine[index] = range.to; | 313 nextInputLine[index] = range.to; |
208 } | 314 } |
209 | 315 |
210 /// Creates a block containing the code lines in [range] from input number | 316 /// Creates a block containing the code lines in [range] from input number |
211 /// [index]. If [codeSource] is provided, the block will contain a | 317 /// [index]. If [codeSource] is provided, the block will contain a |
212 /// corresponding Dart code column. | 318 /// corresponding Dart code column. |
213 void handleSkew(int index, Interval range, [CodeSource codeSource]) { | 319 void handleSkew( |
| 320 int index, |
| 321 Interval range, |
| 322 [Iterable<CodeSource> mainCodeSources = const <CodeSource>[]]) { |
| 323 if (range.isEmpty) return; |
| 324 |
| 325 Set<CodeSource> codeSources = new Set<CodeSource>(); |
| 326 codeSources.addAll(mainCodeSources); |
| 327 |
214 DiffBlock block = new DiffBlock(DiffKind.UNMATCHED); | 328 DiffBlock block = new DiffBlock(DiffKind.UNMATCHED); |
215 checkLineInvariant(index, range); | 329 checkLineInvariant(index, range); |
216 block.addColumn(index, inputLines[index].sublist(range.from, range.to)); | 330 List<CodeLine> jsCodeLines = |
217 if (codeSource != null) { | 331 inputLines[index].sublist(range.from, range.to); |
218 block.addColumn(2, | 332 Map<CodeLine, List<CodeLine>> dartCodeLines = |
219 codeLinesFromCodeSource(sourceFileManager, codeSource)); | 333 dartCodeLinesFromJsCodeLines(jsCodeLines); |
| 334 block.addColumnBlock( |
| 335 new DiffColumn('js', index), |
| 336 new CodeLinesColumnBlock(jsCodeLines, dartCodeLines)); |
| 337 collectCodeSources(codeSources, dartCodeLines); |
| 338 |
| 339 if (codeSources.isNotEmpty) { |
| 340 block.addColumnBlock( |
| 341 const DiffColumn('dart'), |
| 342 codeLinesFromCodeSources(mainCodeSources, codeSources)); |
220 } | 343 } |
221 blocks.add(block); | 344 blocks.add(block); |
222 } | 345 } |
223 | 346 |
224 /// Create a block containing the code lines in [ranges] from the | 347 /// Create a block containing the code lines in [ranges] from the |
225 /// corresponding JavaScript inputs. If [codeSource] is provided, the block | 348 /// corresponding JavaScript inputs. If [codeSource] is provided, the block |
226 /// will contain a corresponding Dart code column. | 349 /// will contain a corresponding Dart code column. |
227 void addLines(DiffKind kind, List<Interval> ranges, [CodeSource codeSource]) { | 350 void addLines( |
| 351 DiffKind kind, |
| 352 List<Interval> ranges, |
| 353 [Iterable<CodeSource> mainCodeSources = const <CodeSource>[]]) { |
| 354 if (ranges.every((range) => range.isEmpty)) return; |
| 355 |
| 356 Set<CodeSource> codeSources = new Set<CodeSource>(); |
| 357 codeSources.addAll(mainCodeSources); |
| 358 |
228 DiffBlock block = new DiffBlock(kind); | 359 DiffBlock block = new DiffBlock(kind); |
229 for (int i = 0; i < ranges.length; i++) { | 360 for (int i = 0; i < ranges.length; i++) { |
230 checkLineInvariant(i, ranges[i]); | 361 checkLineInvariant(i, ranges[i]); |
231 block.addColumn(i, inputLines[i].sublist(ranges[i].from, ranges[i].to)); | 362 List<CodeLine> jsCodeLines = |
| 363 inputLines[i].sublist(ranges[i].from, ranges[i].to); |
| 364 Map<CodeLine, List<CodeLine>> dartCodeLines = |
| 365 dartCodeLinesFromJsCodeLines(jsCodeLines); |
| 366 block.addColumnBlock( |
| 367 new DiffColumn('js', i), |
| 368 new CodeLinesColumnBlock(jsCodeLines, dartCodeLines)); |
| 369 collectCodeSources(codeSources, dartCodeLines); |
232 } | 370 } |
233 if (codeSource != null) { | 371 if (codeSources.isNotEmpty) { |
234 block.addColumn(2, | 372 block.addColumnBlock(const DiffColumn('dart'), |
235 codeLinesFromCodeSource(sourceFileManager, codeSource)); | 373 codeLinesFromCodeSources(mainCodeSources, codeSources)); |
236 } | 374 } |
237 blocks.add(block); | 375 blocks.add(block); |
238 } | 376 } |
239 | 377 |
240 /// Merge the code lines in [range1] and [range2] of the corresponding input. | 378 /// Merge the code lines in [range1] and [range2] of the corresponding input. |
241 void addRaw(Interval range1, Interval range2) { | 379 void addRaw(Interval range1, Interval range2) { |
| 380 if (range1.isEmpty && range2.isEmpty) return; |
| 381 |
242 match(a, b) => a.code == b.code; | 382 match(a, b) => a.code == b.code; |
243 | 383 |
244 List<Interval> currentMatchedIntervals; | 384 List<Interval> currentMatchedIntervals; |
245 List<Interval> currentUnmatchedIntervals; | 385 List<Interval> currentUnmatchedIntervals; |
246 | 386 |
247 void flushMatching() { | 387 void flushMatching() { |
248 if (currentMatchedIntervals != null) { | 388 if (currentMatchedIntervals != null) { |
249 addLines(DiffKind.IDENTICAL, currentMatchedIntervals); | 389 addLines(DiffKind.IDENTICAL, currentMatchedIntervals); |
250 } | 390 } |
251 currentMatchedIntervals = null; | 391 currentMatchedIntervals = null; |
(...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
406 structures[1].children, | 546 structures[1].children, |
407 match: (a, b) => a.name == b.name, | 547 match: (a, b) => a.name == b.name, |
408 handleSkew: addBlock, | 548 handleSkew: addBlock, |
409 handleMatched: addMatchingBlocks, | 549 handleMatched: addMatchingBlocks, |
410 handleUnmatched: addUnmatchedBlocks); | 550 handleUnmatched: addUnmatchedBlocks); |
411 | 551 |
412 addRaw(structures[0].footer, structures[1].footer); | 552 addRaw(structures[0].footer, structures[1].footer); |
413 | 553 |
414 return blocks; | 554 return blocks; |
415 } | 555 } |
416 } | 556 |
417 | 557 /// Creates html lines for code lines in [codeSource]. The [sourceFileManager] |
418 /// Creates html lines for code lines in [codeSource]. [sourceFileManager] is | 558 /// is used to read that text from the source URIs. |
419 /// used to read that text from the source URIs. | 559 List<HtmlPart> codeLinesFromCodeSource(CodeSource codeSource) { |
420 List<HtmlPart> codeLinesFromCodeSource( | 560 List<HtmlPart> lines = <HtmlPart>[]; |
421 SourceFileManager sourceFileManager, | 561 SourceFile sourceFile = sourceFileManager.getSourceFile(codeSource.uri); |
422 CodeSource codeSource) { | 562 String elementName = codeSource.name; |
423 List<HtmlPart> lines = <HtmlPart>[]; | 563 HtmlLine line = new HtmlLine(); |
424 SourceFile sourceFile = sourceFileManager.getSourceFile(codeSource.uri); | 564 line.htmlParts.add(new ConstHtmlPart('<span class="comment">')); |
425 String elementName = codeSource.name; | 565 line.htmlParts.add(new HtmlText( |
426 HtmlLine line = new HtmlLine(); | 566 '${elementName}: ${sourceFile.filename}')); |
427 line.htmlParts.add(new ConstHtmlPart('<span class="comment">')); | 567 line.htmlParts.add(new ConstHtmlPart('</span>')); |
428 line.htmlParts.add(new HtmlText( | 568 lines.add(line); |
429 '${elementName}: ${sourceFile.filename}')); | 569 if (codeSource.begin != null) { |
430 line.htmlParts.add(new ConstHtmlPart('</span>')); | 570 int startLine = sourceFile.getLine(codeSource.begin); |
431 lines.add(line); | 571 int endLine = sourceFile.getLine(codeSource.end) + 1; |
432 if (codeSource.begin != null) { | 572 for (CodeLine codeLine in convertAnnotatedCodeToCodeLines( |
433 int startLine = sourceFile.getLine(codeSource.begin); | 573 sourceFile.slowText(), |
434 int endLine = sourceFile.getLine(codeSource.end); | 574 const <Annotation>[], |
435 for (int lineNo = startLine; lineNo <= endLine; lineNo++) { | 575 startLine: startLine, |
436 String text = sourceFile.getLineText(lineNo); | 576 endLine: endLine)) { |
437 CodeLine codeLine = new CodeLine(lineNo, sourceFile.getOffset(lineNo, 0)); | 577 codeLine.lineAnnotation = codeSource; |
438 codeLine.codeBuffer.write(text); | 578 lines.add(codeLine); |
439 codeLine.htmlParts.add(new HtmlText(text)); | 579 } |
440 lines.add(codeLine); | 580 } |
441 } | 581 return lines; |
442 } | 582 } |
443 return lines; | 583 |
444 } | 584 /// Creates a map from JavaScript [CodeLine]s in [jsCodeLines] to the Dart |
| 585 /// [CodeLine]s references in the source information. |
| 586 Map<CodeLine, List<CodeLine>> dartCodeLinesFromJsCodeLines( |
| 587 List<CodeLine> jsCodeLines) { |
| 588 Map<CodeLine, Interval> codeLineInterval = <CodeLine, Interval>{}; |
| 589 Map<CodeLine, List<CodeLine>> jsToDartMap = <CodeLine, List<CodeLine>>{}; |
| 590 List<Annotation> annotations = <Annotation>[]; |
| 591 Uri currentUri; |
| 592 Interval interval; |
| 593 |
| 594 Map<Uri, Set<CodeSource>> codeSourceMap = <Uri, Set<CodeSource>>{}; |
| 595 |
| 596 for (CodeLine jsCodeLine in jsCodeLines) { |
| 597 for (Annotation annotation in jsCodeLine.annotations) { |
| 598 CodeLineAnnotation codeLineAnnotation = annotation.data; |
| 599 for (CodeSource codeSource in codeLineAnnotation.codeSources) { |
| 600 codeSourceMap.putIfAbsent(codeSource.uri, |
| 601 () => new Set<CodeSource>()).add(codeSource); |
| 602 } |
| 603 } |
| 604 } |
| 605 |
| 606 void flush() { |
| 607 if (currentUri == null) return; |
| 608 |
| 609 Set<CodeSource> codeSources = codeSourceMap[currentUri]; |
| 610 SourceFile sourceFile = sourceFileManager.getSourceFile(currentUri); |
| 611 List<CodeLine> annotatedDartCodeLines = |
| 612 convertAnnotatedCodeToCodeLines( |
| 613 sourceFile.slowText(), |
| 614 annotations, |
| 615 startLine: interval.from, |
| 616 endLine: interval.to, |
| 617 uri: currentUri); |
| 618 if (codeSources != null) { |
| 619 CodeSource currentCodeSource; |
| 620 Interval currentLineInterval; |
| 621 for (CodeLine dartCodeLine in annotatedDartCodeLines) { |
| 622 if (currentCodeSource == null || |
| 623 !currentLineInterval.contains(dartCodeLine.lineNo)) { |
| 624 currentCodeSource = null; |
| 625 for (CodeSource codeSource in codeSources) { |
| 626 Interval interval = new Interval( |
| 627 sourceFile.getLine(codeSource.begin), |
| 628 sourceFile.getLine(codeSource.end) + 1); |
| 629 if (interval.contains(dartCodeLine.lineNo)) { |
| 630 currentCodeSource = codeSource; |
| 631 currentLineInterval = interval; |
| 632 break; |
| 633 } |
| 634 } |
| 635 } |
| 636 if (currentCodeSource != null) { |
| 637 dartCodeLine.lineAnnotation = currentCodeSource; |
| 638 } |
| 639 } |
| 640 } |
| 641 |
| 642 int index = 0; |
| 643 for (CodeLine jsCodeLine in codeLineInterval.keys) { |
| 644 List<CodeLine> dartCodeLines = |
| 645 jsToDartMap.putIfAbsent(jsCodeLine, () => <CodeLine>[]); |
| 646 if (dartCodeLines.isEmpty && index < annotatedDartCodeLines.length) { |
| 647 dartCodeLines.add(annotatedDartCodeLines[index++]); |
| 648 } |
| 649 } |
| 650 while (index < annotatedDartCodeLines.length) { |
| 651 jsToDartMap[codeLineInterval.keys.last].add( |
| 652 annotatedDartCodeLines[index++]); |
| 653 } |
| 654 |
| 655 currentUri = null; |
| 656 } |
| 657 |
| 658 void restart(CodeLine codeLine, CodeLocation codeLocation, int line) { |
| 659 flush(); |
| 660 |
| 661 currentUri = codeLocation.uri; |
| 662 interval = new Interval(line, line + 1); |
| 663 annotations = <Annotation>[]; |
| 664 codeLineInterval.clear(); |
| 665 codeLineInterval[codeLine] = interval; |
| 666 } |
| 667 |
| 668 for (CodeLine jsCodeLine in jsCodeLines) { |
| 669 for (Annotation annotation in jsCodeLine.annotations) { |
| 670 CodeLineAnnotation codeLineAnnotation = annotation.data; |
| 671 |
| 672 for (CodeLocation location in codeLineAnnotation.codeLocations) { |
| 673 SourceFile sourceFile = sourceFileManager.getSourceFile(location.uri); |
| 674 int line = sourceFile.getLine(location.offset); |
| 675 if (currentUri != location.uri) { |
| 676 restart(jsCodeLine, location, line); |
| 677 } else if (interval.inWindow(line, windowSize: 2)) { |
| 678 interval = interval.include(line); |
| 679 codeLineInterval[jsCodeLine] = interval; |
| 680 } else { |
| 681 restart(jsCodeLine, location, line); |
| 682 } |
| 683 |
| 684 annotations.add(new Annotation( |
| 685 codeLineAnnotation.annotationType, |
| 686 location.offset, |
| 687 'id=${codeLineAnnotation.annotationId}', |
| 688 data: codeLineAnnotation)); |
| 689 } |
| 690 } |
| 691 } |
| 692 flush(); |
| 693 return jsToDartMap; |
| 694 } |
| 695 } |
| 696 |
| 697 const DiffColumn column_js0 = const DiffColumn('js', 0); |
| 698 const DiffColumn column_js1 = const DiffColumn('js', 1); |
| 699 const DiffColumn column_dart = const DiffColumn('dart'); |
| 700 |
| 701 class ClassNames { |
| 702 static String column(DiffColumn column) => 'column_${column}'; |
| 703 static String identical(bool alternate) => |
| 704 'identical${alternate ? '1' : '2'}'; |
| 705 static String corresponding(bool alternate) => |
| 706 'corresponding${alternate ? '1' : '2'}'; |
| 707 |
| 708 static const String buttons = 'buttons'; |
| 709 static const String comment = 'comment'; |
| 710 static const String header = 'header'; |
| 711 static const String headerTable = 'header_table'; |
| 712 static const String headerColumn = 'header_column'; |
| 713 static const String legend = 'legend'; |
| 714 static const String table = 'table'; |
| 715 |
| 716 static const String cell = 'cell'; |
| 717 static const String innerCell = 'inner_cell'; |
| 718 |
| 719 static const String originalDart = 'main_dart'; |
| 720 static const String inlinedDart = 'inlined_dart'; |
| 721 |
| 722 static const String line = 'line'; |
| 723 static const String lineNumber = 'line_number'; |
| 724 static String colored(int index) => 'colored${index}'; |
| 725 |
| 726 static const String withSourceInfo = 'with_source_info'; |
| 727 static const String withoutSourceInfo = 'without_source_info'; |
| 728 static const String additionalSourceInfo = 'additional_source_info'; |
| 729 static const String unusedSourceInfo = 'unused_source_info'; |
| 730 |
| 731 static const String sourceMapped = 'source_mapped'; |
| 732 static const String sourceMapping = 'source_mapping'; |
| 733 static String sourceMappingIndex(int index) => 'source_mapping${index}'; |
| 734 |
| 735 static const String markers = 'markers'; |
| 736 static const String marker = 'marker'; |
| 737 } |
| 738 |
| 739 class AnnotationType { |
| 740 static const WITH_SOURCE_INFO = |
| 741 const AnnotationType(0, ClassNames.withSourceInfo, true); |
| 742 static const WITHOUT_SOURCE_INFO = |
| 743 const AnnotationType(1, ClassNames.withoutSourceInfo, false); |
| 744 static const ADDITIONAL_SOURCE_INFO = |
| 745 const AnnotationType(2, ClassNames.additionalSourceInfo, true); |
| 746 static const UNUSED_SOURCE_INFO = |
| 747 const AnnotationType(3, ClassNames.unusedSourceInfo, false); |
| 748 |
| 749 final int index; |
| 750 final String className; |
| 751 final bool isSourceMapped; |
| 752 |
| 753 const AnnotationType(this.index, this.className, this.isSourceMapped); |
| 754 |
| 755 static const List<AnnotationType> values = const <AnnotationType>[ |
| 756 WITH_SOURCE_INFO, |
| 757 WITHOUT_SOURCE_INFO, |
| 758 ADDITIONAL_SOURCE_INFO, |
| 759 UNUSED_SOURCE_INFO]; |
| 760 } |
| 761 |
| 762 class CodeLineAnnotation { |
| 763 final int annotationId; |
| 764 final AnnotationType annotationType; |
| 765 final List<CodeLocation> codeLocations; |
| 766 final List<CodeSource> codeSources; |
| 767 final String stepInfo; |
| 768 int sourceMappingIndex; |
| 769 |
| 770 CodeLineAnnotation( |
| 771 {this.annotationId, |
| 772 this.annotationType, |
| 773 this.codeLocations, |
| 774 this.codeSources, |
| 775 this.stepInfo, |
| 776 this.sourceMappingIndex}); |
| 777 |
| 778 Map toJson(JsonStrategy strategy) { |
| 779 return { |
| 780 'annotationId': annotationId, |
| 781 'annotationType': annotationType.index, |
| 782 'codeLocations': codeLocations.map((l) => l.toJson(strategy)).toList(), |
| 783 'codeSources': codeSources.map((c) => c.toJson()).toList(), |
| 784 'stepInfo': stepInfo, |
| 785 'sourceMappingIndex': sourceMappingIndex, |
| 786 }; |
| 787 } |
| 788 |
| 789 static fromJson(Map json, JsonStrategy strategy) { |
| 790 return new CodeLineAnnotation( |
| 791 annotationId: json['id'], |
| 792 annotationType: AnnotationType.values[json['annotationType']], |
| 793 codeLocations: json['codeLocations'] |
| 794 .map((j) => CodeLocation.fromJson(j, strategy)) |
| 795 .toList(), |
| 796 codeSources: json['codeSources'] |
| 797 .map((j) => CodeSource.fromJson(j)) |
| 798 .toList(), |
| 799 stepInfo: json['stepInfo'], |
| 800 sourceMappingIndex: json['sourceMappingIndex']); |
| 801 } |
| 802 } |
OLD | NEW |