patchApply function
Merge a set of patches onto the text.
Return a patched text, as well as an array of true/false values indicating which patches were applied.
patches
is a List of Patch objectstext
is the old text.
Returns a two element List, containing the new text and a List of bool values.
Implementation
List patchApply(List<Patch> patches, String text,
{double deleteThreshold: 0.5, double diffTimeout: 1.0,
DateTime? diffDeadline, double matchThreshold: 0.5,
int matchDistance: 1000, int margin: 4}) {
if (patches.isEmpty) {
return [text, []];
}
// Deep copy the patches so that no changes are made to originals.
patches = patchDeepCopy(patches);
final nullPadding = patchAddPadding(patches, margin: margin);
text = '$nullPadding$text$nullPadding';
patchSplitMax(patches, margin: margin);
final text_buffer = new StringBuffer();
int x = 0;
// delta keeps track of the offset between the expected and actual location
// of the previous patch. If there are patches expected at positions 10 and
// 20, but the first patch was found at 12, delta is 2 and the second patch
// has an effective expected position of 22.
int delta = 0;
final results = new List<bool>.filled(patches.length, false);
for (Patch aPatch in patches) {
int expected_loc = aPatch.start2 + delta;
String text1 = diffText1(aPatch.diffs);
int start_loc;
int end_loc = -1;
if (text1.length > BITS_PER_INT) {
// patch_splitMax will only provide an oversized pattern in the case of
// a monster delete.
start_loc = match(text, text1.substring(0, BITS_PER_INT), expected_loc,
threshold: matchThreshold, distance: matchDistance);
if (start_loc != -1) {
end_loc = match(text,
text1.substring(text1.length - BITS_PER_INT),
expected_loc + text1.length - BITS_PER_INT,
threshold: matchThreshold, distance: matchDistance);
if (end_loc == -1 || start_loc >= end_loc) {
// Can't find valid trailing context. Drop this patch.
start_loc = -1;
}
}
} else {
start_loc = match(text, text1, expected_loc,
threshold: matchThreshold, distance: matchDistance);
}
if (start_loc == -1) {
// No match found. :(
results[x] = false;
// Subtract the delta for this failed patch from subsequent patches.
delta -= aPatch.length2 - aPatch.length1;
} else {
// Found a match. :)
results[x] = true;
delta = start_loc - expected_loc;
String text2;
if (end_loc == -1) {
text2 = text.substring(start_loc,
min(start_loc + text1.length, text.length));
} else {
text2 = text.substring(start_loc,
min(end_loc + BITS_PER_INT, text.length));
}
if (text1 == text2) {
// Perfect match, just shove the replacement text in.
text_buffer.clear();
text_buffer
..write(text.substring(0, start_loc))
..write(diffText2(aPatch.diffs))
..write(text.substring(start_loc + text1.length));
text = text_buffer.toString();
} else {
// Imperfect match. Run a diff to get a framework of equivalent
// indices.
final diffs = diff(text1, text2, checklines: false,
deadline: diffDeadline, timeout: diffTimeout);
if ((text1.length > BITS_PER_INT)
&& (levenshtein(diffs) / text1.length > deleteThreshold)) {
// The end points match, but the content is unacceptably bad.
results[x] = false;
} else {
cleanupSemanticLossless(diffs);
int index1 = 0;
for (Diff aDiff in aPatch.diffs) {
if (aDiff.operation != DIFF_EQUAL) {
int index2 = diffXIndex(diffs, index1);
if (aDiff.operation == DIFF_INSERT) {
// Insertion
text_buffer.clear();
text_buffer
..write(text.substring(0, start_loc + index2))
..write(aDiff.text)
..write(text.substring(start_loc + index2));
text = text_buffer.toString();
} else if (aDiff.operation == DIFF_DELETE) {
// Deletion
text_buffer.clear();
text_buffer
..write(text.substring(0, start_loc + index2))
..write(text.substring(start_loc + diffXIndex(diffs,
index1 + aDiff.text.length)));
text = text_buffer.toString();
}
}
if (aDiff.operation != DIFF_DELETE) {
index1 += aDiff.text.length;
}
}
}
}
}
x++;
}
// Strip the padding off.
text = text.substring(nullPadding.length, text.length - nullPadding.length);
return [text, results];
}