patchApply function

List patchApply(
  1. List<Patch> patches,
  2. String text, {
  3. double deleteThreshold = 0.5,
  4. double diffTimeout = 1.0,
  5. DateTime? diffDeadline,
  6. double matchThreshold = 0.5,
  7. int matchDistance = 1000,
  8. int margin = 4,
})

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 objects
  • text 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];
}