| 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'; |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 61 final Map<CodeLine, List<CodeLine>> jsToDartMap; | 61 final Map<CodeLine, List<CodeLine>> jsToDartMap; |
| 62 | 62 |
| 63 CodeLinesColumnBlock(this.jsCodeLines, this.jsToDartMap); | 63 CodeLinesColumnBlock(this.jsCodeLines, this.jsToDartMap); |
| 64 | 64 |
| 65 void printHtmlOn(StringBuffer htmlBuffer, HtmlPrintContext context) { | 65 void printHtmlOn(StringBuffer htmlBuffer, HtmlPrintContext context) { |
| 66 if (jsCodeLines.isNotEmpty) { | 66 if (jsCodeLines.isNotEmpty) { |
| 67 htmlBuffer.write('<table style="width:100%">'); | 67 htmlBuffer.write('<table style="width:100%">'); |
| 68 for (CodeLine codeLine in jsCodeLines) { | 68 for (CodeLine codeLine in jsCodeLines) { |
| 69 htmlBuffer.write('<tr><td class="${ClassNames.innerCell}">'); | 69 htmlBuffer.write('<tr><td class="${ClassNames.innerCell}">'); |
| 70 codeLine.printHtmlOn(htmlBuffer, context); | 70 codeLine.printHtmlOn(htmlBuffer, context); |
| 71 htmlBuffer.write( | 71 htmlBuffer.write('</td><td ' |
| 72 '</td><td ' | |
| 73 'class="${ClassNames.innerCell} ${ClassNames.sourceMapped}">'); | 72 'class="${ClassNames.innerCell} ${ClassNames.sourceMapped}">'); |
| 74 List<CodeLine> lines = jsToDartMap[codeLine]; | 73 List<CodeLine> lines = jsToDartMap[codeLine]; |
| 75 if (lines != null) { | 74 if (lines != null) { |
| 76 for (CodeLine line in lines) { | 75 for (CodeLine line in lines) { |
| 77 line.printHtmlOn(htmlBuffer, | 76 line.printHtmlOn(htmlBuffer, context.from(includeAnnotation: (a) { |
| 78 context.from(includeAnnotation: (a) { | 77 CodeLineAnnotation annotation = a.data; |
| 79 CodeLineAnnotation annotation = a.data; | 78 return annotation.annotationType.isSourceMapped; |
| 80 return annotation.annotationType.isSourceMapped; | 79 })); |
| 81 })); | |
| 82 } | 80 } |
| 83 } | 81 } |
| 84 htmlBuffer.write('</td></tr>'); | 82 htmlBuffer.write('</td></tr>'); |
| 85 } | 83 } |
| 86 htmlBuffer.write('</table>'); | 84 htmlBuffer.write('</table>'); |
| 87 } | 85 } |
| 88 } | 86 } |
| 89 } | 87 } |
| 90 | 88 |
| 91 /// A list of columns that should align in output. | 89 /// A list of columns that should align in output. |
| 92 class DiffBlock { | 90 class DiffBlock { |
| 93 final DiffKind kind; | 91 final DiffKind kind; |
| 94 Map<DiffColumn, DiffColumnBlock> _columns = <DiffColumn, DiffColumnBlock>{}; | 92 Map<DiffColumn, DiffColumnBlock> _columns = <DiffColumn, DiffColumnBlock>{}; |
| 95 | 93 |
| 96 DiffBlock(this.kind); | 94 DiffBlock(this.kind); |
| 97 | 95 |
| 98 void addColumnBlock(DiffColumn column, DiffColumnBlock block) { | 96 void addColumnBlock(DiffColumn column, DiffColumnBlock block) { |
| 99 _columns[column] = block; | 97 _columns[column] = block; |
| 100 } | 98 } |
| 101 | 99 |
| 102 Iterable<DiffColumn> get columns => _columns.keys; | 100 Iterable<DiffColumn> get columns => _columns.keys; |
| 103 | 101 |
| 104 void printHtmlOn(DiffColumn column, | 102 void printHtmlOn( |
| 105 StringBuffer htmlBuffer, | 103 DiffColumn column, StringBuffer htmlBuffer, HtmlPrintContext context) { |
| 106 HtmlPrintContext context) { | |
| 107 DiffColumnBlock block = _columns[column]; | 104 DiffColumnBlock block = _columns[column]; |
| 108 if (block != null) { | 105 if (block != null) { |
| 109 block.printHtmlOn(htmlBuffer, context); | 106 block.printHtmlOn(htmlBuffer, context); |
| 110 } | 107 } |
| 111 } | 108 } |
| 112 } | 109 } |
| 113 | 110 |
| 114 | |
| 115 /// Align the content of [list1] and [list2]. | 111 /// Align the content of [list1] and [list2]. |
| 116 /// | 112 /// |
| 117 /// If provided, [range1] and [range2] aligned the subranges of [list1] and | 113 /// If provided, [range1] and [range2] aligned the subranges of [list1] and |
| 118 /// [list2], otherwise the whole lists are aligned. | 114 /// [list2], otherwise the whole lists are aligned. |
| 119 /// | 115 /// |
| 120 /// If provided, [match] determines the equality between members of [list1] and | 116 /// If provided, [match] determines the equality between members of [list1] and |
| 121 /// [list2], otherwise `==` is used. | 117 /// [list2], otherwise `==` is used. |
| 122 /// | 118 /// |
| 123 /// [handleSkew] is called when a subrange of one list is not found in the | 119 /// [handleSkew] is called when a subrange of one list is not found in the |
| 124 /// other. | 120 /// other. |
| 125 /// | 121 /// |
| 126 /// [handleMatched] is called when two indices match up. | 122 /// [handleMatched] is called when two indices match up. |
| 127 /// | 123 /// |
| 128 /// [handleUnmatched] is called when two indices don't match up (none are found | 124 /// [handleUnmatched] is called when two indices don't match up (none are found |
| 129 /// in the other list). | 125 /// in the other list). |
| 130 void align(List list1, | 126 void align(List list1, List list2, |
| 131 List list2, | 127 {Interval range1, |
| 132 {Interval range1, | 128 Interval range2, |
| 133 Interval range2, | 129 bool match(a, b), |
| 134 bool match(a, b), | 130 void handleSkew(int listIndex, Interval range), |
| 135 void handleSkew(int listIndex, Interval range), | 131 void handleMatched(List<int> indices), |
| 136 void handleMatched(List<int> indices), | 132 void handleUnmatched(List<int> indices)}) { |
| 137 void handleUnmatched(List<int> indices)}) { | |
| 138 if (match == null) { | 133 if (match == null) { |
| 139 match = (a, b) => a == b; | 134 match = (a, b) => a == b; |
| 140 } | 135 } |
| 141 | 136 |
| 142 if (range1 == null) { | 137 if (range1 == null) { |
| 143 range1 = new Interval(0, list1.length); | 138 range1 = new Interval(0, list1.length); |
| 144 } | 139 } |
| 145 if (range2 == null) { | 140 if (range2 == null) { |
| 146 range2 = new Interval(0, list2.length); | 141 range2 = new Interval(0, list2.length); |
| 147 } | 142 } |
| 148 | 143 |
| 149 Interval findInOther( | 144 Interval findInOther(List thisLines, Interval thisRange, List otherLines, |
| 150 List thisLines, Interval thisRange, | 145 Interval otherRange) { |
| 151 List otherLines, Interval otherRange) { | |
| 152 for (int index = otherRange.from; index < otherRange.to; index++) { | 146 for (int index = otherRange.from; index < otherRange.to; index++) { |
| 153 if (match(thisLines[thisRange.from], otherLines[index])) { | 147 if (match(thisLines[thisRange.from], otherLines[index])) { |
| 154 int offset = 1; | 148 int offset = 1; |
| 155 while (thisRange.from + offset < thisRange.to && | 149 while (thisRange.from + offset < thisRange.to && |
| 156 otherRange.from + offset < otherRange.to && | 150 otherRange.from + offset < otherRange.to && |
| 157 match(thisLines[thisRange.from + offset], | 151 match(thisLines[thisRange.from + offset], |
| 158 otherLines[otherRange.from + offset])) { | 152 otherLines[otherRange.from + offset])) { |
| 159 offset++; | 153 offset++; |
| 160 } | 154 } |
| 161 return new Interval(index, index + offset); | 155 return new Interval(index, index + offset); |
| 162 } | 156 } |
| 163 } | 157 } |
| 164 return null; | 158 return null; |
| 165 } | 159 } |
| 166 | 160 |
| 167 int start1 = range1.from; | 161 int start1 = range1.from; |
| 168 int end1 = range1.to; | 162 int end1 = range1.to; |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 222 handleSkew(0, new Interval(start1, end1)); | 216 handleSkew(0, new Interval(start1, end1)); |
| 223 } | 217 } |
| 224 if (start2 < end2) { | 218 if (start2 < end2) { |
| 225 handleSkew(1, new Interval(start2, end2)); | 219 handleSkew(1, new Interval(start2, end2)); |
| 226 } | 220 } |
| 227 } | 221 } |
| 228 | 222 |
| 229 /// Create a list of blocks containing the diff of the two output [structures] | 223 /// Create a list of blocks containing the diff of the two output [structures] |
| 230 /// and the corresponding Dart code. | 224 /// and the corresponding Dart code. |
| 231 List<DiffBlock> createDiffBlocks( | 225 List<DiffBlock> createDiffBlocks( |
| 232 List<OutputStructure> structures, | 226 List<OutputStructure> structures, SourceFileManager sourceFileManager) { |
| 233 SourceFileManager sourceFileManager) { | |
| 234 return new DiffCreator(structures, sourceFileManager).computeBlocks(); | 227 return new DiffCreator(structures, sourceFileManager).computeBlocks(); |
| 235 } | 228 } |
| 236 | 229 |
| 237 class DiffCreator { | 230 class DiffCreator { |
| 238 final List<OutputStructure> structures; | 231 final List<OutputStructure> structures; |
| 239 final SourceFileManager sourceFileManager; | 232 final SourceFileManager sourceFileManager; |
| 240 | 233 |
| 241 List<List<CodeLine>> inputLines; | 234 List<List<CodeLine>> inputLines; |
| 242 | 235 |
| 243 List<int> nextInputLine = [0, 0]; | 236 List<int> nextInputLine = [0, 0]; |
| (...skipping 12 matching lines...) Expand all Loading... |
| 256 sources.add(entity.codeSource); | 249 sources.add(entity.codeSource); |
| 257 } | 250 } |
| 258 } | 251 } |
| 259 return sources; | 252 return sources; |
| 260 } | 253 } |
| 261 | 254 |
| 262 /// Create a block with the code from [codeSources]. The [CodeSource]s in | 255 /// Create a block with the code from [codeSources]. The [CodeSource]s in |
| 263 /// [mainSources] are tagged as original code sources, the rest as inlined | 256 /// [mainSources] are tagged as original code sources, the rest as inlined |
| 264 /// code sources. | 257 /// code sources. |
| 265 DiffColumnBlock codeLinesFromCodeSources( | 258 DiffColumnBlock codeLinesFromCodeSources( |
| 266 Iterable<CodeSource> mainSources, | 259 Iterable<CodeSource> mainSources, Iterable<CodeSource> codeSources) { |
| 267 Iterable<CodeSource> codeSources) { | |
| 268 List<HtmlPart> parts = <HtmlPart>[]; | 260 List<HtmlPart> parts = <HtmlPart>[]; |
| 269 for (CodeSource codeSource in codeSources) { | 261 for (CodeSource codeSource in codeSources) { |
| 270 //parts.addAll(codeLinesFromCodeSource(codeSource)); | 262 //parts.addAll(codeLinesFromCodeSource(codeSource)); |
| 271 String className = | 263 String className = mainSources.contains(codeSource) |
| 272 mainSources.contains(codeSource) | 264 ? ClassNames.originalDart |
| 273 ? ClassNames.originalDart : ClassNames.inlinedDart; | 265 : ClassNames.inlinedDart; |
| 274 parts.add( | 266 parts.add(new TagPart('div', |
| 275 new TagPart('div', | 267 properties: {'class': className}, |
| 276 properties: {'class': className}, | 268 content: codeLinesFromCodeSource(codeSource))); |
| 277 content: codeLinesFromCodeSource(codeSource))); | |
| 278 } | 269 } |
| 279 return new PartsColumnBlock(parts); | 270 return new PartsColumnBlock(parts); |
| 280 } | 271 } |
| 281 | 272 |
| 282 /// Adds all [CodeSource]s used in [dartCodeLines] to [codeSourceSet]. | 273 /// Adds all [CodeSource]s used in [dartCodeLines] to [codeSourceSet]. |
| 283 void collectCodeSources(Set<CodeSource> codeSourceSet, | 274 void collectCodeSources(Set<CodeSource> codeSourceSet, |
| 284 Map<CodeLine, List<CodeLine>> dartCodeLines) { | 275 Map<CodeLine, List<CodeLine>> dartCodeLines) { |
| 285 for (List<CodeLine> codeLines in dartCodeLines.values) { | 276 for (List<CodeLine> codeLines in dartCodeLines.values) { |
| 286 for (CodeLine dartCodeLine in codeLines) { | 277 for (CodeLine dartCodeLine in codeLines) { |
| 287 if (dartCodeLine.lineAnnotation != null) { | 278 if (dartCodeLine.lineAnnotation != null) { |
| 288 codeSourceSet.add(dartCodeLine.lineAnnotation); | 279 codeSourceSet.add(dartCodeLine.lineAnnotation); |
| 289 } | 280 } |
| 290 } | 281 } |
| 291 } | 282 } |
| 292 } | 283 } |
| 293 | 284 |
| 294 /// Checks that lines are added in sequence without gaps or duplicates. | 285 /// Checks that lines are added in sequence without gaps or duplicates. |
| (...skipping 14 matching lines...) Expand all Loading... |
| 309 print(inputLines[index][i++].code); | 300 print(inputLines[index][i++].code); |
| 310 } | 301 } |
| 311 } | 302 } |
| 312 } | 303 } |
| 313 nextInputLine[index] = range.to; | 304 nextInputLine[index] = range.to; |
| 314 } | 305 } |
| 315 | 306 |
| 316 /// Creates a block containing the code lines in [range] from input number | 307 /// Creates a block containing the code lines in [range] from input number |
| 317 /// [index]. If [codeSource] is provided, the block will contain a | 308 /// [index]. If [codeSource] is provided, the block will contain a |
| 318 /// corresponding Dart code column. | 309 /// corresponding Dart code column. |
| 319 void handleSkew( | 310 void handleSkew(int index, Interval range, |
| 320 int index, | |
| 321 Interval range, | |
| 322 [Iterable<CodeSource> mainCodeSources = const <CodeSource>[]]) { | 311 [Iterable<CodeSource> mainCodeSources = const <CodeSource>[]]) { |
| 323 if (range.isEmpty) return; | 312 if (range.isEmpty) return; |
| 324 | 313 |
| 325 Set<CodeSource> codeSources = new Set<CodeSource>(); | 314 Set<CodeSource> codeSources = new Set<CodeSource>(); |
| 326 codeSources.addAll(mainCodeSources); | 315 codeSources.addAll(mainCodeSources); |
| 327 | 316 |
| 328 DiffBlock block = new DiffBlock(DiffKind.UNMATCHED); | 317 DiffBlock block = new DiffBlock(DiffKind.UNMATCHED); |
| 329 checkLineInvariant(index, range); | 318 checkLineInvariant(index, range); |
| 330 List<CodeLine> jsCodeLines = | 319 List<CodeLine> jsCodeLines = |
| 331 inputLines[index].sublist(range.from, range.to); | 320 inputLines[index].sublist(range.from, range.to); |
| 332 Map<CodeLine, List<CodeLine>> dartCodeLines = | 321 Map<CodeLine, List<CodeLine>> dartCodeLines = |
| 333 dartCodeLinesFromJsCodeLines(jsCodeLines); | 322 dartCodeLinesFromJsCodeLines(jsCodeLines); |
| 334 block.addColumnBlock( | 323 block.addColumnBlock(new DiffColumn('js', index), |
| 335 new DiffColumn('js', index), | |
| 336 new CodeLinesColumnBlock(jsCodeLines, dartCodeLines)); | 324 new CodeLinesColumnBlock(jsCodeLines, dartCodeLines)); |
| 337 collectCodeSources(codeSources, dartCodeLines); | 325 collectCodeSources(codeSources, dartCodeLines); |
| 338 | 326 |
| 339 if (codeSources.isNotEmpty) { | 327 if (codeSources.isNotEmpty) { |
| 340 block.addColumnBlock( | 328 block.addColumnBlock(const DiffColumn('dart'), |
| 341 const DiffColumn('dart'), | |
| 342 codeLinesFromCodeSources(mainCodeSources, codeSources)); | 329 codeLinesFromCodeSources(mainCodeSources, codeSources)); |
| 343 } | 330 } |
| 344 blocks.add(block); | 331 blocks.add(block); |
| 345 } | 332 } |
| 346 | 333 |
| 347 /// Create a block containing the code lines in [ranges] from the | 334 /// Create a block containing the code lines in [ranges] from the |
| 348 /// corresponding JavaScript inputs. If [codeSource] is provided, the block | 335 /// corresponding JavaScript inputs. If [codeSource] is provided, the block |
| 349 /// will contain a corresponding Dart code column. | 336 /// will contain a corresponding Dart code column. |
| 350 void addLines( | 337 void addLines(DiffKind kind, List<Interval> ranges, |
| 351 DiffKind kind, | |
| 352 List<Interval> ranges, | |
| 353 [Iterable<CodeSource> mainCodeSources = const <CodeSource>[]]) { | 338 [Iterable<CodeSource> mainCodeSources = const <CodeSource>[]]) { |
| 354 if (ranges.every((range) => range.isEmpty)) return; | 339 if (ranges.every((range) => range.isEmpty)) return; |
| 355 | 340 |
| 356 Set<CodeSource> codeSources = new Set<CodeSource>(); | 341 Set<CodeSource> codeSources = new Set<CodeSource>(); |
| 357 codeSources.addAll(mainCodeSources); | 342 codeSources.addAll(mainCodeSources); |
| 358 | 343 |
| 359 DiffBlock block = new DiffBlock(kind); | 344 DiffBlock block = new DiffBlock(kind); |
| 360 for (int i = 0; i < ranges.length; i++) { | 345 for (int i = 0; i < ranges.length; i++) { |
| 361 checkLineInvariant(i, ranges[i]); | 346 checkLineInvariant(i, ranges[i]); |
| 362 List<CodeLine> jsCodeLines = | 347 List<CodeLine> jsCodeLines = |
| 363 inputLines[i].sublist(ranges[i].from, ranges[i].to); | 348 inputLines[i].sublist(ranges[i].from, ranges[i].to); |
| 364 Map<CodeLine, List<CodeLine>> dartCodeLines = | 349 Map<CodeLine, List<CodeLine>> dartCodeLines = |
| 365 dartCodeLinesFromJsCodeLines(jsCodeLines); | 350 dartCodeLinesFromJsCodeLines(jsCodeLines); |
| 366 block.addColumnBlock( | 351 block.addColumnBlock(new DiffColumn('js', i), |
| 367 new DiffColumn('js', i), | |
| 368 new CodeLinesColumnBlock(jsCodeLines, dartCodeLines)); | 352 new CodeLinesColumnBlock(jsCodeLines, dartCodeLines)); |
| 369 collectCodeSources(codeSources, dartCodeLines); | 353 collectCodeSources(codeSources, dartCodeLines); |
| 370 } | 354 } |
| 371 if (codeSources.isNotEmpty) { | 355 if (codeSources.isNotEmpty) { |
| 372 block.addColumnBlock(const DiffColumn('dart'), | 356 block.addColumnBlock(const DiffColumn('dart'), |
| 373 codeLinesFromCodeSources(mainCodeSources, codeSources)); | 357 codeLinesFromCodeSources(mainCodeSources, codeSources)); |
| 374 } | 358 } |
| 375 blocks.add(block); | 359 blocks.add(block); |
| 376 } | 360 } |
| 377 | 361 |
| (...skipping 17 matching lines...) Expand all Loading... |
| 395 if (currentUnmatchedIntervals != null) { | 379 if (currentUnmatchedIntervals != null) { |
| 396 addLines(DiffKind.UNMATCHED, currentUnmatchedIntervals); | 380 addLines(DiffKind.UNMATCHED, currentUnmatchedIntervals); |
| 397 } | 381 } |
| 398 currentUnmatchedIntervals = null; | 382 currentUnmatchedIntervals = null; |
| 399 } | 383 } |
| 400 | 384 |
| 401 List<Interval> updateIntervals(List<Interval> current, List<int> indices) { | 385 List<Interval> updateIntervals(List<Interval> current, List<int> indices) { |
| 402 if (current == null) { | 386 if (current == null) { |
| 403 return [ | 387 return [ |
| 404 new Interval(indices[0], indices[0] + 1), | 388 new Interval(indices[0], indices[0] + 1), |
| 405 new Interval(indices[1], indices[1] + 1)]; | 389 new Interval(indices[1], indices[1] + 1) |
| 390 ]; |
| 406 } else { | 391 } else { |
| 407 current[0] = | 392 current[0] = new Interval(current[0].from, indices[0] + 1); |
| 408 new Interval(current[0].from, indices[0] + 1); | 393 current[1] = new Interval(current[1].from, indices[1] + 1); |
| 409 current[1] = | |
| 410 new Interval(current[1].from, indices[1] + 1); | |
| 411 return current; | 394 return current; |
| 412 } | 395 } |
| 413 } | 396 } |
| 414 | 397 |
| 415 align( | 398 align(inputLines[0], inputLines[1], |
| 416 inputLines[0], | |
| 417 inputLines[1], | |
| 418 range1: range1, | 399 range1: range1, |
| 419 range2: range2, | 400 range2: range2, |
| 420 match: match, | 401 match: match, handleSkew: (int listIndex, Interval range) { |
| 421 handleSkew: (int listIndex, Interval range) { | 402 flushMatching(); |
| 422 flushMatching(); | 403 flushUnmatched(); |
| 423 flushUnmatched(); | 404 handleSkew(listIndex, range); |
| 424 handleSkew(listIndex, range); | 405 }, handleMatched: (List<int> indices) { |
| 425 }, | 406 flushUnmatched(); |
| 426 handleMatched: (List<int> indices) { | 407 currentMatchedIntervals = |
| 427 flushUnmatched(); | 408 updateIntervals(currentMatchedIntervals, indices); |
| 428 currentMatchedIntervals = | 409 }, handleUnmatched: (List<int> indices) { |
| 429 updateIntervals(currentMatchedIntervals, indices); | 410 flushMatching(); |
| 430 }, | 411 currentUnmatchedIntervals = |
| 431 handleUnmatched: (List<int> indices) { | 412 updateIntervals(currentUnmatchedIntervals, indices); |
| 432 flushMatching(); | 413 }); |
| 433 currentUnmatchedIntervals = | |
| 434 updateIntervals(currentUnmatchedIntervals, indices); | |
| 435 }); | |
| 436 | 414 |
| 437 flushMatching(); | 415 flushMatching(); |
| 438 flushUnmatched(); | 416 flushUnmatched(); |
| 439 } | 417 } |
| 440 | 418 |
| 441 /// Adds the top level blocks in [childRange] for structure [index]. | 419 /// Adds the top level blocks in [childRange] for structure [index]. |
| 442 void addBlock(int index, Interval childRange) { | 420 void addBlock(int index, Interval childRange) { |
| 443 addSkewedChildren(index, structures[index], childRange); | 421 addSkewedChildren(index, structures[index], childRange); |
| 444 } | 422 } |
| 445 | 423 |
| 446 /// Adds the [entity] from structure [index]. If the [entity] supports child | 424 /// Adds the [entity] from structure [index]. If the [entity] supports child |
| 447 /// entities, these are process individually. Otherwise the lines from | 425 /// entities, these are process individually. Otherwise the lines from |
| 448 /// [entity] are added directly. | 426 /// [entity] are added directly. |
| 449 void addSkewedEntity(int index, OutputEntity entity) { | 427 void addSkewedEntity(int index, OutputEntity entity) { |
| 450 if (entity.canHaveChildren) { | 428 if (entity.canHaveChildren) { |
| 451 handleSkew(index, entity.header); | 429 handleSkew(index, entity.header); |
| 452 addSkewedChildren( | 430 addSkewedChildren(index, entity, new Interval(0, entity.children.length)); |
| 453 index, entity, new Interval(0, entity.children.length)); | |
| 454 handleSkew(index, entity.footer); | 431 handleSkew(index, entity.footer); |
| 455 } else { | 432 } else { |
| 456 handleSkew(index, entity.interval, codeSourceFromEntities([entity])); | 433 handleSkew(index, entity.interval, codeSourceFromEntities([entity])); |
| 457 } | 434 } |
| 458 } | 435 } |
| 459 | 436 |
| 460 /// Adds the children of [parent] in [childRange] from structure [index]. | 437 /// Adds the children of [parent] in [childRange] from structure [index]. |
| 461 void addSkewedChildren(int index, OutputEntity parent, Interval childRange) { | 438 void addSkewedChildren(int index, OutputEntity parent, Interval childRange) { |
| 462 for (int i = childRange.from; i < childRange.to; i++) { | 439 for (int i = childRange.from; i < childRange.to; i++) { |
| 463 addSkewedEntity(index, parent.getChild(i)); | 440 addSkewedEntity(index, parent.getChild(i)); |
| 464 } | 441 } |
| 465 } | 442 } |
| 466 | 443 |
| 467 /// Adds the members of the [classes] aligned. | 444 /// Adds the members of the [classes] aligned. |
| 468 void addMatchingContainers(List<OutputEntity> classes) { | 445 void addMatchingContainers(List<OutputEntity> classes) { |
| 469 addLines(DiffKind.MATCHING, classes.map((c) => c.header).toList()); | 446 addLines(DiffKind.MATCHING, classes.map((c) => c.header).toList()); |
| 470 align(classes[0].children, classes[1].children, | 447 align(classes[0].children, classes[1].children, |
| 471 match: (a, b) => a.name == b.name, | 448 match: (a, b) => a.name == b.name, |
| 472 handleSkew: (int listIndex, Interval childRange) { | 449 handleSkew: (int listIndex, Interval childRange) { |
| 473 addSkewedChildren(listIndex, classes[listIndex], childRange); | 450 addSkewedChildren(listIndex, classes[listIndex], childRange); |
| 474 }, | 451 }, handleMatched: (List<int> indices) { |
| 475 handleMatched: (List<int> indices) { | 452 List<BasicEntity> entities = [ |
| 476 List<BasicEntity> entities = [ | 453 classes[0].getChild(indices[0]), |
| 477 classes[0].getChild(indices[0]), | 454 classes[1].getChild(indices[1]) |
| 478 classes[1].getChild(indices[1])]; | 455 ]; |
| 479 if (entities.every((e) => e is Statics)) { | 456 if (entities.every((e) => e is Statics)) { |
| 480 addMatchingContainers(entities); | 457 addMatchingContainers(entities); |
| 481 } else { | 458 } else { |
| 482 addLines(DiffKind.MATCHING, | 459 addLines(DiffKind.MATCHING, entities.map((e) => e.interval).toList(), |
| 483 entities.map((e) => e.interval).toList(), | 460 codeSourceFromEntities(entities)); |
| 484 codeSourceFromEntities(entities)); | 461 } |
| 485 } | 462 }, handleUnmatched: (List<int> indices) { |
| 486 }, | 463 List<Interval> intervals = [ |
| 487 handleUnmatched: (List<int> indices) { | 464 classes[0].getChild(indices[0]).interval, |
| 488 List<Interval> intervals = [ | 465 classes[1].getChild(indices[1]).interval |
| 489 classes[0].getChild(indices[0]).interval, | 466 ]; |
| 490 classes[1].getChild(indices[1]).interval]; | 467 addLines(DiffKind.UNMATCHED, intervals); |
| 491 addLines(DiffKind.UNMATCHED, intervals); | 468 }); |
| 492 }); | |
| 493 addLines(DiffKind.MATCHING, classes.map((c) => c.footer).toList()); | 469 addLines(DiffKind.MATCHING, classes.map((c) => c.footer).toList()); |
| 494 } | 470 } |
| 495 | 471 |
| 496 /// Adds the library blocks in [indices] from the corresponding | 472 /// Adds the library blocks in [indices] from the corresponding |
| 497 /// [OutputStructure]s, aligning their content. | 473 /// [OutputStructure]s, aligning their content. |
| 498 void addMatchingBlocks(List<int> indices) { | 474 void addMatchingBlocks(List<int> indices) { |
| 499 List<LibraryBlock> blocks = [ | 475 List<LibraryBlock> blocks = [ |
| 500 structures[0].getChild(indices[0]), | 476 structures[0].getChild(indices[0]), |
| 501 structures[1].getChild(indices[1])]; | 477 structures[1].getChild(indices[1]) |
| 478 ]; |
| 502 | 479 |
| 503 addLines(DiffKind.MATCHING, blocks.map((b) => b.header).toList()); | 480 addLines(DiffKind.MATCHING, blocks.map((b) => b.header).toList()); |
| 504 align(blocks[0].children, blocks[1].children, | 481 align(blocks[0].children, blocks[1].children, |
| 505 match: (a, b) => a.name == b.name, | 482 match: (a, b) => a.name == b.name, |
| 506 handleSkew: (int listIndex, Interval childRange) { | 483 handleSkew: (int listIndex, Interval childRange) { |
| 507 addSkewedChildren( | 484 addSkewedChildren(listIndex, blocks[listIndex], childRange); |
| 508 listIndex, blocks[listIndex], childRange); | 485 }, handleMatched: (List<int> indices) { |
| 509 }, | 486 List<BasicEntity> entities = [ |
| 510 handleMatched: (List<int> indices) { | 487 blocks[0].getChild(indices[0]), |
| 511 List<BasicEntity> entities = [ | 488 blocks[1].getChild(indices[1]) |
| 512 blocks[0].getChild(indices[0]), | 489 ]; |
| 513 blocks[1].getChild(indices[1])]; | 490 if (entities.every((e) => e is LibraryClass)) { |
| 514 if (entities.every((e) => e is LibraryClass)) { | 491 addMatchingContainers(entities); |
| 515 addMatchingContainers(entities); | 492 } else { |
| 516 } else { | 493 addLines(DiffKind.MATCHING, entities.map((e) => e.interval).toList(), |
| 517 addLines( | 494 codeSourceFromEntities(entities)); |
| 518 DiffKind.MATCHING, | 495 } |
| 519 entities.map((e) => e.interval).toList(), | 496 }, handleUnmatched: (List<int> indices) { |
| 520 codeSourceFromEntities(entities)); | 497 List<Interval> intervals = [ |
| 521 } | 498 blocks[0].getChild(indices[0]).interval, |
| 522 }, | 499 blocks[1].getChild(indices[1]).interval |
| 523 handleUnmatched: (List<int> indices) { | 500 ]; |
| 524 List<Interval> intervals = [ | 501 addLines(DiffKind.UNMATCHED, intervals); |
| 525 blocks[0].getChild(indices[0]).interval, | 502 }); |
| 526 blocks[1].getChild(indices[1]).interval]; | |
| 527 addLines(DiffKind.UNMATCHED, intervals); | |
| 528 }); | |
| 529 addLines(DiffKind.MATCHING, blocks.map((b) => b.footer).toList()); | 503 addLines(DiffKind.MATCHING, blocks.map((b) => b.footer).toList()); |
| 530 } | 504 } |
| 531 | 505 |
| 532 /// Adds the lines of the blocks in [indices] from the corresponding | 506 /// Adds the lines of the blocks in [indices] from the corresponding |
| 533 /// [OutputStructure]s. | 507 /// [OutputStructure]s. |
| 534 void addUnmatchedBlocks(List<int> indices) { | 508 void addUnmatchedBlocks(List<int> indices) { |
| 535 List<LibraryBlock> blocks = [ | 509 List<LibraryBlock> blocks = [ |
| 536 structures[0].getChild(indices[0]), | 510 structures[0].getChild(indices[0]), |
| 537 structures[1].getChild(indices[1])]; | 511 structures[1].getChild(indices[1]) |
| 512 ]; |
| 538 addLines(DiffKind.UNMATCHED, [blocks[0].interval, blocks[1].interval]); | 513 addLines(DiffKind.UNMATCHED, [blocks[0].interval, blocks[1].interval]); |
| 539 } | 514 } |
| 540 | 515 |
| 541 /// Computes the diff blocks for [OutputStructure]s. | 516 /// Computes the diff blocks for [OutputStructure]s. |
| 542 List<DiffBlock> computeBlocks() { | 517 List<DiffBlock> computeBlocks() { |
| 543 addRaw(structures[0].header, structures[1].header); | 518 addRaw(structures[0].header, structures[1].header); |
| 544 | 519 |
| 545 align(structures[0].children, | 520 align(structures[0].children, structures[1].children, |
| 546 structures[1].children, | 521 match: (a, b) => a.name == b.name, |
| 547 match: (a, b) => a.name == b.name, | 522 handleSkew: addBlock, |
| 548 handleSkew: addBlock, | 523 handleMatched: addMatchingBlocks, |
| 549 handleMatched: addMatchingBlocks, | 524 handleUnmatched: addUnmatchedBlocks); |
| 550 handleUnmatched: addUnmatchedBlocks); | |
| 551 | 525 |
| 552 addRaw(structures[0].footer, structures[1].footer); | 526 addRaw(structures[0].footer, structures[1].footer); |
| 553 | 527 |
| 554 return blocks; | 528 return blocks; |
| 555 } | 529 } |
| 556 | 530 |
| 557 /// Creates html lines for code lines in [codeSource]. The [sourceFileManager] | 531 /// Creates html lines for code lines in [codeSource]. The [sourceFileManager] |
| 558 /// is used to read that text from the source URIs. | 532 /// is used to read that text from the source URIs. |
| 559 List<HtmlPart> codeLinesFromCodeSource(CodeSource codeSource) { | 533 List<HtmlPart> codeLinesFromCodeSource(CodeSource codeSource) { |
| 560 List<HtmlPart> lines = <HtmlPart>[]; | 534 List<HtmlPart> lines = <HtmlPart>[]; |
| 561 SourceFile sourceFile = sourceFileManager.getSourceFile(codeSource.uri); | 535 SourceFile sourceFile = sourceFileManager.getSourceFile(codeSource.uri); |
| 562 String elementName = codeSource.name; | 536 String elementName = codeSource.name; |
| 563 HtmlLine line = new HtmlLine(); | 537 HtmlLine line = new HtmlLine(); |
| 564 line.htmlParts.add(new ConstHtmlPart('<span class="comment">')); | 538 line.htmlParts.add(new ConstHtmlPart('<span class="comment">')); |
| 565 line.htmlParts.add(new HtmlText( | 539 line.htmlParts.add(new HtmlText('${elementName}: ${sourceFile.filename}')); |
| 566 '${elementName}: ${sourceFile.filename}')); | |
| 567 line.htmlParts.add(new ConstHtmlPart('</span>')); | 540 line.htmlParts.add(new ConstHtmlPart('</span>')); |
| 568 lines.add(line); | 541 lines.add(line); |
| 569 if (codeSource.begin != null) { | 542 if (codeSource.begin != null) { |
| 570 int startLine = sourceFile.getLine(codeSource.begin); | 543 int startLine = sourceFile.getLine(codeSource.begin); |
| 571 int endLine = sourceFile.getLine(codeSource.end) + 1; | 544 int endLine = sourceFile.getLine(codeSource.end) + 1; |
| 572 for (CodeLine codeLine in convertAnnotatedCodeToCodeLines( | 545 for (CodeLine codeLine in convertAnnotatedCodeToCodeLines( |
| 573 sourceFile.slowText(), | 546 sourceFile.slowText(), const <Annotation>[], |
| 574 const <Annotation>[], | 547 startLine: startLine, endLine: endLine)) { |
| 575 startLine: startLine, | |
| 576 endLine: endLine)) { | |
| 577 codeLine.lineAnnotation = codeSource; | 548 codeLine.lineAnnotation = codeSource; |
| 578 lines.add(codeLine); | 549 lines.add(codeLine); |
| 579 } | 550 } |
| 580 } | 551 } |
| 581 return lines; | 552 return lines; |
| 582 } | 553 } |
| 583 | 554 |
| 584 /// Creates a map from JavaScript [CodeLine]s in [jsCodeLines] to the Dart | 555 /// Creates a map from JavaScript [CodeLine]s in [jsCodeLines] to the Dart |
| 585 /// [CodeLine]s references in the source information. | 556 /// [CodeLine]s references in the source information. |
| 586 Map<CodeLine, List<CodeLine>> dartCodeLinesFromJsCodeLines( | 557 Map<CodeLine, List<CodeLine>> dartCodeLinesFromJsCodeLines( |
| 587 List<CodeLine> jsCodeLines) { | 558 List<CodeLine> jsCodeLines) { |
| 588 Map<CodeLine, Interval> codeLineInterval = <CodeLine, Interval>{}; | 559 Map<CodeLine, Interval> codeLineInterval = <CodeLine, Interval>{}; |
| 589 Map<CodeLine, List<CodeLine>> jsToDartMap = <CodeLine, List<CodeLine>>{}; | 560 Map<CodeLine, List<CodeLine>> jsToDartMap = <CodeLine, List<CodeLine>>{}; |
| 590 List<Annotation> annotations = <Annotation>[]; | 561 List<Annotation> annotations = <Annotation>[]; |
| 591 Uri currentUri; | 562 Uri currentUri; |
| 592 Interval interval; | 563 Interval interval; |
| 593 | 564 |
| 594 Map<Uri, Set<CodeSource>> codeSourceMap = <Uri, Set<CodeSource>>{}; | 565 Map<Uri, Set<CodeSource>> codeSourceMap = <Uri, Set<CodeSource>>{}; |
| 595 | 566 |
| 596 for (CodeLine jsCodeLine in jsCodeLines) { | 567 for (CodeLine jsCodeLine in jsCodeLines) { |
| 597 for (Annotation annotation in jsCodeLine.annotations) { | 568 for (Annotation annotation in jsCodeLine.annotations) { |
| 598 CodeLineAnnotation codeLineAnnotation = annotation.data; | 569 CodeLineAnnotation codeLineAnnotation = annotation.data; |
| 599 for (CodeSource codeSource in codeLineAnnotation.codeSources) { | 570 for (CodeSource codeSource in codeLineAnnotation.codeSources) { |
| 600 codeSourceMap.putIfAbsent(codeSource.uri, | 571 codeSourceMap |
| 601 () => new Set<CodeSource>()).add(codeSource); | 572 .putIfAbsent(codeSource.uri, () => new Set<CodeSource>()) |
| 573 .add(codeSource); |
| 602 } | 574 } |
| 603 } | 575 } |
| 604 } | 576 } |
| 605 | 577 |
| 606 void flush() { | 578 void flush() { |
| 607 if (currentUri == null) return; | 579 if (currentUri == null) return; |
| 608 | 580 |
| 609 Set<CodeSource> codeSources = codeSourceMap[currentUri]; | 581 Set<CodeSource> codeSources = codeSourceMap[currentUri]; |
| 610 SourceFile sourceFile = sourceFileManager.getSourceFile(currentUri); | 582 SourceFile sourceFile = sourceFileManager.getSourceFile(currentUri); |
| 611 List<CodeLine> annotatedDartCodeLines = | 583 List<CodeLine> annotatedDartCodeLines = convertAnnotatedCodeToCodeLines( |
| 612 convertAnnotatedCodeToCodeLines( | 584 sourceFile.slowText(), annotations, |
| 613 sourceFile.slowText(), | 585 startLine: interval.from, endLine: interval.to, uri: currentUri); |
| 614 annotations, | |
| 615 startLine: interval.from, | |
| 616 endLine: interval.to, | |
| 617 uri: currentUri); | |
| 618 if (codeSources != null) { | 586 if (codeSources != null) { |
| 619 CodeSource currentCodeSource; | 587 CodeSource currentCodeSource; |
| 620 Interval currentLineInterval; | 588 Interval currentLineInterval; |
| 621 for (CodeLine dartCodeLine in annotatedDartCodeLines) { | 589 for (CodeLine dartCodeLine in annotatedDartCodeLines) { |
| 622 if (currentCodeSource == null || | 590 if (currentCodeSource == null || |
| 623 !currentLineInterval.contains(dartCodeLine.lineNo)) { | 591 !currentLineInterval.contains(dartCodeLine.lineNo)) { |
| 624 currentCodeSource = null; | 592 currentCodeSource = null; |
| 625 for (CodeSource codeSource in codeSources) { | 593 for (CodeSource codeSource in codeSources) { |
| 626 Interval interval = new Interval( | 594 Interval interval = new Interval( |
| 627 sourceFile.getLine(codeSource.begin), | 595 sourceFile.getLine(codeSource.begin), |
| (...skipping 13 matching lines...) Expand all Loading... |
| 641 | 609 |
| 642 int index = 0; | 610 int index = 0; |
| 643 for (CodeLine jsCodeLine in codeLineInterval.keys) { | 611 for (CodeLine jsCodeLine in codeLineInterval.keys) { |
| 644 List<CodeLine> dartCodeLines = | 612 List<CodeLine> dartCodeLines = |
| 645 jsToDartMap.putIfAbsent(jsCodeLine, () => <CodeLine>[]); | 613 jsToDartMap.putIfAbsent(jsCodeLine, () => <CodeLine>[]); |
| 646 if (dartCodeLines.isEmpty && index < annotatedDartCodeLines.length) { | 614 if (dartCodeLines.isEmpty && index < annotatedDartCodeLines.length) { |
| 647 dartCodeLines.add(annotatedDartCodeLines[index++]); | 615 dartCodeLines.add(annotatedDartCodeLines[index++]); |
| 648 } | 616 } |
| 649 } | 617 } |
| 650 while (index < annotatedDartCodeLines.length) { | 618 while (index < annotatedDartCodeLines.length) { |
| 651 jsToDartMap[codeLineInterval.keys.last].add( | 619 jsToDartMap[codeLineInterval.keys.last] |
| 652 annotatedDartCodeLines[index++]); | 620 .add(annotatedDartCodeLines[index++]); |
| 653 } | 621 } |
| 654 | 622 |
| 655 currentUri = null; | 623 currentUri = null; |
| 656 } | 624 } |
| 657 | 625 |
| 658 void restart(CodeLine codeLine, CodeLocation codeLocation, int line) { | 626 void restart(CodeLine codeLine, CodeLocation codeLocation, int line) { |
| 659 flush(); | 627 flush(); |
| 660 | 628 |
| 661 currentUri = codeLocation.uri; | 629 currentUri = codeLocation.uri; |
| 662 interval = new Interval(line, line + 1); | 630 interval = new Interval(line, line + 1); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 674 int line = sourceFile.getLine(location.offset); | 642 int line = sourceFile.getLine(location.offset); |
| 675 if (currentUri != location.uri) { | 643 if (currentUri != location.uri) { |
| 676 restart(jsCodeLine, location, line); | 644 restart(jsCodeLine, location, line); |
| 677 } else if (interval.inWindow(line, windowSize: 2)) { | 645 } else if (interval.inWindow(line, windowSize: 2)) { |
| 678 interval = interval.include(line); | 646 interval = interval.include(line); |
| 679 codeLineInterval[jsCodeLine] = interval; | 647 codeLineInterval[jsCodeLine] = interval; |
| 680 } else { | 648 } else { |
| 681 restart(jsCodeLine, location, line); | 649 restart(jsCodeLine, location, line); |
| 682 } | 650 } |
| 683 | 651 |
| 684 annotations.add(new Annotation( | 652 annotations.add(new Annotation(codeLineAnnotation.annotationType, |
| 685 codeLineAnnotation.annotationType, | 653 location.offset, 'id=${codeLineAnnotation.annotationId}', |
| 686 location.offset, | |
| 687 'id=${codeLineAnnotation.annotationId}', | |
| 688 data: codeLineAnnotation)); | 654 data: codeLineAnnotation)); |
| 689 } | 655 } |
| 690 } | 656 } |
| 691 } | 657 } |
| 692 flush(); | 658 flush(); |
| 693 return jsToDartMap; | 659 return jsToDartMap; |
| 694 } | 660 } |
| 695 } | 661 } |
| 696 | 662 |
| 697 const DiffColumn column_js0 = const DiffColumn('js', 0); | 663 const DiffColumn column_js0 = const DiffColumn('js', 0); |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 749 final int index; | 715 final int index; |
| 750 final String className; | 716 final String className; |
| 751 final bool isSourceMapped; | 717 final bool isSourceMapped; |
| 752 | 718 |
| 753 const AnnotationType(this.index, this.className, this.isSourceMapped); | 719 const AnnotationType(this.index, this.className, this.isSourceMapped); |
| 754 | 720 |
| 755 static const List<AnnotationType> values = const <AnnotationType>[ | 721 static const List<AnnotationType> values = const <AnnotationType>[ |
| 756 WITH_SOURCE_INFO, | 722 WITH_SOURCE_INFO, |
| 757 WITHOUT_SOURCE_INFO, | 723 WITHOUT_SOURCE_INFO, |
| 758 ADDITIONAL_SOURCE_INFO, | 724 ADDITIONAL_SOURCE_INFO, |
| 759 UNUSED_SOURCE_INFO]; | 725 UNUSED_SOURCE_INFO |
| 726 ]; |
| 760 } | 727 } |
| 761 | 728 |
| 762 class CodeLineAnnotation { | 729 class CodeLineAnnotation { |
| 763 final int annotationId; | 730 final int annotationId; |
| 764 final AnnotationType annotationType; | 731 final AnnotationType annotationType; |
| 765 final List<CodeLocation> codeLocations; | 732 final List<CodeLocation> codeLocations; |
| 766 final List<CodeSource> codeSources; | 733 final List<CodeSource> codeSources; |
| 767 final String stepInfo; | 734 final String stepInfo; |
| 768 int sourceMappingIndex; | 735 int sourceMappingIndex; |
| 769 | 736 |
| 770 CodeLineAnnotation( | 737 CodeLineAnnotation( |
| 771 {this.annotationId, | 738 {this.annotationId, |
| 772 this.annotationType, | 739 this.annotationType, |
| 773 this.codeLocations, | 740 this.codeLocations, |
| 774 this.codeSources, | 741 this.codeSources, |
| 775 this.stepInfo, | 742 this.stepInfo, |
| 776 this.sourceMappingIndex}); | 743 this.sourceMappingIndex}); |
| 777 | 744 |
| 778 Map toJson(JsonStrategy strategy) { | 745 Map toJson(JsonStrategy strategy) { |
| 779 return { | 746 return { |
| 780 'annotationId': annotationId, | 747 'annotationId': annotationId, |
| 781 'annotationType': annotationType.index, | 748 'annotationType': annotationType.index, |
| 782 'codeLocations': codeLocations.map((l) => l.toJson(strategy)).toList(), | 749 'codeLocations': codeLocations.map((l) => l.toJson(strategy)).toList(), |
| 783 'codeSources': codeSources.map((c) => c.toJson()).toList(), | 750 'codeSources': codeSources.map((c) => c.toJson()).toList(), |
| 784 'stepInfo': stepInfo, | 751 'stepInfo': stepInfo, |
| 785 'sourceMappingIndex': sourceMappingIndex, | 752 'sourceMappingIndex': sourceMappingIndex, |
| 786 }; | 753 }; |
| 787 } | 754 } |
| 788 | 755 |
| 789 static fromJson(Map json, JsonStrategy strategy) { | 756 static fromJson(Map json, JsonStrategy strategy) { |
| 790 return new CodeLineAnnotation( | 757 return new CodeLineAnnotation( |
| 791 annotationId: json['id'], | 758 annotationId: json['id'], |
| 792 annotationType: AnnotationType.values[json['annotationType']], | 759 annotationType: AnnotationType.values[json['annotationType']], |
| 793 codeLocations: json['codeLocations'] | 760 codeLocations: json['codeLocations'] |
| 794 .map((j) => CodeLocation.fromJson(j, strategy)) | 761 .map((j) => CodeLocation.fromJson(j, strategy)) |
| 795 .toList(), | 762 .toList(), |
| 796 codeSources: json['codeSources'] | 763 codeSources: |
| 797 .map((j) => CodeSource.fromJson(j)) | 764 json['codeSources'].map((j) => CodeSource.fromJson(j)).toList(), |
| 798 .toList(), | |
| 799 stepInfo: json['stepInfo'], | 765 stepInfo: json['stepInfo'], |
| 800 sourceMappingIndex: json['sourceMappingIndex']); | 766 sourceMappingIndex: json['sourceMappingIndex']); |
| 801 } | 767 } |
| 802 } | 768 } |
| OLD | NEW |