diff --git a/DiffMatchPatch.zig b/DiffMatchPatch.zig
index 97c26f4..2fdaaf1 100644
--- a/DiffMatchPatch.zig
+++ b/DiffMatchPatch.zig
@@ -3,6 +3,7 @@ const DiffMatchPatch = @This();
const std = @import("std");
const testing = std.testing;
const ArrayListUnmanaged = std.ArrayListUnmanaged;
+const DiffList = ArrayListUnmanaged(Diff);
/// DMP with default configuration options
pub const default = DiffMatchPatch{};
@@ -61,6 +62,14 @@ patch_margin: u16 = 4,
pub const DiffError = error{OutOfMemory};
/// It is recommended that you use an Arena for this operation.
+///
+/// Find the differences between two texts.
+/// @param before Old string to be diffed.
+/// @param after New string to be diffed.
+/// @param checklines 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.
+/// @return List of Diff objects.
pub fn diff(
dmp: DiffMatchPatch,
allocator: std.mem.Allocator,
@@ -70,7 +79,7 @@ pub fn diff(
/// to identify the changed areas. If true, then run
/// a faster slightly less optimal diff.
check_lines: bool,
-) DiffError!ArrayListUnmanaged(Diff) {
+) DiffError!DiffList {
const deadline = @intCast(u64, std.time.microTimestamp()) + dmp.diff_timeout;
return dmp.diffInternal(allocator, before, after, check_lines, deadline);
}
@@ -82,12 +91,12 @@ fn diffInternal(
after: []const u8,
check_lines: bool,
deadline: u64,
-) DiffError!ArrayListUnmanaged(Diff) {
+) DiffError!DiffList {
// Check for equality (speedup).
- var diffs = ArrayListUnmanaged(Diff){};
+ var diffs = DiffList{};
if (std.mem.eql(u8, before, after)) {
if (before.len != 0) {
- try diffs.append(allocator, Diff{ .operation = .equal, .text = before });
+ try diffs.append(allocator, Diff.init(.equal, before));
}
return diffs;
}
@@ -109,10 +118,10 @@ fn diffInternal(
// Restore the prefix and suffix.
if (common_prefix.len != 0) {
- try diffs.insert(allocator, 0, Diff{ .operation = .equal, .text = common_prefix });
+ try diffs.insert(allocator, 0, Diff.init(.equal, common_prefix));
}
if (common_suffix.len != 0) {
- try diffs.append(allocator, Diff{ .operation = .equal, .text = common_suffix });
+ try diffs.append(allocator, Diff.init(.equal, common_suffix));
}
try diffCleanupMerge(allocator, &diffs);
@@ -145,6 +154,15 @@ fn diffCommonSuffix(before: []const u8, after: []const u8) usize {
return n;
}
+/// Find the differences between two texts. Assumes that the texts do not
+/// have any common prefix or suffix.
+/// @param before Old string to be diffed.
+/// @param after New string to be diffed.
+/// @param checklines 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.
+/// @param deadline Time when the diff should be complete by.
+/// @return List of Diff objects.
fn diffCompute(
dmp: DiffMatchPatch,
allocator: std.mem.Allocator,
@@ -152,18 +170,18 @@ fn diffCompute(
after: []const u8,
check_lines: bool,
deadline: u64,
-) DiffError!ArrayListUnmanaged(Diff) {
- var diffs = ArrayListUnmanaged(Diff){};
+) DiffError!DiffList {
+ var diffs = DiffList{};
if (before.len == 0) {
// Just add some text (speedup).
- try diffs.append(allocator, Diff{ .operation = .insert, .text = after });
+ try diffs.append(allocator, Diff.init(.insert, after));
return diffs;
}
if (after.len == 0) {
// Just delete some text (speedup).
- try diffs.append(allocator, Diff{ .operation = .delete, .text = before });
+ try diffs.append(allocator, Diff.init(.delete, before));
return diffs;
}
@@ -177,17 +195,17 @@ fn diffCompute(
.delete
else
.insert;
- try diffs.append(allocator, Diff{ .operation = op, .text = long_text[0..index] });
- try diffs.append(allocator, Diff{ .operation = .equal, .text = short_text });
- try diffs.append(allocator, Diff{ .operation = op, .text = long_text[index + short_text.len ..] });
+ try diffs.append(allocator, Diff.init(op, long_text[0..index]));
+ try diffs.append(allocator, Diff.init(.equal, short_text));
+ try diffs.append(allocator, Diff.init(op, long_text[index + short_text.len ..]));
return diffs;
}
if (short_text.len == 1) {
// Single character string.
// After the previous speedup, the character can't be an equality.
- try diffs.append(allocator, Diff{ .operation = .delete, .text = before });
- try diffs.append(allocator, Diff{ .operation = .insert, .text = after });
+ try diffs.append(allocator, Diff.init(.delete, before));
+ try diffs.append(allocator, Diff.init(.insert, after));
return diffs;
}
@@ -197,13 +215,25 @@ fn diffCompute(
// A half-match was found, sort out the return data.
// Send both pairs off for separate processing.
- var diffs_a = try dmp.diffInternal(allocator, half_match.prefix_before, half_match.prefix_after, check_lines, deadline);
- var diffs_b = try dmp.diffInternal(allocator, half_match.suffix_before, half_match.suffix_after, check_lines, deadline);
+ var diffs_a = try dmp.diffInternal(
+ allocator,
+ half_match.prefix_before,
+ half_match.prefix_after,
+ check_lines,
+ deadline,
+ );
+ var diffs_b = try dmp.diffInternal(
+ allocator,
+ half_match.suffix_before,
+ half_match.suffix_after,
+ check_lines,
+ deadline,
+ );
defer diffs_b.deinit(allocator);
// Merge the results.
diffs = diffs_a;
- try diffs.append(allocator, Diff{ .operation = .equal, .text = half_match.common_middle });
+ try diffs.append(allocator, Diff.init(.equal, half_match.common_middle));
try diffs.appendSlice(allocator, diffs_b.items);
return diffs;
}
@@ -223,6 +253,14 @@ const HalfMatchResult = struct {
common_middle: []const u8,
};
+/// Do the two texts share a Substring which is at least half the length of
+/// the longer text?
+/// This speedup can produce non-minimal diffs.
+/// @param before First string.
+/// @param after Second string.
+/// @return Five element String array, containing the prefix of text1, the
+/// suffix of text1, the prefix of text2, the suffix of text2 and the
+/// common middle. Or null if there was no match.
fn diffHalfMatch(
dmp: DiffMatchPatch,
allocator: std.mem.Allocator,
@@ -254,7 +292,10 @@ fn diffHalfMatch(
half_match = half_match_2.?;
} else {
// Both matched. Select the longest.
- half_match = if (half_match_1.?.common_middle.len > half_match_2.?.common_middle.len) half_match_1 else half_match_2;
+ half_match = if (half_match_1.?.common_middle.len > half_match_2.?.common_middle.len)
+ half_match_1
+ else
+ half_match_2;
}
// A half-match was found, sort out the return data.
@@ -272,6 +313,14 @@ fn diffHalfMatch(
}
}
+/// Does a Substring of shorttext exist within longtext such that the
+/// Substring is at least half the length of longtext?
+/// @param longtext Longer string.
+/// @param shorttext Shorter string.
+/// @param i Start index of quarter length Substring within longtext.
+/// @return 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.
fn diffHalfMatchInternal(
_: DiffMatchPatch,
allocator: std.mem.Allocator,
@@ -319,13 +368,20 @@ fn diffHalfMatchInternal(
}
}
+/// Find the 'middle snake' of a diff, split the problem in two
+/// and return the recursively constructed diff.
+/// See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
+/// @param before Old string to be diffed.
+/// @param after New string to be diffed.
+/// @param deadline Time at which to bail if not yet complete.
+/// @return List of Diff objects.
fn diffBisect(
dmp: DiffMatchPatch,
allocator: std.mem.Allocator,
before: []const u8,
after: []const u8,
deadline: u64,
-) error{OutOfMemory}!ArrayListUnmanaged(Diff) {
+) DiffError!DiffList {
const before_length = @intCast(isize, before.len);
const after_length = @intCast(isize, after.len);
const max_d = @intCast(isize, (before.len + after.len + 1) / 2);
@@ -367,13 +423,17 @@ fn diffBisect(
while (k1 <= d - k1end) : (k1 += 2) {
var k1_offset = v_offset + k1;
var x1: isize = 0;
- if (k1 == -d or k1 != d and v1.items[@intCast(usize, k1_offset - 1)] < v1.items[@intCast(usize, k1_offset + 1)]) {
+ if (k1 == -d or k1 != d and
+ v1.items[@intCast(usize, k1_offset - 1)] < v1.items[@intCast(usize, k1_offset + 1)])
+ {
x1 = v1.items[@intCast(usize, k1_offset + 1)];
} else {
x1 = v1.items[@intCast(usize, k1_offset - 1)] + 1;
}
var y1 = x1 - k1;
- while (x1 < before_length and y1 < after_length and before[@intCast(usize, x1)] == after[@intCast(usize, y1)]) {
+ while (x1 < before_length and
+ y1 < after_length and before[@intCast(usize, x1)] == after[@intCast(usize, y1)])
+ {
x1 += 1;
y1 += 1;
}
@@ -402,13 +462,17 @@ fn diffBisect(
while (k2 <= d - k2end) : (k2 += 2) {
var k2_offset: isize = @intCast(isize, v_offset) + k2;
var x2: isize = 0;
- if (k2 == -@intCast(isize, d) or k2 != d and v2.items[@intCast(usize, k2_offset - 1)] < v2.items[@intCast(usize, k2_offset + 1)]) {
+ if (k2 == -@intCast(isize, d) or k2 != d and
+ v2.items[@intCast(usize, k2_offset - 1)] < v2.items[@intCast(usize, k2_offset + 1)])
+ {
x2 = v2.items[@intCast(usize, k2_offset + 1)];
} else {
x2 = v2.items[@intCast(usize, k2_offset - 1)] + 1;
}
var y2: isize = x2 - k2;
- while (x2 < before.len and y2 < after.len and before[@intCast(usize, @intCast(isize, before.len) - x2 - 1)] == after[@intCast(usize, @intCast(isize, after.len) - y2 - 1)]) {
+ while (x2 < before.len and y2 < after.len and
+ before[@intCast(usize, @intCast(isize, before.len) - x2 - 1)] == after[@intCast(usize, @intCast(isize, after.len) - y2 - 1)])
+ {
x2 += 1;
y2 += 1;
}
@@ -436,12 +500,20 @@ fn diffBisect(
}
// Diff took too long and hit the deadline or
// number of diffs equals number of characters, no commonality at all.
- var diffs = ArrayListUnmanaged(Diff){};
- try diffs.append(allocator, Diff{ .operation = .delete, .text = before });
- try diffs.append(allocator, Diff{ .operation = .insert, .text = after });
+ var diffs = DiffList{};
+ try diffs.append(allocator, Diff.init(.delete, before));
+ try diffs.append(allocator, Diff.init(.insert, after));
return diffs;
}
+/// Given the location of the 'middle snake', split the diff in two parts
+/// and recurse.
+/// @param text1 Old string to be diffed.
+/// @param text2 New string to be diffed.
+/// @param x Index of split point in text1.
+/// @param y Index of split point in text2.
+/// @param deadline Time at which to bail if not yet complete.
+/// @return LinkedList of Diff objects.
fn diffBisectSplit(
dmp: DiffMatchPatch,
allocator: std.mem.Allocator,
@@ -450,7 +522,7 @@ fn diffBisectSplit(
x: isize,
y: isize,
deadline: u64,
-) DiffError!ArrayListUnmanaged(Diff) {
+) DiffError!DiffList {
const text1a = text1[0..@intCast(usize, x)];
const text2a = text2[0..@intCast(usize, y)];
const text1b = text1[@intCast(usize, x)..];
@@ -465,29 +537,27 @@ fn diffBisectSplit(
return diffs;
}
-//
-// Do a quick line-level diff on both strings, then rediff the parts for
-// greater accuracy.
-// This speedup can produce non-minimal diffs.
-// @param text1 Old string to be diffed.
-// @param text2 New string to be diffed.
-// @param deadline Time when the diff should be complete by.
-// @return List of Diff objects.
-//
+/// Do a quick line-level diff on both strings, then rediff the parts for
+/// greater accuracy.
+/// This speedup can produce non-minimal diffs.
+/// @param text1 Old string to be diffed.
+/// @param text2 New string to be diffed.
+/// @param deadline Time when the diff should be complete by.
+/// @return List of Diff objects.
fn diffLineMode(
dmp: DiffMatchPatch,
allocator: std.mem.Allocator,
text1_in: []const u8,
text2_in: []const u8,
deadline: u64,
-) DiffError!ArrayListUnmanaged(Diff) {
+) DiffError!DiffList {
// Scan the text on a line-by-line basis first.
var a = try diffLinesToChars(allocator, text1_in, text2_in);
var text1 = a.chars_1;
var text2 = a.chars_2;
var line_array = a.line_array;
- var diffs: ArrayListUnmanaged(Diff) = try dmp.diffInternal(allocator, text1, text2, false, deadline);
+ var diffs: DiffList = try dmp.diffInternal(allocator, text1, text2, false, deadline);
// Convert the diff back to original text.
try diffCharsToLines(allocator, diffs.items, line_array.items);
@@ -496,7 +566,7 @@ fn diffLineMode(
// Rediff any replacement blocks, this time character-by-character.
// Add a dummy entry at the end.
- try diffs.append(allocator, Diff{ .operation = .equal, .text = "" });
+ try diffs.append(allocator, Diff.init(.equal, ""));
var pointer: usize = 0;
var count_delete: usize = 0;
@@ -557,7 +627,18 @@ const LinesToCharsResult = struct {
line_array: ArrayListUnmanaged([]const u8),
};
-fn diffLinesToChars(allocator: std.mem.Allocator, text1: []const u8, text2: []const u8) error{OutOfMemory}!LinesToCharsResult {
+/// Split two texts into a list of strings. Reduce the texts to a string of
+/// hashes where each Unicode character represents one line.
+/// @param text1 First string.
+/// @param text2 Second string.
+/// @return Three element Object array, containing the encoded text1, the
+/// encoded text2 and the List of unique strings. The zeroth element
+/// of the List of unique strings is intentionally blank.
+fn diffLinesToChars(
+ allocator: std.mem.Allocator,
+ text1: []const u8,
+ text2: []const u8,
+) DiffError!LinesToCharsResult {
var line_array = ArrayListUnmanaged([]const u8){};
var line_hash = std.StringHashMapUnmanaged(usize){};
// e.g. line_array[4] == "Hello\n"
@@ -573,13 +654,20 @@ fn diffLinesToChars(allocator: std.mem.Allocator, text1: []const u8, text2: []co
return .{ .chars_1 = chars1, .chars_2 = chars2, .line_array = line_array };
}
+/// Split a text into a list of strings. Reduce the texts to a string of
+/// hashes where each Unicode character represents one line.
+/// @param text String to encode.
+/// @param lineArray List of unique strings.
+/// @param lineHash Map of strings to indices.
+/// @param maxLines Maximum length of lineArray.
+/// @return Encoded string.
fn diffLinesToCharsMunge(
allocator: std.mem.Allocator,
text: []const u8,
line_array: *ArrayListUnmanaged([]const u8),
line_hash: *std.StringHashMapUnmanaged(usize),
max_lines: usize,
-) error{OutOfMemory}![]const u8 {
+) DiffError![]const u8 {
var line_start: isize = 0;
var line_end: isize = -1;
var line: []const u8 = "";
@@ -589,7 +677,8 @@ fn diffLinesToCharsMunge(
// Modifying text would create many large strings to garbage collect.
while (line_end < @intCast(isize, text.len) - 1) {
line_end = b: {
- break :b @intCast(isize, std.mem.indexOf(u8, text[@intCast(usize, line_start)..], "\n") orelse break :b @intCast(isize, text.len - 1)) + line_start;
+ break :b @intCast(isize, std.mem.indexOf(u8, text[@intCast(usize, line_start)..], "\n") orelse
+ break :b @intCast(isize, text.len - 1)) + line_start;
};
line = text[@intCast(usize, line_start) .. @intCast(usize, line_start) + @intCast(usize, line_end + 1 - line_start)];
@@ -610,7 +699,15 @@ fn diffLinesToCharsMunge(
return try chars.toOwnedSlice(allocator);
}
-fn diffCharsToLines(allocator: std.mem.Allocator, diffs: []Diff, line_array: []const []const u8) error{OutOfMemory}!void {
+/// Rehydrate the text in a diff from a string of line hashes to real lines
+/// of text.
+/// @param diffs List of Diff objects.
+/// @param lineArray List of unique strings.
+fn diffCharsToLines(
+ allocator: std.mem.Allocator,
+ diffs: []Diff,
+ line_array: []const []const u8,
+) DiffError!void {
var text = ArrayListUnmanaged(u8){};
defer text.deinit(allocator);
@@ -624,14 +721,12 @@ fn diffCharsToLines(allocator: std.mem.Allocator, diffs: []Diff, line_array: []c
}
}
-//
-// Reorder and merge like edit sections. Merge equalities.
-// Any edit section can move as long as it doesn't cross an equality.
-// @param diffs List of Diff objects.
-//
-fn diffCleanupMerge(allocator: std.mem.Allocator, diffs: *std.ArrayListUnmanaged(Diff)) error{OutOfMemory}!void {
+/// Reorder and merge like edit sections. Merge equalities.
+/// Any edit section can move as long as it doesn't cross an equality.
+/// @param diffs List of Diff objects.
+fn diffCleanupMerge(allocator: std.mem.Allocator, diffs: *DiffList) DiffError!void {
// Add a dummy entry at the end.
- try diffs.append(allocator, Diff{ .operation = .equal, .text = "" });
+ try diffs.append(allocator, Diff.init(.equal, ""));
var pointer: usize = 0;
var count_delete: usize = 0;
var count_insert: usize = 0;
@@ -680,8 +775,10 @@ fn diffCleanupMerge(allocator: std.mem.Allocator, diffs: *std.ArrayListUnmanaged
} else {
// diffs.Insert(0, Diff.init(.equal,
// text_insert.Substring(0, common_length)));
- const text = std.ArrayListUnmanaged(u8){ .items = try allocator.dupe(u8, text_insert.items[0..common_length]) };
- try diffs.insert(allocator, 0, Diff{ .operation = .equal, .text = try allocator.dupe(u8, text.items) });
+ const text = std.ArrayListUnmanaged(u8){
+ .items = try allocator.dupe(u8, text_insert.items[0..common_length]),
+ };
+ try diffs.insert(allocator, 0, Diff.init(.equal, try allocator.dupe(u8, text.items)));
pointer += 1;
}
try text_insert.replaceRange(allocator, 0, common_length, &.{});
@@ -691,7 +788,10 @@ fn diffCleanupMerge(allocator: std.mem.Allocator, diffs: *std.ArrayListUnmanaged
// @ZigPort this seems very wrong
common_length = diffCommonSuffix(text_insert.items, text_delete.items);
if (common_length != 0) {
- diffs.items[pointer].text = try std.mem.concat(allocator, u8, &.{ text_insert.items[text_insert.items.len - common_length ..], diffs.items[pointer].text });
+ diffs.items[pointer].text = try std.mem.concat(allocator, u8, &.{
+ text_insert.items[text_insert.items.len - common_length ..],
+ diffs.items[pointer].text,
+ });
text_insert.items.len -= common_length;
text_delete.items.len -= common_length;
}
@@ -701,11 +801,15 @@ fn diffCleanupMerge(allocator: std.mem.Allocator, diffs: *std.ArrayListUnmanaged
try diffs.replaceRange(allocator, pointer, count_delete + count_insert, &.{});
if (text_delete.items.len != 0) {
- try diffs.replaceRange(allocator, pointer, 0, &.{Diff{ .operation = .delete, .text = try allocator.dupe(u8, text_delete.items) }});
+ try diffs.replaceRange(allocator, pointer, 0, &.{
+ Diff.init(.delete, try allocator.dupe(u8, text_delete.items)),
+ });
pointer += 1;
}
if (text_insert.items.len != 0) {
- try diffs.replaceRange(allocator, pointer, 0, &.{Diff{ .operation = .insert, .text = try allocator.dupe(u8, text_insert.items) }});
+ try diffs.replaceRange(allocator, pointer, 0, &.{
+ Diff.init(.insert, try allocator.dupe(u8, text_insert.items)),
+ });
pointer += 1;
}
pointer += 1;
@@ -762,7 +866,10 @@ fn diffCleanupMerge(allocator: std.mem.Allocator, diffs: *std.ArrayListUnmanaged
diffs.items[pointer].text[0 .. diffs.items[pointer].text.len -
diffs.items[pointer - 1].text.len],
});
- const p1t = try std.mem.concat(allocator, u8, &.{ diffs.items[pointer - 1].text, diffs.items[pointer + 1].text });
+ const p1t = try std.mem.concat(allocator, u8, &.{
+ diffs.items[pointer - 1].text,
+ diffs.items[pointer + 1].text,
+ });
// allocator.free(diffs.items[pointer].text);
// allocator.free(diffs.items[pointer + 1].text);
@@ -778,7 +885,10 @@ fn diffCleanupMerge(allocator: std.mem.Allocator, diffs: *std.ArrayListUnmanaged
// diffs.items[pointer].text =
// diffs.items[pointer].text[diffs.items[pointer + 1].text.len..] + diffs.items[pointer + 1].text;
- const pm1t = try std.mem.concat(allocator, u8, &.{ diffs.items[pointer - 1].text, diffs.items[pointer + 1].text });
+ const pm1t = try std.mem.concat(allocator, u8, &.{
+ diffs.items[pointer - 1].text,
+ diffs.items[pointer + 1].text,
+ });
const pt = try std.mem.concat(allocator, u8, &.{
diffs.items[pointer].text[diffs.items[pointer + 1].text.len..],
diffs.items[pointer + 1].text,
@@ -802,7 +912,10 @@ fn diffCleanupMerge(allocator: std.mem.Allocator, diffs: *std.ArrayListUnmanaged
}
}
-fn diffCleanupSemantic(allocator: std.mem.Allocator, diffs: *ArrayListUnmanaged(Diff)) error{OutOfMemory}!void {
+/// Reduce the number of edits by eliminating semantically trivial
+/// equalities.
+/// @param diffs List of Diff objects.
+fn diffCleanupSemantic(allocator: std.mem.Allocator, diffs: *DiffList) DiffError!void {
var changes = false;
// Stack of indices where equalities are found.
var equalities = ArrayListUnmanaged(isize){};
@@ -831,9 +944,16 @@ fn diffCleanupSemantic(allocator: std.mem.Allocator, diffs: *ArrayListUnmanaged(
}
// Eliminate an equality that is smaller or equal to the edits on both
// sides of it.
- if (last_equality != null and (last_equality.?.len <= std.math.max(length_insertions1, length_deletions1)) and (last_equality.?.len <= std.math.max(length_insertions2, length_deletions2))) {
+ if (last_equality != null and
+ (last_equality.?.len <= std.math.max(length_insertions1, length_deletions1)) and
+ (last_equality.?.len <= std.math.max(length_insertions2, length_deletions2)))
+ {
// Duplicate record.
- try diffs.insert(allocator, @intCast(usize, equalities.items[equalities.items.len - 1]), Diff{ .operation = .delete, .text = last_equality.? });
+ try diffs.insert(
+ allocator,
+ @intCast(usize, equalities.items[equalities.items.len - 1]),
+ Diff.init(.delete, last_equality.?),
+ );
// Change second copy to insert.
diffs.items[@intCast(usize, equalities.items[equalities.items.len - 1] + 1)].operation = .insert;
// Throw away the equality we just deleted.
@@ -880,9 +1000,15 @@ fn diffCleanupSemantic(allocator: std.mem.Allocator, diffs: *ArrayListUnmanaged(
{
// Overlap found.
// Insert an equality and trim the surrounding edits.
- try diffs.insert(allocator, @intCast(usize, pointer), Diff{ .operation = .equal, .text = try allocator.dupe(u8, insertion[0..overlap_length1]) });
- diffs.items[@intCast(usize, pointer - 1)].text = try allocator.dupe(u8, deletion[0 .. deletion.len - overlap_length1]);
- diffs.items[@intCast(usize, pointer + 1)].text = try allocator.dupe(u8, insertion[overlap_length1..]);
+ try diffs.insert(
+ allocator,
+ @intCast(usize, pointer),
+ Diff.init(.equal, try allocator.dupe(u8, insertion[0..overlap_length1])),
+ );
+ diffs.items[@intCast(usize, pointer - 1)].text =
+ try allocator.dupe(u8, deletion[0 .. deletion.len - overlap_length1]);
+ diffs.items[@intCast(usize, pointer + 1)].text =
+ try allocator.dupe(u8, insertion[overlap_length1..]);
pointer += 1;
}
} else {
@@ -891,11 +1017,17 @@ fn diffCleanupSemantic(allocator: std.mem.Allocator, diffs: *ArrayListUnmanaged(
{
// Reverse overlap found.
// Insert an equality and swap and trim the surrounding edits.
- try diffs.insert(allocator, @intCast(usize, pointer), Diff{ .operation = .equal, .text = try allocator.dupe(u8, deletion[0..overlap_length2]) });
+ try diffs.insert(
+ allocator,
+ @intCast(usize, pointer),
+ Diff.init(.equal, try allocator.dupe(u8, deletion[0..overlap_length2])),
+ );
diffs.items[@intCast(usize, pointer - 1)].operation = .insert;
- diffs.items[@intCast(usize, pointer - 1)].text = try allocator.dupe(u8, insertion[0 .. insertion.len - overlap_length2]);
+ diffs.items[@intCast(usize, pointer - 1)].text =
+ try allocator.dupe(u8, insertion[0 .. insertion.len - overlap_length2]);
diffs.items[@intCast(usize, pointer + 1)].operation = .delete;
- diffs.items[@intCast(usize, pointer + 1)].text = try allocator.dupe(u8, deletion[overlap_length2..]);
+ diffs.items[@intCast(usize, pointer + 1)].text =
+ try allocator.dupe(u8, deletion[overlap_length2..]);
pointer += 1;
}
}
@@ -910,8 +1042,8 @@ fn diffCleanupSemantic(allocator: std.mem.Allocator, diffs: *ArrayListUnmanaged(
/// e.g: The cat came. -> The cat came.
pub fn diffCleanupSemanticLossless(
allocator: std.mem.Allocator,
- diffs: *ArrayListUnmanaged(Diff),
-) error{OutOfMemory}!void {
+ diffs: *DiffList,
+) DiffError!void {
var pointer: usize = 1;
// Intentionally ignore the first and last element (don't need checking).
while (pointer < @intCast(isize, diffs.items.len) - 1) {
@@ -1015,14 +1147,12 @@ pub fn diffCleanupSemanticLossless(
}
}
-//
-// Given two strings, compute a score representing whether the internal
-// boundary falls on logical boundaries.
-// Scores range from 6 (best) to 0 (worst).
-// @param one First string.
-// @param two Second string.
-// @return The score.
-//
+/// Given two strings, compute a score representing whether the internal
+/// boundary falls on logical boundaries.
+/// Scores range from 6 (best) to 0 (worst).
+/// @param one First string.
+/// @param two Second string.
+/// @return The score.
fn diffCleanupSemanticScore(one: []const u8, two: []const u8) usize {
if (one.len == 0 or two.len == 0) {
// Edges are the best.
@@ -1047,7 +1177,10 @@ fn diffCleanupSemanticScore(one: []const u8, two: []const u8) usize {
(std.mem.endsWith(u8, one, "\n\n") or std.mem.endsWith(u8, one, "\n\r\n"));
const blankLine2 = lineBreak2 and
// BLANKLINESTART.IsMatch(two);
- (std.mem.startsWith(u8, two, "\n\n") or std.mem.startsWith(u8, two, "\r\n\n") or std.mem.startsWith(u8, two, "\n\r\n") or std.mem.startsWith(u8, two, "\r\n\r\n"));
+ (std.mem.startsWith(u8, two, "\n\n") or
+ std.mem.startsWith(u8, two, "\r\n\n") or
+ std.mem.startsWith(u8, two, "\n\r\n") or
+ std.mem.startsWith(u8, two, "\r\n\r\n"));
if (blankLine1 or blankLine2) {
// Five points for blank lines.
@@ -1083,11 +1216,11 @@ fn diffCleanupSemanticScore(one: []const u8, two: []const u8) usize {
pub fn diffCleanupEfficiency(
dmp: DiffMatchPatch,
allocator: std.mem.Allocator,
- diffs: *ArrayListUnmanaged(Diff),
-) error{OutOfMemory}!void {
+ diffs: *DiffList,
+) DiffError!void {
var changes = false;
// Stack of indices where equalities are found.
- var equalities = ArrayListUnmanaged(Diff){};
+ var equalities = DiffList{};
// Always equal to equalities[equalitiesLength-1][1]
var last_equality = "";
var pointer: isize = 0; // Index of current position.
@@ -1126,12 +1259,16 @@ pub fn diffCleanupEfficiency(
// ABXC
// AXCD
// ABXC
- if ((last_equality.Length != 0) and ((pre_ins and pre_del and post_ins and post_del) or ((last_equality.Length < dmp.diff_edit_cost / 2) and ((if (pre_ins) 1 else 0) + (if (pre_del) 1 else 0) + (if (post_ins) 1 else 0) + (if (post_del) 1 else 0)) == 3))) {
+ if ((last_equality.Length != 0) and
+ ((pre_ins and pre_del and post_ins and post_del) or
+ ((last_equality.Length < dmp.diff_edit_cost / 2) and
+ ((if (pre_ins) 1 else 0) + (if (pre_del) 1 else 0) + (if (post_ins) 1 else 0) + (if (post_del) 1 else 0)) == 3)))
+ {
// Duplicate record.
try diffs.insert(
allocator,
equalities.items[equalities.items.len - 1],
- Diff{ .operation = .delete, .text = try allocator.dupe(u8, last_equality) },
+ Diff.init(.delete, try allocator.dupe(u8, last_equality)),
);
// Change second copy to insert.
diffs.items[equalities.items[equalities.items.len - 1] + 1].operation = .insert;
@@ -1162,13 +1299,11 @@ pub fn diffCleanupEfficiency(
}
}
-//
-// Determine if the suffix of one string is the prefix of another.
-// @param text1 First string.
-// @param text2 Second string.
-// @return The number of characters common to the end of the first
-// string and the start of the second string.
-//
+/// Determine if the suffix of one string is the prefix of another.
+/// @param text1 First string.
+/// @param text2 Second string.
+/// @return The number of characters common to the end of the first
+/// string and the start of the second string.
fn diffCommonOverlap(text1_in: []const u8, text2_in: []const u8) usize {
var text1 = text1_in;
var text2 = text2_in;
@@ -1275,8 +1410,14 @@ test diffHalfMatch {
var one_timeout = DiffMatchPatch{};
one_timeout.diff_timeout = 1;
- try testing.expectEqual(@as(?HalfMatchResult, null), try one_timeout.diffHalfMatch(arena.allocator(), "1234567890", "abcdef")); // No match #1
- try testing.expectEqual(@as(?HalfMatchResult, null), try one_timeout.diffHalfMatch(arena.allocator(), "12345", "23")); // No match #2
+ try testing.expectEqual(
+ @as(?HalfMatchResult, null),
+ try one_timeout.diffHalfMatch(arena.allocator(), "1234567890", "abcdef"),
+ ); // No match #1
+ try testing.expectEqual(
+ @as(?HalfMatchResult, null),
+ try one_timeout.diffHalfMatch(arena.allocator(), "12345", "23"),
+ ); // No match #2
// Single matches
try testing.expectEqualDeep(@as(?HalfMatchResult, HalfMatchResult{
@@ -1312,21 +1453,27 @@ test diffHalfMatch {
}), try one_timeout.diffHalfMatch(arena.allocator(), "a23456xyz", "1234567890")); // Single Match #4
// Multiple matches
- try testing.expectEqualDeep(@as(?HalfMatchResult, HalfMatchResult{
- .prefix_before = "12123",
- .suffix_before = "123121",
- .prefix_after = "a",
- .suffix_after = "z",
- .common_middle = "1234123451234",
- }), try one_timeout.diffHalfMatch(arena.allocator(), "121231234123451234123121", "a1234123451234z")); // Multiple Matches #1
-
- try testing.expectEqualDeep(@as(?HalfMatchResult, HalfMatchResult{
- .prefix_before = "",
- .suffix_before = "-=-=-=-=-=",
- .prefix_after = "x",
- .suffix_after = "",
- .common_middle = "x-=-=-=-=-=-=-=",
- }), try one_timeout.diffHalfMatch(arena.allocator(), "x-=-=-=-=-=-=-=-=-=-=-=-=", "xx-=-=-=-=-=-=-=")); // Multiple Matches #2
+ try testing.expectEqualDeep(
+ @as(?HalfMatchResult, HalfMatchResult{
+ .prefix_before = "12123",
+ .suffix_before = "123121",
+ .prefix_after = "a",
+ .suffix_after = "z",
+ .common_middle = "1234123451234",
+ }),
+ try one_timeout.diffHalfMatch(arena.allocator(), "121231234123451234123121", "a1234123451234z"),
+ ); // Multiple Matches #1
+
+ try testing.expectEqualDeep(
+ @as(?HalfMatchResult, HalfMatchResult{
+ .prefix_before = "",
+ .suffix_before = "-=-=-=-=-=",
+ .prefix_after = "x",
+ .suffix_after = "",
+ .common_middle = "x-=-=-=-=-=-=-=",
+ }),
+ try one_timeout.diffHalfMatch(arena.allocator(), "x-=-=-=-=-=-=-=-=-=-=-=-=", "xx-=-=-=-=-=-=-="),
+ ); // Multiple Matches #2
try testing.expectEqualDeep(@as(?HalfMatchResult, HalfMatchResult{
.prefix_before = "-=-=-=-=-=",
@@ -1416,10 +1563,10 @@ test diffCharsToLines {
var arena = std.heap.ArenaAllocator.init(testing.allocator);
defer arena.deinit();
- try std.testing.expect((Diff{ .operation = .equal, .text = "a" }).eql(Diff{ .operation = .equal, .text = "a" }));
- try std.testing.expect(!(Diff{ .operation = .insert, .text = "a" }).eql(Diff{ .operation = .equal, .text = "a" }));
- try std.testing.expect(!(Diff{ .operation = .equal, .text = "a" }).eql(Diff{ .operation = .equal, .text = "b" }));
- try std.testing.expect(!(Diff{ .operation = .equal, .text = "a" }).eql(Diff{ .operation = .delete, .text = "b" }));
+ try testing.expect((Diff.init(.equal, "a")).eql(Diff.init(.equal, "a")));
+ try testing.expect(!(Diff.init(.insert, "a")).eql(Diff.init(.equal, "a")));
+ try testing.expect(!(Diff.init(.equal, "a")).eql(Diff.init(.equal, "b")));
+ try testing.expect(!(Diff.init(.equal, "a")).eql(Diff.init(.delete, "b")));
// Convert chars up to lines.
var diffs = std.ArrayList(Diff).init(arena.allocator());
@@ -1433,9 +1580,9 @@ test diffCharsToLines {
try tmp_vector.append("beta\n");
try diffCharsToLines(arena.allocator(), diffs.items, tmp_vector.items);
- try std.testing.expectEqualDeep(@as([]const Diff, &[_]Diff{
- Diff{ .operation = .equal, .text = "alpha\nbeta\nalpha\n" },
- Diff{ .operation = .insert, .text = "beta\nalpha\nbeta\n" },
+ try testing.expectEqualDeep(@as([]const Diff, &[_]Diff{
+ Diff.init(.equal, "alpha\nbeta\nalpha\n"),
+ Diff.init(.insert, "beta\nalpha\nbeta\n"),
}), diffs.items);
// TODO: Implement exhaustive tests
@@ -1446,8 +1593,8 @@ test diffCleanupMerge {
defer arena.deinit();
// Cleanup a messy diff.
- var diffs = std.ArrayListUnmanaged(Diff){};
- try std.testing.expectEqualDeep(@as([]const Diff, &[0]Diff{}), diffs.items); // Null case
+ var diffs = DiffList{};
+ try testing.expectEqualDeep(@as([]const Diff, &[0]Diff{}), diffs.items); // Null case
try diffs.appendSlice(arena.allocator(), &[_]Diff{
.{ .operation = .equal, .text = "a" },
@@ -1455,7 +1602,7 @@ test diffCleanupMerge {
.{ .operation = .insert, .text = "c" },
});
try diffCleanupMerge(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &[_]Diff{
+ try testing.expectEqualDeep(@as([]const Diff, &[_]Diff{
.{ .operation = .equal, .text = "a" },
.{ .operation = .delete, .text = "b" },
.{ .operation = .insert, .text = "c" },
@@ -1469,7 +1616,7 @@ test diffCleanupMerge {
.{ .operation = .equal, .text = "c" },
});
try diffCleanupMerge(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &[_]Diff{
+ try testing.expectEqualDeep(@as([]const Diff, &[_]Diff{
.{ .operation = .equal, .text = "abc" },
}), diffs.items); // Merge equalities
@@ -1481,7 +1628,7 @@ test diffCleanupMerge {
.{ .operation = .delete, .text = "c" },
});
try diffCleanupMerge(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &[_]Diff{
+ try testing.expectEqualDeep(@as([]const Diff, &[_]Diff{
.{ .operation = .delete, .text = "abc" },
}), diffs.items); // Merge deletions
@@ -1493,7 +1640,7 @@ test diffCleanupMerge {
.{ .operation = .insert, .text = "c" },
});
try diffCleanupMerge(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &[_]Diff{
+ try testing.expectEqualDeep(@as([]const Diff, &[_]Diff{
.{ .operation = .insert, .text = "abc" },
}), diffs.items); // Merge insertions
@@ -1508,7 +1655,7 @@ test diffCleanupMerge {
.{ .operation = .equal, .text = "f" },
});
try diffCleanupMerge(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &[_]Diff{
+ try testing.expectEqualDeep(@as([]const Diff, &[_]Diff{
.{ .operation = .delete, .text = "ac" },
.{ .operation = .insert, .text = "bd" },
.{ .operation = .equal, .text = "ef" },
@@ -1522,7 +1669,7 @@ test diffCleanupMerge {
.{ .operation = .delete, .text = "dc" },
});
try diffCleanupMerge(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &[_]Diff{
+ try testing.expectEqualDeep(@as([]const Diff, &[_]Diff{
.{ .operation = .equal, .text = "a" },
.{ .operation = .delete, .text = "d" },
.{ .operation = .insert, .text = "b" },
@@ -1539,7 +1686,7 @@ test diffCleanupMerge {
.{ .operation = .equal, .text = "y" },
});
try diffCleanupMerge(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &[_]Diff{
+ try testing.expectEqualDeep(@as([]const Diff, &[_]Diff{
.{ .operation = .equal, .text = "xa" },
.{ .operation = .delete, .text = "d" },
.{ .operation = .insert, .text = "b" },
@@ -1554,7 +1701,7 @@ test diffCleanupMerge {
.{ .operation = .equal, .text = "c" },
});
try diffCleanupMerge(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &[_]Diff{
+ try testing.expectEqualDeep(@as([]const Diff, &[_]Diff{
.{ .operation = .insert, .text = "ab" },
.{ .operation = .equal, .text = "ac" },
}), diffs.items); // Slide edit left
@@ -1567,7 +1714,7 @@ test diffCleanupMerge {
.{ .operation = .equal, .text = "a" },
});
try diffCleanupMerge(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &[_]Diff{
+ try testing.expectEqualDeep(@as([]const Diff, &[_]Diff{
.{ .operation = .equal, .text = "ca" },
.{ .operation = .insert, .text = "ba" },
}), diffs.items); // Slide edit right
@@ -1582,7 +1729,7 @@ test diffCleanupMerge {
Diff.init(.equal, "x"),
});
try diffCleanupMerge(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &[_]Diff{
+ try testing.expectEqualDeep(@as([]const Diff, &[_]Diff{
Diff.init(.delete, "abc"),
Diff.init(.equal, "acx"),
}), diffs.items); // Slide edit left recursive
@@ -1597,7 +1744,7 @@ test diffCleanupMerge {
Diff.init(.equal, "a"),
});
try diffCleanupMerge(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &[_]Diff{
+ try testing.expectEqualDeep(@as([]const Diff, &[_]Diff{
Diff.init(.equal, "xca"),
Diff.init(.delete, "cba"),
}), diffs.items); // Slide edit right recursive
@@ -1610,7 +1757,7 @@ test diffCleanupMerge {
Diff.init(.equal, "c"),
});
try diffCleanupMerge(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &[_]Diff{
+ try testing.expectEqualDeep(@as([]const Diff, &[_]Diff{
Diff.init(.insert, "a"),
Diff.init(.equal, "bc"),
}), diffs.items); // Empty merge
@@ -1623,7 +1770,7 @@ test diffCleanupMerge {
Diff.init(.equal, "b"),
});
try diffCleanupMerge(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &[_]Diff{
+ try testing.expectEqualDeep(@as([]const Diff, &[_]Diff{
Diff.init(.insert, "a"),
Diff.init(.equal, "b"),
}), diffs.items); // Empty equality
@@ -1633,9 +1780,9 @@ test diffCleanupSemanticLossless {
var arena = std.heap.ArenaAllocator.init(testing.allocator);
defer arena.deinit();
- var diffs = std.ArrayListUnmanaged(Diff){};
+ var diffs = DiffList{};
try diffCleanupSemanticLossless(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &[0]Diff{}), diffs.items); // Null case
+ try testing.expectEqualDeep(@as([]const Diff, &[0]Diff{}), diffs.items); // Null case
diffs.items.len = 0;
@@ -1645,7 +1792,7 @@ test diffCleanupSemanticLossless {
Diff.init(.equal, "\r\nEEE"),
});
try diffCleanupSemanticLossless(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &.{
+ try testing.expectEqualDeep(@as([]const Diff, &.{
Diff.init(.equal, "AAA\r\n\r\n"),
Diff.init(.insert, "BBB\r\nDDD\r\n\r\n"),
Diff.init(.equal, "BBB\r\nEEE"),
@@ -1659,7 +1806,7 @@ test diffCleanupSemanticLossless {
Diff.init(.equal, " EEE"),
});
try diffCleanupSemanticLossless(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &.{
+ try testing.expectEqualDeep(@as([]const Diff, &.{
Diff.init(.equal, "AAA\r\n"),
Diff.init(.insert, "BBB DDD\r\n"),
Diff.init(.equal, "BBB EEE"),
@@ -1673,7 +1820,7 @@ test diffCleanupSemanticLossless {
Diff.init(.equal, "at."),
});
try diffCleanupSemanticLossless(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &.{
+ try testing.expectEqualDeep(@as([]const Diff, &.{
Diff.init(.equal, "The "),
Diff.init(.insert, "cow and the "),
Diff.init(.equal, "cat."),
@@ -1687,7 +1834,7 @@ test diffCleanupSemanticLossless {
Diff.init(.equal, "at."),
});
try diffCleanupSemanticLossless(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &.{
+ try testing.expectEqualDeep(@as([]const Diff, &.{
Diff.init(.equal, "The-"),
Diff.init(.insert, "cow-and-the-"),
Diff.init(.equal, "cat."),
@@ -1701,7 +1848,7 @@ test diffCleanupSemanticLossless {
Diff.init(.equal, "ax"),
});
try diffCleanupSemanticLossless(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &.{
+ try testing.expectEqualDeep(@as([]const Diff, &.{
Diff.init(.delete, "a"),
Diff.init(.equal, "aax"),
}), diffs.items);
@@ -1714,7 +1861,7 @@ test diffCleanupSemanticLossless {
Diff.init(.equal, "a"),
});
try diffCleanupSemanticLossless(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &.{
+ try testing.expectEqualDeep(@as([]const Diff, &.{
Diff.init(.equal, "xaa"),
Diff.init(.delete, "a"),
}), diffs.items);
@@ -1727,14 +1874,14 @@ test diffCleanupSemanticLossless {
Diff.init(.equal, "yyy."),
});
try diffCleanupSemanticLossless(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &.{
+ try testing.expectEqualDeep(@as([]const Diff, &.{
Diff.init(.equal, "The xxx."),
Diff.init(.insert, " The zzz."),
Diff.init(.equal, " The yyy."),
}), diffs.items);
}
-fn rebuildtexts(allocator: std.mem.Allocator, diffs: std.ArrayListUnmanaged(Diff)) ![2][]const u8 {
+fn rebuildtexts(allocator: std.mem.Allocator, diffs: DiffList) ![2][]const u8 {
var text = [2]std.ArrayList(u8){
std.ArrayList(u8).init(allocator),
std.ArrayList(u8).init(allocator),
@@ -1764,81 +1911,90 @@ test diffBisect {
// Since the resulting diff hasn't been normalized, it would be ok if
// the insertion and deletion pairs are swapped.
// If the order changes, tweak this test as required.
- var diffs = std.ArrayListUnmanaged(Diff){};
+ var diffs = DiffList{};
defer diffs.deinit(arena.allocator());
var this = default;
- try diffs.appendSlice(arena.allocator(), &.{ Diff.init(.delete, "c"), Diff.init(.insert, "m"), Diff.init(.equal, "a"), Diff.init(.delete, "t"), Diff.init(.insert, "p") });
+ try diffs.appendSlice(arena.allocator(), &.{
+ Diff.init(.delete, "c"),
+ Diff.init(.insert, "m"),
+ Diff.init(.equal, "a"),
+ Diff.init(.delete, "t"),
+ Diff.init(.insert, "p"),
+ });
// Travis TODO not sure if maxInt(u64) is correct for DateTime.MaxValue
- try std.testing.expectEqualDeep(diffs, try this.diffBisect(arena.allocator(), a, b, std.math.maxInt(u64))); // Normal.
+ try testing.expectEqualDeep(diffs, try this.diffBisect(arena.allocator(), a, b, std.math.maxInt(u64))); // Normal.
// Timeout.
diffs.items.len = 0;
- try diffs.appendSlice(arena.allocator(), &.{ Diff.init(.delete, "cat"), Diff.init(.insert, "map") });
+ try diffs.appendSlice(arena.allocator(), &.{
+ Diff.init(.delete, "cat"),
+ Diff.init(.insert, "map"),
+ });
// Travis TODO not sure if 0 is correct for DateTime.MinValue
- try std.testing.expectEqualDeep(diffs, try this.diffBisect(arena.allocator(), a, b, 0)); // Timeout.
+ try testing.expectEqualDeep(diffs, try this.diffBisect(arena.allocator(), a, b, 0)); // Timeout.
}
-const talloc = std.testing.allocator;
+const talloc = testing.allocator;
test diff {
var arena = std.heap.ArenaAllocator.init(talloc);
defer arena.deinit();
// Perform a trivial diff.
- var diffs = std.ArrayListUnmanaged(Diff){};
+ var diffs = DiffList{};
defer diffs.deinit(arena.allocator());
var this = DiffMatchPatch{};
- try std.testing.expectEqualDeep(diffs, try this.diff(arena.allocator(), "", "", false)); // diff: Null case.
+ try testing.expectEqualDeep(diffs, try this.diff(arena.allocator(), "", "", false)); // diff: Null case.
diffs.items.len = 0;
try diffs.appendSlice(arena.allocator(), &.{Diff.init(.equal, "abc")});
- try std.testing.expectEqualDeep(diffs, try this.diff(arena.allocator(), "abc", "abc", false)); // diff: Equality.
+ try testing.expectEqualDeep(diffs, try this.diff(arena.allocator(), "abc", "abc", false)); // diff: Equality.
diffs.items.len = 0;
try diffs.appendSlice(arena.allocator(), &.{ Diff.init(.equal, "ab"), Diff.init(.insert, "123"), Diff.init(.equal, "c") });
- try std.testing.expectEqualDeep(diffs, try this.diff(arena.allocator(), "abc", "ab123c", false)); // diff: Simple insertion.
+ try testing.expectEqualDeep(diffs, try this.diff(arena.allocator(), "abc", "ab123c", false)); // diff: Simple insertion.
diffs.items.len = 0;
try diffs.appendSlice(arena.allocator(), &.{ Diff.init(.equal, "a"), Diff.init(.delete, "123"), Diff.init(.equal, "bc") });
- try std.testing.expectEqualDeep(diffs, try this.diff(arena.allocator(), "a123bc", "abc", false)); // diff: Simple deletion.
+ try testing.expectEqualDeep(diffs, try this.diff(arena.allocator(), "a123bc", "abc", false)); // diff: Simple deletion.
diffs.items.len = 0;
try diffs.appendSlice(arena.allocator(), &.{ Diff.init(.equal, "a"), Diff.init(.insert, "123"), Diff.init(.equal, "b"), Diff.init(.insert, "456"), Diff.init(.equal, "c") });
- try std.testing.expectEqualDeep(diffs, try this.diff(arena.allocator(), "abc", "a123b456c", false)); // diff: Two insertions.
+ try testing.expectEqualDeep(diffs, try this.diff(arena.allocator(), "abc", "a123b456c", false)); // diff: Two insertions.
diffs.items.len = 0;
try diffs.appendSlice(arena.allocator(), &.{ Diff.init(.equal, "a"), Diff.init(.delete, "123"), Diff.init(.equal, "b"), Diff.init(.delete, "456"), Diff.init(.equal, "c") });
- try std.testing.expectEqualDeep(diffs, try this.diff(arena.allocator(), "a123b456c", "abc", false)); // diff: Two deletions.
+ try testing.expectEqualDeep(diffs, try this.diff(arena.allocator(), "a123b456c", "abc", false)); // diff: Two deletions.
// Perform a real diff.
// Switch off the timeout.
this.diff_timeout = 0;
diffs.items.len = 0;
try diffs.appendSlice(arena.allocator(), &.{ Diff.init(.delete, "a"), Diff.init(.insert, "b") });
- try std.testing.expectEqualDeep(diffs, try this.diff(arena.allocator(), "a", "b", false)); // diff: Simple case #1.
+ try testing.expectEqualDeep(diffs, try this.diff(arena.allocator(), "a", "b", false)); // diff: Simple case #1.
diffs.items.len = 0;
try diffs.appendSlice(arena.allocator(), &.{ Diff.init(.delete, "Apple"), Diff.init(.insert, "Banana"), Diff.init(.equal, "s are a"), Diff.init(.insert, "lso"), Diff.init(.equal, " fruit.") });
- try std.testing.expectEqualDeep(diffs, try this.diff(arena.allocator(), "Apples are a fruit.", "Bananas are also fruit.", false)); // diff: Simple case #2.
+ try testing.expectEqualDeep(diffs, try this.diff(arena.allocator(), "Apples are a fruit.", "Bananas are also fruit.", false)); // diff: Simple case #2.
diffs.items.len = 0;
try diffs.appendSlice(arena.allocator(), &.{ Diff.init(.delete, "a"), Diff.init(.insert, "\u{0680}"), Diff.init(.equal, "x"), Diff.init(.delete, "\t"), Diff.init(.insert, "\x00") });
- try std.testing.expectEqualDeep(diffs, try this.diff(arena.allocator(), "ax\t", "\u{0680}x\x00", false)); // diff: Simple case #3.
+ try testing.expectEqualDeep(diffs, try this.diff(arena.allocator(), "ax\t", "\u{0680}x\x00", false)); // diff: Simple case #3.
diffs.items.len = 0;
try diffs.appendSlice(arena.allocator(), &.{ Diff.init(.delete, "1"), Diff.init(.equal, "a"), Diff.init(.delete, "y"), Diff.init(.equal, "b"), Diff.init(.delete, "2"), Diff.init(.insert, "xab") });
- try std.testing.expectEqualDeep(diffs, try this.diff(arena.allocator(), "1ayb2", "abxab", false)); // diff: Overlap #1.
+ try testing.expectEqualDeep(diffs, try this.diff(arena.allocator(), "1ayb2", "abxab", false)); // diff: Overlap #1.
diffs.items.len = 0;
try diffs.appendSlice(arena.allocator(), &.{ Diff.init(.insert, "xaxcx"), Diff.init(.equal, "abc"), Diff.init(.delete, "y") });
- try std.testing.expectEqualDeep(diffs, try this.diff(arena.allocator(), "abcy", "xaxcxabc", false)); // diff: Overlap #2.
+ try testing.expectEqualDeep(diffs, try this.diff(arena.allocator(), "abcy", "xaxcxabc", false)); // diff: Overlap #2.
diffs.items.len = 0;
try diffs.appendSlice(arena.allocator(), &.{ Diff.init(.delete, "ABCD"), Diff.init(.equal, "a"), Diff.init(.delete, "="), Diff.init(.insert, "-"), Diff.init(.equal, "bcd"), Diff.init(.delete, "="), Diff.init(.insert, "-"), Diff.init(.equal, "efghijklmnopqrs"), Diff.init(.delete, "EFGHIJKLMNOefg") });
- try std.testing.expectEqualDeep(diffs, try this.diff(arena.allocator(), "ABCDa=bcd=efghijklmnopqrsEFGHIJKLMNOefg", "a-bcd-efghijklmnopqrs", false)); // diff: Overlap #3.
+ try testing.expectEqualDeep(diffs, try this.diff(arena.allocator(), "ABCDa=bcd=efghijklmnopqrsEFGHIJKLMNOefg", "a-bcd-efghijklmnopqrs", false)); // diff: Overlap #3.
diffs.items.len = 0;
try diffs.appendSlice(arena.allocator(), &.{ Diff.init(.insert, " "), Diff.init(.equal, "a"), Diff.init(.insert, "nd"), Diff.init(.equal, " [[Pennsylvania]]"), Diff.init(.delete, " and [[New") });
- try std.testing.expectEqualDeep(diffs, try this.diff(arena.allocator(), "a [[Pennsylvania]] and [[New", " and [[Pennsylvania]]", false)); // diff: Large equality.
+ try testing.expectEqualDeep(diffs, try this.diff(arena.allocator(), "a [[Pennsylvania]] and [[New", " and [[Pennsylvania]]", false)); // diff: Large equality.
this.diff_timeout = 100 * std.time.ms_per_s; // 100ms
// Increase the text lengths by 1024 times to ensure a timeout.
@@ -1849,11 +2005,11 @@ test diff {
_ = try this.diff(arena.allocator(), a, b, false); // Travis - TODO not sure what the third arg should be
const end_time = std.time.milliTimestamp();
// Test that we took at least the timeout period.
- try std.testing.expect((this.diff_timeout * 1000) * 10000 <= end_time - start_time); // diff: Timeout min.
+ try testing.expect((this.diff_timeout * 1000) * 10000 <= end_time - start_time); // diff: Timeout min.
// Test that we didn't take forever (be forgiving).
// Theoretically this test could fail very occasionally if the
// OS task swaps or locks up for a second at the wrong moment.
- try std.testing.expect((this.diff_timeout * 1000) * 10000 * 2 > end_time - start_time); // diff: Timeout max.
+ try testing.expect((this.diff_timeout * 1000) * 10000 * 2 > end_time - start_time); // diff: Timeout max.
this.diff_timeout = 0;
}
{
@@ -1861,12 +2017,12 @@ test diff {
// Must be long to pass the 100 char cutoff.
const a = "1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n";
const b = "abcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\n";
- try std.testing.expectEqualDeep(try this.diff(arena.allocator(), a, b, true), try this.diff(arena.allocator(), a, b, false)); // diff: Simple line-mode.
+ try testing.expectEqualDeep(try this.diff(arena.allocator(), a, b, true), try this.diff(arena.allocator(), a, b, false)); // diff: Simple line-mode.
}
{
const a = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
const b = "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij";
- try std.testing.expectEqualDeep(try this.diff(arena.allocator(), a, b, true), try this.diff(arena.allocator(), a, b, false)); // diff: Single line-mode.
+ try testing.expectEqualDeep(try this.diff(arena.allocator(), a, b, true), try this.diff(arena.allocator(), a, b, false)); // diff: Single line-mode.
}
const a = "1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n";
@@ -1881,7 +2037,7 @@ test diff {
arena.allocator().free(texts_textmode[0]);
arena.allocator().free(texts_textmode[1]);
}
- try std.testing.expectEqualDeep(texts_textmode, texts_linemode); // diff: Overlap line-mode.
+ try testing.expectEqualDeep(texts_textmode, texts_linemode); // diff: Overlap line-mode.
// Test null inputs -- not needed because nulls can't be passed in C#.
}
@@ -1892,16 +2048,21 @@ test diffCleanupSemantic {
// Cleanup semantically trivial equalities.
// Null case.
- var diffs = std.ArrayListUnmanaged(Diff){};
+ var diffs = DiffList{};
defer diffs.deinit(arena.allocator());
// var this = default;
try diffCleanupSemantic(arena.allocator(), &diffs);
- try std.testing.expectEqual(@as(usize, 0), diffs.items.len); // Null case
+ try testing.expectEqual(@as(usize, 0), diffs.items.len); // Null case
diffs.items.len = 0;
- try diffs.appendSlice(arena.allocator(), &.{ Diff.init(.delete, "ab"), Diff.init(.insert, "cd"), Diff.init(.equal, "12"), Diff.init(.delete, "e") });
+ try diffs.appendSlice(arena.allocator(), &.{
+ Diff.init(.delete, "ab"),
+ Diff.init(.insert, "cd"),
+ Diff.init(.equal, "12"),
+ Diff.init(.delete, "e"),
+ });
try diffCleanupSemantic(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &[_]Diff{ // No elimination #1
+ try testing.expectEqualDeep(@as([]const Diff, &[_]Diff{ // No elimination #1
Diff.init(.delete, "ab"),
Diff.init(.insert, "cd"),
Diff.init(.equal, "12"),
@@ -1909,9 +2070,14 @@ test diffCleanupSemantic {
}), diffs.items);
diffs.items.len = 0;
- try diffs.appendSlice(arena.allocator(), &.{ Diff.init(.delete, "abc"), Diff.init(.insert, "ABC"), Diff.init(.equal, "1234"), Diff.init(.delete, "wxyz") });
+ try diffs.appendSlice(arena.allocator(), &.{
+ Diff.init(.delete, "abc"),
+ Diff.init(.insert, "ABC"),
+ Diff.init(.equal, "1234"),
+ Diff.init(.delete, "wxyz"),
+ });
try diffCleanupSemantic(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &[_]Diff{ // No elimination #2
+ try testing.expectEqualDeep(@as([]const Diff, &[_]Diff{ // No elimination #2
Diff.init(.delete, "abc"),
Diff.init(.insert, "ABC"),
Diff.init(.equal, "1234"),
@@ -1919,68 +2085,107 @@ test diffCleanupSemantic {
}), diffs.items);
diffs.items.len = 0;
- try diffs.appendSlice(arena.allocator(), &.{ Diff.init(.delete, "a"), Diff.init(.equal, "b"), Diff.init(.delete, "c") });
+ try diffs.appendSlice(arena.allocator(), &.{
+ Diff.init(.delete, "a"),
+ Diff.init(.equal, "b"),
+ Diff.init(.delete, "c"),
+ });
try diffCleanupSemantic(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &[_]Diff{ // Simple elimination
+ try testing.expectEqualDeep(@as([]const Diff, &[_]Diff{ // Simple elimination
Diff.init(.delete, "abc"),
Diff.init(.insert, "b"),
}), diffs.items);
diffs.items.len = 0;
- try diffs.appendSlice(arena.allocator(), &.{ Diff.init(.delete, "ab"), Diff.init(.equal, "cd"), Diff.init(.delete, "e"), Diff.init(.equal, "f"), Diff.init(.insert, "g") });
+ try diffs.appendSlice(arena.allocator(), &.{
+ Diff.init(.delete, "ab"),
+ Diff.init(.equal, "cd"),
+ Diff.init(.delete, "e"),
+ Diff.init(.equal, "f"),
+ Diff.init(.insert, "g"),
+ });
try diffCleanupSemantic(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &[_]Diff{ // Backpass elimination
+ try testing.expectEqualDeep(@as([]const Diff, &[_]Diff{ // Backpass elimination
Diff.init(.delete, "abcdef"),
Diff.init(.insert, "cdfg"),
}), diffs.items);
diffs.items.len = 0;
- try diffs.appendSlice(arena.allocator(), &.{ Diff.init(.insert, "1"), Diff.init(.equal, "A"), Diff.init(.delete, "B"), Diff.init(.insert, "2"), Diff.init(.equal, "_"), Diff.init(.insert, "1"), Diff.init(.equal, "A"), Diff.init(.delete, "B"), Diff.init(.insert, "2") });
+ try diffs.appendSlice(arena.allocator(), &.{
+ Diff.init(.insert, "1"),
+ Diff.init(.equal, "A"),
+ Diff.init(.delete, "B"),
+ Diff.init(.insert, "2"),
+ Diff.init(.equal, "_"),
+ Diff.init(.insert, "1"),
+ Diff.init(.equal, "A"),
+ Diff.init(.delete, "B"),
+ Diff.init(.insert, "2"),
+ });
try diffCleanupSemantic(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &[_]Diff{ // Multiple elimination
+ try testing.expectEqualDeep(@as([]const Diff, &[_]Diff{ // Multiple elimination
Diff.init(.delete, "AB_AB"),
Diff.init(.insert, "1A2_1A2"),
}), diffs.items);
diffs.items.len = 0;
- try diffs.appendSlice(arena.allocator(), &.{ Diff.init(.equal, "The c"), Diff.init(.delete, "ow and the c"), Diff.init(.equal, "at.") });
+ try diffs.appendSlice(arena.allocator(), &.{
+ Diff.init(.equal, "The c"),
+ Diff.init(.delete, "ow and the c"),
+ Diff.init(.equal, "at."),
+ });
try diffCleanupSemantic(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &[_]Diff{ // Word boundaries
+ try testing.expectEqualDeep(@as([]const Diff, &[_]Diff{ // Word boundaries
Diff.init(.equal, "The "),
Diff.init(.delete, "cow and the "),
Diff.init(.equal, "cat."),
}), diffs.items);
diffs.items.len = 0;
- try diffs.appendSlice(arena.allocator(), &.{ Diff.init(.delete, "abcxx"), Diff.init(.insert, "xxdef") });
+ try diffs.appendSlice(arena.allocator(), &.{
+ Diff.init(.delete, "abcxx"),
+ Diff.init(.insert, "xxdef"),
+ });
try diffCleanupSemantic(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &[_]Diff{ // No overlap elimination
+ try testing.expectEqualDeep(@as([]const Diff, &[_]Diff{ // No overlap elimination
Diff.init(.delete, "abcxx"),
Diff.init(.insert, "xxdef"),
}), diffs.items);
diffs.items.len = 0;
- try diffs.appendSlice(arena.allocator(), &.{ Diff.init(.delete, "abcxxx"), Diff.init(.insert, "xxxdef") });
+ try diffs.appendSlice(arena.allocator(), &.{
+ Diff.init(.delete, "abcxxx"),
+ Diff.init(.insert, "xxxdef"),
+ });
try diffCleanupSemantic(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &[_]Diff{ // Overlap elimination
+ try testing.expectEqualDeep(@as([]const Diff, &[_]Diff{ // Overlap elimination
Diff.init(.delete, "abc"),
Diff.init(.equal, "xxx"),
Diff.init(.insert, "def"),
}), diffs.items);
diffs.items.len = 0;
- try diffs.appendSlice(arena.allocator(), &.{ Diff.init(.delete, "xxxabc"), Diff.init(.insert, "defxxx") });
+ try diffs.appendSlice(arena.allocator(), &.{
+ Diff.init(.delete, "xxxabc"),
+ Diff.init(.insert, "defxxx"),
+ });
try diffCleanupSemantic(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &[_]Diff{ // Reverse overlap elimination
+ try testing.expectEqualDeep(@as([]const Diff, &[_]Diff{ // Reverse overlap elimination
Diff.init(.insert, "def"),
Diff.init(.equal, "xxx"),
Diff.init(.delete, "abc"),
}), diffs.items);
diffs.items.len = 0;
- try diffs.appendSlice(arena.allocator(), &.{ Diff.init(.delete, "abcd1212"), Diff.init(.insert, "1212efghi"), Diff.init(.equal, "----"), Diff.init(.delete, "A3"), Diff.init(.insert, "3BC") });
+ try diffs.appendSlice(arena.allocator(), &.{
+ Diff.init(.delete, "abcd1212"),
+ Diff.init(.insert, "1212efghi"),
+ Diff.init(.equal, "----"),
+ Diff.init(.delete, "A3"),
+ Diff.init(.insert, "3BC"),
+ });
try diffCleanupSemantic(arena.allocator(), &diffs);
- try std.testing.expectEqualDeep(@as([]const Diff, &[_]Diff{ // Two overlap eliminations
+ try testing.expectEqualDeep(@as([]const Diff, &[_]Diff{ // Two overlap eliminations
Diff.init(.delete, "abcd"),
Diff.init(.equal, "1212"),
Diff.init(.insert, "efghi"),