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 |