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