diff_cleanupMerge method
Reorder and merge like edit sections. Merge equalities.
Any edit section can move as long as it doesn't cross an equality.
diffs
is a List of Diff objects.
Implementation
void diff_cleanupMerge(List<Diff> diffs) {
diffs.add(Diff(Operation.equal, '')); // Add a dummy entry at the end.
int pointer = 0;
int count_delete = 0;
int count_insert = 0;
String text_delete = '';
String text_insert = '';
int commonlength;
while (pointer < diffs.length) {
switch (diffs[pointer].operation) {
case Operation.insert:
count_insert++;
text_insert += diffs[pointer].text;
pointer++;
break;
case Operation.delete:
count_delete++;
text_delete += diffs[pointer].text;
pointer++;
break;
case Operation.equal:
// Upon reaching an equality, check for prior redundancies.
if (count_delete + count_insert > 1) {
if (count_delete != 0 && count_insert != 0) {
// Factor out any common prefixies.
commonlength = diff_commonPrefix(text_insert, text_delete);
if (commonlength != 0) {
if ((pointer - count_delete - count_insert) > 0 &&
diffs[pointer - count_delete - count_insert - 1]
.operation ==
Operation.equal) {
final i = pointer - count_delete - count_insert - 1;
diffs[i].text =
diffs[i].text + text_insert.substring(0, commonlength);
} else {
diffs.insert(
0,
Diff(Operation.equal,
text_insert.substring(0, commonlength)));
pointer++;
}
text_insert = text_insert.substring(commonlength);
text_delete = text_delete.substring(commonlength);
}
// Factor out any common suffixies.
commonlength = diff_commonSuffix(text_insert, text_delete);
if (commonlength != 0) {
diffs[pointer].text =
text_insert.substring(text_insert.length - commonlength) +
diffs[pointer].text;
text_insert =
text_insert.substring(0, text_insert.length - commonlength);
text_delete =
text_delete.substring(0, text_delete.length - commonlength);
}
}
// Delete the offending records and add the merged ones.
pointer -= count_delete + count_insert;
diffs.removeRange(pointer, pointer + count_delete + count_insert);
if (text_delete.isNotEmpty) {
diffs.insert(pointer, Diff(Operation.delete, text_delete));
pointer++;
}
if (text_insert.isNotEmpty) {
diffs.insert(pointer, Diff(Operation.insert, text_insert));
pointer++;
}
pointer++;
} else if (pointer != 0 &&
diffs[pointer - 1].operation == Operation.equal) {
// Merge this equality with the previous one.
diffs[pointer - 1].text =
diffs[pointer - 1].text + diffs[pointer].text;
diffs.removeAt(pointer);
} else {
pointer++;
}
count_insert = 0;
count_delete = 0;
text_delete = '';
text_insert = '';
break;
}
}
if (diffs.last.text.isEmpty) {
diffs.removeLast(); // Remove the dummy entry at the end.
}
// Second pass: look for single edits surrounded on both sides by equalities
// which can be shifted sideways to eliminate an equality.
// e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
bool changes = false;
pointer = 1;
// Intentionally ignore the first and last element (don't need checking).
while (pointer < diffs.length - 1) {
if (diffs[pointer - 1].operation == Operation.equal &&
diffs[pointer + 1].operation == Operation.equal) {
// This is a single edit surrounded by equalities.
if (diffs[pointer].text.endsWith(diffs[pointer - 1].text)) {
// Shift the edit over the previous equality.
diffs[pointer].text = diffs[pointer - 1].text +
diffs[pointer].text.substring(0,
diffs[pointer].text.length - diffs[pointer - 1].text.length);
diffs[pointer + 1].text =
diffs[pointer - 1].text + diffs[pointer + 1].text;
diffs.removeAt(pointer - 1);
changes = true;
} else if (diffs[pointer].text.startsWith(diffs[pointer + 1].text)) {
// Shift the edit over the next equality.
diffs[pointer - 1].text =
diffs[pointer - 1].text + diffs[pointer + 1].text;
diffs[pointer].text =
diffs[pointer].text.substring(diffs[pointer + 1].text.length) +
diffs[pointer + 1].text;
diffs.removeAt(pointer + 1);
changes = true;
}
}
pointer++;
}
// If shifts were made, the diff needs reordering and another shift sweep.
if (changes) {
diff_cleanupMerge(diffs);
}
}