Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A little cleanup to vendored diff_match_patch #1330

Merged
merged 1 commit into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion lib/src/third_party/diff_match_patch/README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
# Diff-Match-Patch

This is code was forked from google's [diff-match-patch](https://github.com/google/diff-match-patch) library.
This code was forked from Google's [diff-match-patch](https://github.com/google/diff-match-patch) library.

## Modifications made in code

- Code was updated for modern Dart naming standards and language features.
- `diff` - Code related to diff functionality was retained and ported to [null-safety](https://dart.dev/null-safety) along with linting.
- `Levenshtein distance` - Levenshtein distance which was calculated initially based on the number of characters modified was altered to calculate based on the number of words modified.

## Source

Repository - [link](https://github.com/google/diff-match-patch)
Git revision - [a6367d7](https://github.com/google/diff-match-patch/commit/a6367d7866833ac037fbdefcdbcbee4def86e326)
154 changes: 76 additions & 78 deletions lib/src/third_party/diff_match_patch/diff.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ double diffTimeout = 1.0;
/// off the texts before diffing.
/// [text1] is the old string to be diffed.
/// [text2] is the new string to be diffed.
/// [checklines] is an optional speedup flag. If present and false, then don't
/// [checkLines] is an optional speedup flag. If present and false, then don't
/// run a line-level diff first to identify the changed areas.
/// Defaults to true, which does a faster, slightly less optimal diff.
/// [deadline] is an optional time when the diff should be complete by. Used
Expand All @@ -79,7 +79,7 @@ double diffTimeout = 1.0;
List<Diff> diffMain(
String text1,
String text2, {
bool checklines = true,
bool checkLines = true,
DateTime? deadline,
}) {
// Set a deadline by which time the diff must be complete.
Expand All @@ -104,26 +104,26 @@ List<Diff> diffMain(
}

// Trim off common prefix (speedup).
var commonlength = diffCommonPrefix(text1, text2);
var commonprefix = text1.substring(0, commonlength);
text1 = text1.substring(commonlength);
text2 = text2.substring(commonlength);
var commonLength = diffCommonPrefix(text1, text2);
var commonPrefix = text1.substring(0, commonLength);
text1 = text1.substring(commonLength);
text2 = text2.substring(commonLength);

// Trim off common suffix (speedup).
commonlength = diffCommonSuffix(text1, text2);
var commonsuffix = text1.substring(text1.length - commonlength);
text1 = text1.substring(0, text1.length - commonlength);
text2 = text2.substring(0, text2.length - commonlength);
commonLength = diffCommonSuffix(text1, text2);
var commonSuffix = text1.substring(text1.length - commonLength);
text1 = text1.substring(0, text1.length - commonLength);
text2 = text2.substring(0, text2.length - commonLength);

// Compute the diff on the middle block.
diffs = diffCompute(text1, text2, checklines, deadline);
diffs = diffCompute(text1, text2, checkLines, deadline);

// Restore the prefix and suffix.
if (commonprefix.isNotEmpty) {
diffs.insert(0, Diff(Operation.equal, commonprefix));
if (commonPrefix.isNotEmpty) {
diffs.insert(0, Diff(Operation.equal, commonPrefix));
}
if (commonsuffix.isNotEmpty) {
diffs.add(Diff(Operation.equal, commonsuffix));
if (commonSuffix.isNotEmpty) {
diffs.add(Diff(Operation.equal, commonSuffix));
}

diffCleanupMerge(diffs);
Expand All @@ -135,7 +135,7 @@ List<Diff> diffMain(
/// Assumes that the texts do not have any common prefix or suffix.
/// [text1] is the old string to be diffed.
/// [text2] is the new string to be diffed.
/// [checklines] is a speedup flag. If false, then don't run a
/// [checkLines] is a speedup flag. If false, then don't run a
/// line-level diff first to identify the changed areas.
/// If true, then run a faster slightly less optimal diff.
/// [deadline] is the time when the diff should be complete by.
Expand All @@ -144,7 +144,7 @@ List<Diff> diffMain(
List<Diff> diffCompute(
String text1,
String text2,
bool checklines,
bool checkLines,
DateTime deadline,
) {
var diffs = <Diff>[];
Expand Down Expand Up @@ -199,14 +199,14 @@ List<Diff> diffCompute(
final diffsA = diffMain(
text1A,
text2A,
checklines: checklines,
checkLines: checkLines,
deadline: deadline,
);

final diffsB = diffMain(
textB,
text2B,
checklines: checklines,
checkLines: checkLines,
deadline: deadline,
);

Expand All @@ -217,7 +217,7 @@ List<Diff> diffCompute(
return diffs;
}

if (checklines && text1.length > 100 && text2.length > 100) {
if (checkLines && text1.length > 100 && text2.length > 100) {
return _diffLineMode(text1, text2, deadline);
}

Expand All @@ -237,7 +237,7 @@ void diffCleanupMerge(List<Diff> diffs) {
var countInsert = 0;
var textDelete = '';
var textInsert = '';
int commonlength;
int commonLength;

while (pointer < diffs.length) {
switch (diffs[pointer].operation) {
Expand All @@ -256,35 +256,35 @@ void diffCleanupMerge(List<Diff> diffs) {
if (countDelete + countInsert > 1) {
if (countDelete != 0 && countInsert != 0) {
// Factor out any common prefixes.
commonlength = diffCommonPrefix(textInsert, textDelete);
if (commonlength != 0) {
commonLength = diffCommonPrefix(textInsert, textDelete);
if (commonLength != 0) {
if ((pointer - countDelete - countInsert) > 0 &&
diffs[pointer - countDelete - countInsert - 1].operation ==
Operation.equal) {
final i = pointer - countDelete - countInsert - 1;
diffs[i].text =
diffs[i].text + textInsert.substring(0, commonlength);
diffs[i].text + textInsert.substring(0, commonLength);
} else {
diffs.insert(
0,
Diff(Operation.equal,
textInsert.substring(0, commonlength)));
textInsert.substring(0, commonLength)));
pointer++;
}
textInsert = textInsert.substring(commonlength);
textDelete = textDelete.substring(commonlength);
textInsert = textInsert.substring(commonLength);
textDelete = textDelete.substring(commonLength);
}

// Factor out any common suffixes.
commonlength = diffCommonSuffix(textInsert, textDelete);
if (commonlength != 0) {
commonLength = diffCommonSuffix(textInsert, textDelete);
if (commonLength != 0) {
diffs[pointer].text =
textInsert.substring(textInsert.length - commonlength) +
textInsert.substring(textInsert.length - commonLength) +
diffs[pointer].text;
textInsert =
textInsert.substring(0, textInsert.length - commonlength);
textInsert.substring(0, textInsert.length - commonLength);
textDelete =
textDelete.substring(0, textDelete.length - commonlength);
textDelete.substring(0, textDelete.length - commonLength);
}
}
// Delete the offending records and add the merged ones.
Expand Down Expand Up @@ -378,25 +378,25 @@ List<String>? diffHalfMatch(String text1, String text2) {
return null;
}

final longtext = text1.length > text2.length ? text1 : text2;
final shorttext = text1.length > text2.length ? text2 : text1;
final longText = text1.length > text2.length ? text1 : text2;
final shortText = text1.length > text2.length ? text2 : text1;

if (longtext.length < 4 || shorttext.length * 2 < longtext.length) {
if (longText.length < 4 || shortText.length * 2 < longText.length) {
return null; // Pointless.
}

// First check if the second quarter is the seed for a half-match.
final hm1 = _diffHalfMatchI(
longtext,
shorttext,
((longtext.length + 3) / 4).ceil().toInt(),
longText,
shortText,
((longText.length + 3) / 4).ceil().toInt(),
);

// Check again based on the third quarter.
final hm2 = _diffHalfMatchI(
longtext,
shorttext,
((longtext.length + 1) / 2).ceil().toInt(),
longText,
shortText,
((longText.length + 1) / 2).ceil().toInt(),
);

List<String>? hm;
Expand All @@ -420,7 +420,8 @@ List<String>? diffHalfMatch(String text1, String text2) {
}
}

/// Do a quick line-level diff on both strings, then rediff the parts for greater accuracy.
/// Do a quick line-level diff on both strings,
/// then rediff the parts for greater accuracy.
///
/// This speedup can produce non-minimal diffs.
/// [text1] is the old string to be diffed.
Expand All @@ -429,21 +430,17 @@ List<String>? diffHalfMatch(String text1, String text2) {
/// Returns a List of Diff objects.
List<Diff> _diffLineMode(String text1, String text2, DateTime deadline) {
// Scan the text on a line-by-line basis first.
final a = diffLinesToChars(text1, text2);

final linearray = a['lineArray'] as List<String>;
text1 = a['chars1'] as String;
text2 = a['chars2'] as String;
final (:chars1, :chars2, :lineArray) = diffLinesToChars(text1, text2);

final diffs = diffMain(
text1,
text2,
checklines: false,
chars1,
chars2,
checkLines: false,
deadline: deadline,
);

// Convert the diff back to original text.
diffCharsToLines(diffs, linearray);
diffCharsToLines(diffs, lineArray);
// Eliminate freak matches (e.g. blank lines)
diffCleanupSemantic(diffs);

Expand Down Expand Up @@ -479,7 +476,7 @@ List<Diff> _diffLineMode(String text1, String text2, DateTime deadline) {
final subDiff = diffMain(
textDelete.toString(),
textInsert.toString(),
checklines: false,
checkLines: false,
deadline: deadline,
);

Expand Down Expand Up @@ -630,44 +627,44 @@ List<Diff> diffBisect(String text1, String text2, DateTime deadline) {
return [Diff(Operation.delete, text1), Diff(Operation.insert, text2)];
}

/// Does a substring of shorttext exist within longtext such that the
/// substring is at least half the length of longtext?
/// Does a substring of [shortText] exist within [longText] such that the
/// substring is at least half the length of [longText]?
///
/// [longtext] is the longer string. [shorttext] is the shorter string.
/// [i] Start index of quarter length substring within longtext.
/// Returns a five element String array, containing the prefix of longtext,
/// the suffix of longtext, the prefix of shorttext, the suffix of
/// shorttext and the common middle. Or null if there was no match.
List<String>? _diffHalfMatchI(String longtext, String shorttext, int i) {
/// [longText] is the longer string. [shortText] is the shorter string.
/// [i] Start index of quarter length substring within [longText].
/// Returns a five element String array, containing the prefix of [longText],
/// the suffix of [longText], the prefix of [shortText], the suffix of
/// [shortText] and the common middle. Or `null` if there was no match.
List<String>? _diffHalfMatchI(String longText, String shortText, int i) {
// Start with a 1/4 length substring at position i as a seed.
final seed = longtext.substring(i, i + (longtext.length / 4).floor().toInt());
final seed = longText.substring(i, i + (longText.length / 4).floor().toInt());
var j = -1;
var bestCommon = '';
var bestLongtextA = '', bestLongtextB = '';
var bestShortTextA = '', bestShortTextB = '';

while ((j = shorttext.indexOf(seed, j + 1)) != -1) {
while ((j = shortText.indexOf(seed, j + 1)) != -1) {
final prefixLength = diffCommonPrefix(
longtext.substring(i),
shorttext.substring(j),
longText.substring(i),
shortText.substring(j),
);

final suffixLength = diffCommonSuffix(
longtext.substring(0, i),
shorttext.substring(0, j),
longText.substring(0, i),
shortText.substring(0, j),
);

if (bestCommon.length < suffixLength + prefixLength) {
bestCommon = shorttext.substring(j - suffixLength, j) +
shorttext.substring(j, j + prefixLength);
bestLongtextA = longtext.substring(0, i - suffixLength);
bestLongtextB = longtext.substring(i + prefixLength);
bestShortTextA = shorttext.substring(0, j - suffixLength);
bestShortTextB = shorttext.substring(j + prefixLength);
bestCommon = shortText.substring(j - suffixLength, j) +
shortText.substring(j, j + prefixLength);
bestLongtextA = longText.substring(0, i - suffixLength);
bestLongtextB = longText.substring(i + prefixLength);
bestShortTextA = shortText.substring(0, j - suffixLength);
bestShortTextB = shortText.substring(j + prefixLength);
}
}

if (bestCommon.length * 2 >= longtext.length) {
if (bestCommon.length * 2 >= longText.length) {
return [
bestLongtextA,
bestLongtextB,
Expand All @@ -680,17 +677,18 @@ List<String>? _diffHalfMatchI(String longtext, String shorttext, int i) {
}
}

/// Rehydrate the text in a diff from a string of line hashes to real lines of text.
/// Rehydrate the text in a diff from a
/// string of line hashes to real lines of text.
///
/// [diffs] is a List of Diff objects.
/// [lineArray] is a List of unique strings.
@visibleForTesting
void diffCharsToLines(List<Diff> diffs, List<String>? lineArray) {
void diffCharsToLines(List<Diff> diffs, List<String> lineArray) {
final text = StringBuffer();

for (var diff in diffs) {
for (var j = 0; j < diff.text.length; j++) {
text.write(lineArray![diff.text.codeUnitAt(j)]);
text.write(lineArray[diff.text.codeUnitAt(j)]);
}
diff.text = text.toString();
text.clear();
Expand Down Expand Up @@ -722,13 +720,13 @@ List<Diff> _diffBisectSplit(
final diffs = diffMain(
text1a,
text2a,
checklines: false,
checkLines: false,
deadline: deadline,
);
final diffsb = diffMain(
text1b,
text2b,
checklines: false,
checkLines: false,
deadline: deadline,
);

Expand Down