Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(481)

Side by Side Diff: tests/compiler/dart2js/sourcemaps/diff.dart

Issue 1678043003: Add Dart code to diff_view (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Update status. Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tests/compiler/dart2js/dart2js.status ('k') | tests/compiler/dart2js/sourcemaps/diff_view.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library sourcemap.diff;
6
7 import 'package:compiler/src/io/source_file.dart';
8
9 import 'html_parts.dart';
10 import 'output_structure.dart';
11 import 'sourcemap_helper.dart';
12
13 enum DiffKind {
14 UNMATCHED,
15 MATCHING,
16 IDENTICAL,
17 }
18
19 /// A list of columns that should align in output.
20 class DiffBlock {
21 final DiffKind kind;
22 List<List<HtmlPart>> columns = <List<HtmlPart>>[];
23
24 DiffBlock(this.kind);
25
26 void addColumn(int index, List<HtmlPart> lines) {
27 if (index >= columns.length) {
28 columns.length = index + 1;
29 }
30 columns[index] = lines;
31 }
32
33 List<HtmlPart> getColumn(int index) {
34 List<HtmlPart> lines;
35 if (index < columns.length) {
36 lines = columns[index];
37 }
38 return lines != null ? lines : const <HtmlPart>[];
39 }
40 }
41
42
43 /// Align the content of [list1] and [list2].
44 ///
45 /// If provided, [range1] and [range2] aligned the subranges of [list1] and
46 /// [list2], otherwise the whole lists are aligned.
47 ///
48 /// If provided, [match] determines the equality between members of [list1] and
49 /// [list2], otherwise `==` is used.
50 ///
51 /// [handleSkew] is called when a subrange of one list is not found in the
52 /// other.
53 ///
54 /// [handleMatched] is called when two indices match up.
55 ///
56 /// [handleUnmatched] is called when two indices don't match up (none are found
57 /// in the other list).
58 void align(List list1,
59 List list2,
60 {Interval range1,
61 Interval range2,
62 bool match(a, b),
63 void handleSkew(int listIndex, Interval range),
64 void handleMatched(List<int> indices),
65 void handleUnmatched(List<int> indices)}) {
66 if (match == null) {
67 match = (a, b) => a == b;
68 }
69
70 if (range1 == null) {
71 range1 = new Interval(0, list1.length);
72 }
73 if (range2 == null) {
74 range2 = new Interval(0, list2.length);
75 }
76
77 Interval findInOther(
78 List thisLines, Interval thisRange,
79 List otherLines, Interval otherRange) {
80 for (int index = otherRange.from; index < otherRange.to; index++) {
81 if (match(thisLines[thisRange.from], otherLines[index])) {
82 int offset = 1;
83 while (thisRange.from + offset < thisRange.to &&
84 otherRange.from + offset < otherRange.to &&
85 match(thisLines[thisRange.from + offset],
86 otherLines[otherRange.from + offset])) {
87 offset++;
88 }
89 return new Interval(index, index + offset);
90 }
91 }
92 return null;
93 }
94
95 int start1 = range1.from;
96 int end1 = range1.to;
97 int start2 = range2.from;
98 int end2 = range2.to;
99
100 const int ALIGN1 = -1;
101 const int UNMATCHED = 0;
102 const int ALIGN2 = 1;
103
104 while (start1 < end1 && start2 < end2) {
105 if (match(list1[start1], list2[start2])) {
106 handleMatched([start1++, start2++]);
107 } else {
108 Interval subrange1 = new Interval(start1, end1);
109 Interval subrange2 = new Interval(start2, end2);
110 Interval element2inList1 =
111 findInOther(list1, subrange1, list2, subrange2);
112 Interval element1inList2 =
113 findInOther(list2, subrange2, list1, subrange1);
114 int choice = 0;
115 if (element2inList1 != null) {
116 if (element1inList2 != null) {
117 if (element1inList2.length > 1 && element2inList1.length > 1) {
118 choice =
119 element2inList1.from < element1inList2.from ? ALIGN2 : ALIGN1;
120 } else if (element2inList1.length > 1) {
121 choice = ALIGN2;
122 } else if (element1inList2.length > 1) {
123 choice = ALIGN1;
124 } else {
125 choice =
126 element2inList1.from < element1inList2.from ? ALIGN2 : ALIGN1;
127 }
128 } else {
129 choice = ALIGN2;
130 }
131 } else if (element1inList2 != null) {
132 choice = ALIGN1;
133 }
134 switch (choice) {
135 case ALIGN1:
136 handleSkew(0, new Interval(start1, element1inList2.from));
137 start1 = element1inList2.from;
138 break;
139 case ALIGN2:
140 handleSkew(1, new Interval(start2, element2inList1.from));
141 start2 = element2inList1.from;
142 break;
143 case UNMATCHED:
144 handleUnmatched([start1++, start2++]);
145 break;
146 }
147 }
148 }
149 if (start1 < end1) {
150 handleSkew(0, new Interval(start1, end1));
151 }
152 if (start2 < end2) {
153 handleSkew(1, new Interval(start2, end2));
154 }
155 }
156
157 /// Create a list of blocks containing the diff of the two output [structures]
158 /// and the corresponding Dart code.
159 List<DiffBlock> createDiffBlocks(
160 List<OutputStructure> structures,
161 SourceFileManager sourceFileManager) {
162 return new DiffCreator(structures, sourceFileManager).computeBlocks();
163 }
164
165 class DiffCreator {
166 final List<OutputStructure> structures;
167 final SourceFileManager sourceFileManager;
168
169 List<List<CodeLine>> inputLines;
170
171 List<int> nextInputLine = [0, 0];
172
173 List<DiffBlock> blocks = <DiffBlock>[];
174
175 DiffCreator(List<OutputStructure> structures, this.sourceFileManager)
176 : this.structures = structures,
177 this.inputLines = structures.map((s) => s.lines).toList();
178
179 CodeSource codeSourceFromEntities(Iterable<OutputEntity> entities) {
180 for (OutputEntity entity in entities) {
181 if (entity.codeSource != null) {
182 return entity.codeSource;
183 }
184 }
185 return null;
186 }
187
188 /// Checks that lines are added in sequence without gaps or duplicates.
189 void checkLineInvariant(int index, Interval range) {
190 int expectedLineNo = nextInputLine[index];
191 if (range.from != expectedLineNo) {
192 print('Expected line no $expectedLineNo, found ${range.from}');
193 if (range.from < expectedLineNo) {
194 print('Duplicate lines:');
195 int i = range.from;
196 while (i <= expectedLineNo) {
197 print(inputLines[index][i++].code);
198 }
199 } else {
200 print('Missing lines:');
201 int i = expectedLineNo;
202 while (i <= range.from) {
203 print(inputLines[index][i++].code);
204 }
205 }
206 }
207 nextInputLine[index] = range.to;
208 }
209
210 /// Creates a block containing the code lines in [range] from input number
211 /// [index]. If [codeSource] is provided, the block will contain a
212 /// corresponding Dart code column.
213 void handleSkew(int index, Interval range, [CodeSource codeSource]) {
214 DiffBlock block = new DiffBlock(DiffKind.UNMATCHED);
215 checkLineInvariant(index, range);
216 block.addColumn(index, inputLines[index].sublist(range.from, range.to));
217 if (codeSource != null) {
218 block.addColumn(2,
219 codeLinesFromCodeSource(sourceFileManager, codeSource));
220 }
221 blocks.add(block);
222 }
223
224 /// Create a block containing the code lines in [ranges] from the
225 /// corresponding JavaScript inputs. If [codeSource] is provided, the block
226 /// will contain a corresponding Dart code column.
227 void addLines(DiffKind kind, List<Interval> ranges, [CodeSource codeSource]) {
228 DiffBlock block = new DiffBlock(kind);
229 for (int i = 0; i < ranges.length; i++) {
230 checkLineInvariant(i, ranges[i]);
231 block.addColumn(i, inputLines[i].sublist(ranges[i].from, ranges[i].to));
232 }
233 if (codeSource != null) {
234 block.addColumn(2,
235 codeLinesFromCodeSource(sourceFileManager, codeSource));
236 }
237 blocks.add(block);
238 }
239
240 /// Merge the code lines in [range1] and [range2] of the corresponding input.
241 void addRaw(Interval range1, Interval range2) {
242 match(a, b) => a.code == b.code;
243
244 List<Interval> currentMatchedIntervals;
245 List<Interval> currentUnmatchedIntervals;
246
247 void flushMatching() {
248 if (currentMatchedIntervals != null) {
249 addLines(DiffKind.IDENTICAL, currentMatchedIntervals);
250 }
251 currentMatchedIntervals = null;
252 }
253
254 void flushUnmatched() {
255 if (currentUnmatchedIntervals != null) {
256 addLines(DiffKind.UNMATCHED, currentUnmatchedIntervals);
257 }
258 currentUnmatchedIntervals = null;
259 }
260
261 List<Interval> updateIntervals(List<Interval> current, List<int> indices) {
262 if (current == null) {
263 return [
264 new Interval(indices[0], indices[0] + 1),
265 new Interval(indices[1], indices[1] + 1)];
266 } else {
267 current[0] =
268 new Interval(current[0].from, indices[0] + 1);
269 current[1] =
270 new Interval(current[1].from, indices[1] + 1);
271 return current;
272 }
273 }
274
275 align(
276 inputLines[0],
277 inputLines[1],
278 range1: range1,
279 range2: range2,
280 match: match,
281 handleSkew: (int listIndex, Interval range) {
282 flushMatching();
283 flushUnmatched();
284 handleSkew(listIndex, range);
285 },
286 handleMatched: (List<int> indices) {
287 flushUnmatched();
288 currentMatchedIntervals =
289 updateIntervals(currentMatchedIntervals, indices);
290 },
291 handleUnmatched: (List<int> indices) {
292 flushMatching();
293 currentUnmatchedIntervals =
294 updateIntervals(currentUnmatchedIntervals, indices);
295 });
296
297 flushMatching();
298 flushUnmatched();
299 }
300
301 /// Adds the top level blocks in [childRange] for structure [index].
302 void addBlock(int index, Interval childRange) {
303 addSkewedChildren(index, structures[index], childRange);
304 }
305
306 /// Adds the [entity] from structure [index]. If the [entity] supports child
307 /// entities, these are process individually. Otherwise the lines from
308 /// [entity] are added directly.
309 void addSkewedEntity(int index, OutputEntity entity) {
310 if (entity.canHaveChildren) {
311 handleSkew(index, entity.header);
312 addSkewedChildren(
313 index, entity, new Interval(0, entity.children.length));
314 handleSkew(index, entity.footer);
315 } else {
316 handleSkew(index, entity.interval, codeSourceFromEntities([entity]));
317 }
318 }
319
320 /// Adds the children of [parent] in [childRange] from structure [index].
321 void addSkewedChildren(int index, OutputEntity parent, Interval childRange) {
322 for (int i = childRange.from; i < childRange.to; i++) {
323 addSkewedEntity(index, parent.getChild(i));
324 }
325 }
326
327 /// Adds the members of the [classes] aligned.
328 void addMatchingContainers(List<OutputEntity> classes) {
329 addLines(DiffKind.MATCHING, classes.map((c) => c.header).toList());
330 align(classes[0].children, classes[1].children,
331 match: (a, b) => a.name == b.name,
332 handleSkew: (int listIndex, Interval childRange) {
333 addSkewedChildren(listIndex, classes[listIndex], childRange);
334 },
335 handleMatched: (List<int> indices) {
336 List<BasicEntity> entities = [
337 classes[0].getChild(indices[0]),
338 classes[1].getChild(indices[1])];
339 if (entities.every((e) => e is Statics)) {
340 addMatchingContainers(entities);
341 } else {
342 addLines(DiffKind.MATCHING,
343 entities.map((e) => e.interval).toList(),
344 codeSourceFromEntities(entities));
345 }
346 },
347 handleUnmatched: (List<int> indices) {
348 List<Interval> intervals = [
349 classes[0].getChild(indices[0]).interval,
350 classes[1].getChild(indices[1]).interval];
351 addLines(DiffKind.UNMATCHED, intervals);
352 });
353 addLines(DiffKind.MATCHING, classes.map((c) => c.footer).toList());
354 }
355
356 /// Adds the library blocks in [indices] from the corresponding
357 /// [OutputStructure]s, aligning their content.
358 void addMatchingBlocks(List<int> indices) {
359 List<LibraryBlock> blocks = [
360 structures[0].getChild(indices[0]),
361 structures[1].getChild(indices[1])];
362
363 addLines(DiffKind.MATCHING, blocks.map((b) => b.header).toList());
364 align(blocks[0].children, blocks[1].children,
365 match: (a, b) => a.name == b.name,
366 handleSkew: (int listIndex, Interval childRange) {
367 addSkewedChildren(
368 listIndex, blocks[listIndex], childRange);
369 },
370 handleMatched: (List<int> indices) {
371 List<BasicEntity> entities = [
372 blocks[0].getChild(indices[0]),
373 blocks[1].getChild(indices[1])];
374 if (entities.every((e) => e is LibraryClass)) {
375 addMatchingContainers(entities);
376 } else {
377 addLines(
378 DiffKind.MATCHING,
379 entities.map((e) => e.interval).toList(),
380 codeSourceFromEntities(entities));
381 }
382 },
383 handleUnmatched: (List<int> indices) {
384 List<Interval> intervals = [
385 blocks[0].getChild(indices[0]).interval,
386 blocks[1].getChild(indices[1]).interval];
387 addLines(DiffKind.UNMATCHED, intervals);
388 });
389 addLines(DiffKind.MATCHING, blocks.map((b) => b.footer).toList());
390 }
391
392 /// Adds the lines of the blocks in [indices] from the corresponding
393 /// [OutputStructure]s.
394 void addUnmatchedBlocks(List<int> indices) {
395 List<LibraryBlock> blocks = [
396 structures[0].getChild(indices[0]),
397 structures[1].getChild(indices[1])];
398 addLines(DiffKind.UNMATCHED, [blocks[0].interval, blocks[1].interval]);
399 }
400
401 /// Computes the diff blocks for [OutputStructure]s.
402 List<DiffBlock> computeBlocks() {
403 addRaw(structures[0].header, structures[1].header);
404
405 align(structures[0].children,
406 structures[1].children,
407 match: (a, b) => a.name == b.name,
408 handleSkew: addBlock,
409 handleMatched: addMatchingBlocks,
410 handleUnmatched: addUnmatchedBlocks);
411
412 addRaw(structures[0].footer, structures[1].footer);
413
414 return blocks;
415 }
416 }
417
418 /// Creates html lines for code lines in [codeSource]. [sourceFileManager] is
419 /// used to read that text from the source URIs.
420 List<HtmlPart> codeLinesFromCodeSource(
421 SourceFileManager sourceFileManager,
422 CodeSource codeSource) {
423 List<HtmlPart> lines = <HtmlPart>[];
424 SourceFile sourceFile = sourceFileManager.getSourceFile(codeSource.uri);
425 String elementName = codeSource.name;
426 HtmlLine line = new HtmlLine();
427 line.htmlParts.add(new ConstHtmlPart('<span class="comment">'));
428 line.htmlParts.add(new HtmlText(
429 '${elementName}: ${sourceFile.filename}'));
430 line.htmlParts.add(new ConstHtmlPart('</span>'));
431 lines.add(line);
432 if (codeSource.begin != null) {
433 int startLine = sourceFile.getLine(codeSource.begin);
434 int endLine = sourceFile.getLine(codeSource.end);
435 for (int lineNo = startLine; lineNo <= endLine; lineNo++) {
436 String text = sourceFile.getLineText(lineNo);
437 CodeLine codeLine = new CodeLine(lineNo, sourceFile.getOffset(lineNo, 0));
438 codeLine.codeBuffer.write(text);
439 codeLine.htmlParts.add(new HtmlText(text));
440 lines.add(codeLine);
441 }
442 }
443 return lines;
444 }
OLDNEW
« no previous file with comments | « tests/compiler/dart2js/dart2js.status ('k') | tests/compiler/dart2js/sourcemaps/diff_view.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698