|
|
|
@ -121,23 +121,7 @@ public abstract class DiffAlgorithm {
|
|
|
|
|
Subsequence<S> as = Subsequence.a(a, region); |
|
|
|
|
Subsequence<S> bs = Subsequence.b(b, region); |
|
|
|
|
EditList e = Subsequence.toBase(diffNonCommon(cs, as, bs), as, bs); |
|
|
|
|
|
|
|
|
|
// The last insertion may need to be shifted later if it
|
|
|
|
|
// inserts elements that were previously reduced out as
|
|
|
|
|
// common at the end.
|
|
|
|
|
//
|
|
|
|
|
Edit last = e.get(e.size() - 1); |
|
|
|
|
if (last.getType() == Edit.Type.INSERT) { |
|
|
|
|
while (last.endB < b.size() |
|
|
|
|
&& cmp.equals(b, last.beginB, b, last.endB)) { |
|
|
|
|
last.beginA++; |
|
|
|
|
last.endA++; |
|
|
|
|
last.beginB++; |
|
|
|
|
last.endB++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return e; |
|
|
|
|
return normalize(cmp, e, a, b); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
case EMPTY: |
|
|
|
@ -152,6 +136,107 @@ public abstract class DiffAlgorithm {
|
|
|
|
|
return new Edit(0, a.size(), 0, b.size()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Reorganize an {@link EditList} for better diff consistency. |
|
|
|
|
* <p> |
|
|
|
|
* {@code DiffAlgorithms} may return {@link Edit.Type#INSERT} or |
|
|
|
|
* {@link Edit.Type#DELETE} edits that can be "shifted". For |
|
|
|
|
* example, the deleted section |
|
|
|
|
* <pre> |
|
|
|
|
* -a |
|
|
|
|
* -b |
|
|
|
|
* -c |
|
|
|
|
* a |
|
|
|
|
* b |
|
|
|
|
* c |
|
|
|
|
* </pre> |
|
|
|
|
* can be shifted down by 1, 2 or 3 locations. |
|
|
|
|
* <p> |
|
|
|
|
* To avoid later merge issues, we shift such edits to a |
|
|
|
|
* consistent location. {@code normalize} uses a simple strategy of |
|
|
|
|
* shifting such edits to their latest possible location. |
|
|
|
|
* <p> |
|
|
|
|
* This strategy may not always produce an aesthetically pleasing |
|
|
|
|
* diff. For instance, it works well with |
|
|
|
|
* <pre> |
|
|
|
|
* function1 { |
|
|
|
|
* ... |
|
|
|
|
* } |
|
|
|
|
* |
|
|
|
|
* +function2 { |
|
|
|
|
* + ... |
|
|
|
|
* +} |
|
|
|
|
* + |
|
|
|
|
* function3 { |
|
|
|
|
* ... |
|
|
|
|
* } |
|
|
|
|
* </pre> |
|
|
|
|
* but less so for |
|
|
|
|
* <pre> |
|
|
|
|
* # |
|
|
|
|
* # comment1 |
|
|
|
|
* # |
|
|
|
|
* function1() { |
|
|
|
|
* } |
|
|
|
|
* |
|
|
|
|
* # |
|
|
|
|
* +# comment3 |
|
|
|
|
* +# |
|
|
|
|
* +function3() { |
|
|
|
|
* +} |
|
|
|
|
* + |
|
|
|
|
* +# |
|
|
|
|
* # comment2 |
|
|
|
|
* # |
|
|
|
|
* function2() { |
|
|
|
|
* } |
|
|
|
|
* </pre> |
|
|
|
|
* <a href="https://github.com/mhagger/diff-slider-tools">More |
|
|
|
|
* sophisticated strategies</a> are possible, say by calculating a |
|
|
|
|
* suitable "aesthetic cost" for each possible position and using |
|
|
|
|
* the lowest cost, but {@code normalize} just shifts edits |
|
|
|
|
* to the end as much as possible. |
|
|
|
|
* |
|
|
|
|
* @param <S> |
|
|
|
|
* type of sequence being compared. |
|
|
|
|
* @param cmp |
|
|
|
|
* the comparator supplying the element equivalence function. |
|
|
|
|
* @param e |
|
|
|
|
* a modifiable edit list comparing the provided sequences. |
|
|
|
|
* @param a |
|
|
|
|
* the first (also known as old or pre-image) sequence. |
|
|
|
|
* @param b |
|
|
|
|
* the second (also known as new or post-image) sequence. |
|
|
|
|
* @return a modifiable edit list with edit regions shifted to their |
|
|
|
|
* latest possible location. The result list is never null. |
|
|
|
|
* @since 4.7 |
|
|
|
|
*/ |
|
|
|
|
private static <S extends Sequence> EditList normalize( |
|
|
|
|
SequenceComparator<? super S> cmp, EditList e, S a, S b) { |
|
|
|
|
Edit prev = null; |
|
|
|
|
for (int i = e.size() - 1; i >= 0; i--) { |
|
|
|
|
Edit cur = e.get(i); |
|
|
|
|
Edit.Type curType = cur.getType(); |
|
|
|
|
|
|
|
|
|
int maxA = (prev == null) ? a.size() : prev.beginA; |
|
|
|
|
int maxB = (prev == null) ? b.size() : prev.beginB; |
|
|
|
|
|
|
|
|
|
if (curType == Edit.Type.INSERT) { |
|
|
|
|
while (cur.endA < maxA && cur.endB < maxB |
|
|
|
|
&& cmp.equals(b, cur.beginB, b, cur.endB)) { |
|
|
|
|
cur.shift(1); |
|
|
|
|
} |
|
|
|
|
} else if (curType == Edit.Type.DELETE) { |
|
|
|
|
while (cur.endA < maxA && cur.endB < maxB |
|
|
|
|
&& cmp.equals(a, cur.beginA, a, cur.endA)) { |
|
|
|
|
cur.shift(1); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
prev = cur; |
|
|
|
|
} |
|
|
|
|
return e; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Compare two sequences and identify a list of edits between them. |
|
|
|
|
* |
|
|
|
|