patchMake function

List<Patch> patchMake(
  1. Object? a, {
  2. Object? b,
  3. Object? c,
  4. double diffTimeout = 1.0,
  5. DateTime? diffDeadline,
  6. int diffEditCost = 4,
  7. double deleteThreshold = 0.5,
  8. int margin = 4,
})

Compute a List of Patches to turn text1 into text2.

Use diffs if provided, otherwise compute it ourselves. There are four ways to call this function, depending on what data is available to the caller:

  • Method 1: a = text1, b = text2
  • Method 2: a = diffs
  • Method 3 (optimal): a = text1, b = diffs
  • Method 4 (deprecated, use method 3): a = text1, b = text2, c = diffs

Returns a List of Patch objects.

Implementation

List<Patch> patchMake(Object? a, {Object? b, Object? c, double diffTimeout: 1.0, DateTime? diffDeadline,
  int diffEditCost: 4, double deleteThreshold: 0.5, int margin: 4}) {

  String text1;
  List<Diff> diffs;
  if (a is String && b is String && c == null) {
    // Method 1: text1, text2
    // Compute diffs from text1 and text2.
    text1 = a;
    diffs = diff(text1, b, checklines: true, timeout: diffTimeout,
                 deadline: diffDeadline);
    if (diffs.length > 2) {
      cleanupSemantic(diffs);
      cleanupEfficiency(diffs, diffEditCost);
    }
  } else if (a is List<Diff> && b == null && c == null) {
    // Method 2: diffs
    // Compute text1 from diffs.
    diffs = a;
    text1 = diffText1(diffs);
  } else if (a is String && b is List<Diff> && c == null) {
    // Method 3: text1, diffs
    text1 = a;
    diffs = b;
  } else if (a is String && b is String && c is List<Diff>) {
    // Method 4: text1, text2, diffs
    // text2 is not used.
    text1 = a;
    diffs = c;
  } else {
    throw new ArgumentError('Unknown call format to patch_make.');
  }

  final patches = <Patch>[];
  if (diffs.isEmpty) {
    return patches;  // Get rid of the null case.
  }
  Patch patch = new Patch();
  final postpatch_buffer = new StringBuffer();
  int char_count1 = 0;  // Number of characters into the text1 string.
  int char_count2 = 0;  // Number of characters into the text2 string.
  // Start with text1 (prepatch_text) and apply the diffs until we arrive at
  // text2 (postpatch_text). We recreate the patches one by one to determine
  // context info.
  String prepatch_text = text1;
  String postpatch_text = text1;
  for (Diff aDiff in diffs) {
    if (patch.diffs.isEmpty && aDiff.operation != DIFF_EQUAL) {
      // A new patch starts here.
      patch.start1 = char_count1;
      patch.start2 = char_count2;
    }

    switch (aDiff.operation) {
    case DIFF_INSERT:
      patch.diffs.add(aDiff);
      patch.length2 += aDiff.text.length;
      postpatch_buffer.clear();
      postpatch_buffer
      ..write(postpatch_text.substring(0, char_count2))
      ..write(aDiff.text)
      ..write(postpatch_text.substring(char_count2));
      postpatch_text = postpatch_buffer.toString();
      break;
    case DIFF_DELETE:
      patch.length1 += aDiff.text.length;
      patch.diffs.add(aDiff);
      postpatch_buffer.clear();
      postpatch_buffer
      ..write(postpatch_text.substring(0, char_count2))
      ..write(postpatch_text.substring(char_count2 + aDiff.text.length));
      postpatch_text = postpatch_buffer.toString();
      break;
    case DIFF_EQUAL:
      if (aDiff.text.length <= 2 * margin
          && !patch.diffs.isEmpty && aDiff != diffs.last) {
        // Small equality inside a patch.
        patch.diffs.add(aDiff);
        patch.length1 += aDiff.text.length;
        patch.length2 += aDiff.text.length;
      }

      if (aDiff.text.length >= 2 * margin) {
        // Time for a new patch.
        if (!patch.diffs.isEmpty) {
          patchAddContext(patch, prepatch_text, margin);
          patches.add(patch);
          patch = new Patch();
          // Unlike Unidiff, our patch lists have a rolling context.
          // http://code.google.com/p/google-diff-match-patch/wiki/Unidiff
          // Update prepatch text & pos to reflect the application of the
          // just completed patch.
          prepatch_text = postpatch_text;
          char_count1 = char_count2;
        }
      }
      break;
    }

    // Update the current character count.
    if (aDiff.operation != DIFF_INSERT) {
      char_count1 += aDiff.text.length;
    }
    if (aDiff.operation != DIFF_DELETE) {
      char_count2 += aDiff.text.length;
    }
  }
  // Pick up the leftover patch if not empty.
  if (!patch.diffs.isEmpty) {
    patchAddContext(patch, prepatch_text, margin);
    patches.add(patch);
  }

  return patches;
}