Comparing version 1.2.0 to 1.3.0
declare function diff( | ||
text1: string, | ||
text2: string, | ||
cursorPos?: number | diff.CursorInfo | ||
text1: string, | ||
text2: string, | ||
cursorPos?: number | diff.CursorInfo, | ||
cleanup?: boolean | ||
): diff.Diff[]; | ||
declare namespace diff { | ||
type Diff = [-1 | 0 | 1, string]; | ||
type Diff = [-1 | 0 | 1, string]; | ||
const DELETE: -1; | ||
const INSERT: 1; | ||
const EQUAL: 0; | ||
const DELETE: -1; | ||
const INSERT: 1; | ||
const EQUAL: 0; | ||
interface CursorInfo { | ||
oldRange: { index: number; length: number }; | ||
newRange: { index: number; length: number }; | ||
} | ||
interface CursorInfo { | ||
oldRange: { index: number; length: number }; | ||
newRange: { index: number; length: number }; | ||
} | ||
} | ||
export = diff; |
498
diff.js
@@ -26,3 +26,2 @@ /** | ||
/** | ||
@@ -37,3 +36,2 @@ * The data structure representing a diff is an array of tuples: | ||
/** | ||
@@ -45,5 +43,6 @@ * Find the differences between two texts. Simplifies the problem by stripping | ||
* @param {Int|Object} [cursor_pos] Edit position in text1 or object with more info | ||
* @param {boolean} [cleanup] Apply semantic cleanup before returning. | ||
* @return {Array} Array of diff tuples. | ||
*/ | ||
function diff_main(text1, text2, cursor_pos, _fix_unicode) { | ||
function diff_main(text1, text2, cursor_pos, cleanup, _fix_unicode) { | ||
// Check for equality | ||
@@ -87,6 +86,8 @@ if (text1 === text2) { | ||
diff_cleanupMerge(diffs, _fix_unicode); | ||
if (cleanup) { | ||
diff_cleanupSemantic(diffs); | ||
} | ||
return diffs; | ||
}; | ||
} | ||
/** | ||
@@ -120,3 +121,3 @@ * Find the differences between two texts. Assumes that the texts do not | ||
[DIFF_EQUAL, shorttext], | ||
[DIFF_INSERT, longtext.substring(i + shorttext.length)] | ||
[DIFF_INSERT, longtext.substring(i + shorttext.length)], | ||
]; | ||
@@ -133,3 +134,6 @@ // Swap insertions for deletions if diff is reversed. | ||
// After the previous speedup, the character can't be an equality. | ||
return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]]; | ||
return [ | ||
[DIFF_DELETE, text1], | ||
[DIFF_INSERT, text2], | ||
]; | ||
} | ||
@@ -154,5 +158,4 @@ | ||
return diff_bisect_(text1, text2); | ||
}; | ||
} | ||
/** | ||
@@ -187,3 +190,3 @@ * Find the 'middle snake' of a diff, split the problem in two | ||
// with the reverse path. | ||
var front = (delta % 2 !== 0); | ||
var front = delta % 2 !== 0; | ||
// Offsets for start and end of k loop. | ||
@@ -207,3 +210,4 @@ // Prevents mapping of space beyond the grid. | ||
while ( | ||
x1 < text1_length && y1 < text2_length && | ||
x1 < text1_length && | ||
y1 < text2_length && | ||
text1.charAt(x1) === text2.charAt(y1) | ||
@@ -245,4 +249,6 @@ ) { | ||
while ( | ||
x2 < text1_length && y2 < text2_length && | ||
text1.charAt(text1_length - x2 - 1) === text2.charAt(text2_length - y2 - 1) | ||
x2 < text1_length && | ||
y2 < text2_length && | ||
text1.charAt(text1_length - x2 - 1) === | ||
text2.charAt(text2_length - y2 - 1) | ||
) { | ||
@@ -276,6 +282,8 @@ x2++; | ||
// number of diffs equals number of characters, no commonality at all. | ||
return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]]; | ||
}; | ||
return [ | ||
[DIFF_DELETE, text1], | ||
[DIFF_INSERT, text2], | ||
]; | ||
} | ||
/** | ||
@@ -301,5 +309,4 @@ * Given the location of the 'middle snake', split the diff in two parts | ||
return diffs.concat(diffsb); | ||
}; | ||
} | ||
/** | ||
@@ -341,5 +348,54 @@ * Determine the common prefix of two strings. | ||
return pointermid; | ||
}; | ||
} | ||
/** | ||
* Determine if the suffix of one string is the prefix of another. | ||
* @param {string} text1 First string. | ||
* @param {string} text2 Second string. | ||
* @return {number} The number of characters common to the end of the first | ||
* string and the start of the second string. | ||
* @private | ||
*/ | ||
function diff_commonOverlap_(text1, text2) { | ||
// Cache the text lengths to prevent multiple calls. | ||
var text1_length = text1.length; | ||
var text2_length = text2.length; | ||
// Eliminate the null case. | ||
if (text1_length == 0 || text2_length == 0) { | ||
return 0; | ||
} | ||
// Truncate the longer string. | ||
if (text1_length > text2_length) { | ||
text1 = text1.substring(text1_length - text2_length); | ||
} else if (text1_length < text2_length) { | ||
text2 = text2.substring(0, text1_length); | ||
} | ||
var text_length = Math.min(text1_length, text2_length); | ||
// Quick check for the worst case. | ||
if (text1 == text2) { | ||
return text_length; | ||
} | ||
// Start by looking for a single character match | ||
// and increase length until no match is found. | ||
// Performance analysis: http://neil.fraser.name/news/2010/11/04/ | ||
var best = 0; | ||
var length = 1; | ||
while (true) { | ||
var pattern = text1.substring(text_length - length); | ||
var found = text2.indexOf(pattern); | ||
if (found == -1) { | ||
return best; | ||
} | ||
length += found; | ||
if ( | ||
found == 0 || | ||
text1.substring(text_length - length) == text2.substring(0, length) | ||
) { | ||
best = length; | ||
length++; | ||
} | ||
} | ||
} | ||
/** | ||
@@ -380,5 +436,4 @@ * Determine the common suffix of two strings. | ||
return pointermid; | ||
}; | ||
} | ||
/** | ||
@@ -398,3 +453,3 @@ * Do the two texts share a substring which is at least half the length of the | ||
if (longtext.length < 4 || shorttext.length * 2 < longtext.length) { | ||
return null; // Pointless. | ||
return null; // Pointless. | ||
} | ||
@@ -418,12 +473,17 @@ | ||
var j = -1; | ||
var best_common = ''; | ||
var best_common = ""; | ||
var best_longtext_a, best_longtext_b, best_shorttext_a, best_shorttext_b; | ||
while ((j = shorttext.indexOf(seed, j + 1)) !== -1) { | ||
var prefixLength = diff_commonPrefix( | ||
longtext.substring(i), shorttext.substring(j)); | ||
longtext.substring(i), | ||
shorttext.substring(j) | ||
); | ||
var suffixLength = diff_commonSuffix( | ||
longtext.substring(0, i), shorttext.substring(0, j)); | ||
longtext.substring(0, i), | ||
shorttext.substring(0, j) | ||
); | ||
if (best_common.length < suffixLength + prefixLength) { | ||
best_common = shorttext.substring( | ||
j - suffixLength, j) + shorttext.substring(j, j + prefixLength); | ||
best_common = | ||
shorttext.substring(j - suffixLength, j) + | ||
shorttext.substring(j, j + prefixLength); | ||
best_longtext_a = longtext.substring(0, i - suffixLength); | ||
@@ -437,4 +497,7 @@ best_longtext_b = longtext.substring(i + prefixLength); | ||
return [ | ||
best_longtext_a, best_longtext_b, | ||
best_shorttext_a, best_shorttext_b, best_common | ||
best_longtext_a, | ||
best_longtext_b, | ||
best_shorttext_a, | ||
best_shorttext_b, | ||
best_common, | ||
]; | ||
@@ -447,5 +510,13 @@ } else { | ||
// First check if the second quarter is the seed for a half-match. | ||
var hm1 = diff_halfMatchI_(longtext, shorttext, Math.ceil(longtext.length / 4)); | ||
var hm1 = diff_halfMatchI_( | ||
longtext, | ||
shorttext, | ||
Math.ceil(longtext.length / 4) | ||
); | ||
// Check again based on the third quarter. | ||
var hm2 = diff_halfMatchI_(longtext, shorttext, Math.ceil(longtext.length / 2)); | ||
var hm2 = diff_halfMatchI_( | ||
longtext, | ||
shorttext, | ||
Math.ceil(longtext.length / 2) | ||
); | ||
var hm; | ||
@@ -478,6 +549,265 @@ if (!hm1 && !hm2) { | ||
return [text1_a, text1_b, text2_a, text2_b, mid_common]; | ||
}; | ||
} | ||
/** | ||
* Reduce the number of edits by eliminating semantically trivial equalities. | ||
* @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples. | ||
*/ | ||
function diff_cleanupSemantic(diffs) { | ||
var changes = false; | ||
var equalities = []; // Stack of indices where equalities are found. | ||
var equalitiesLength = 0; // Keeping our own length var is faster in JS. | ||
/** @type {?string} */ | ||
var lastequality = null; | ||
// Always equal to diffs[equalities[equalitiesLength - 1]][1] | ||
var pointer = 0; // Index of current position. | ||
// Number of characters that changed prior to the equality. | ||
var length_insertions1 = 0; | ||
var length_deletions1 = 0; | ||
// Number of characters that changed after the equality. | ||
var length_insertions2 = 0; | ||
var length_deletions2 = 0; | ||
while (pointer < diffs.length) { | ||
if (diffs[pointer][0] == DIFF_EQUAL) { | ||
// Equality found. | ||
equalities[equalitiesLength++] = pointer; | ||
length_insertions1 = length_insertions2; | ||
length_deletions1 = length_deletions2; | ||
length_insertions2 = 0; | ||
length_deletions2 = 0; | ||
lastequality = diffs[pointer][1]; | ||
} else { | ||
// An insertion or deletion. | ||
if (diffs[pointer][0] == DIFF_INSERT) { | ||
length_insertions2 += diffs[pointer][1].length; | ||
} else { | ||
length_deletions2 += diffs[pointer][1].length; | ||
} | ||
// Eliminate an equality that is smaller or equal to the edits on both | ||
// sides of it. | ||
if ( | ||
lastequality && | ||
lastequality.length <= | ||
Math.max(length_insertions1, length_deletions1) && | ||
lastequality.length <= Math.max(length_insertions2, length_deletions2) | ||
) { | ||
// Duplicate record. | ||
diffs.splice(equalities[equalitiesLength - 1], 0, [ | ||
DIFF_DELETE, | ||
lastequality, | ||
]); | ||
// Change second copy to insert. | ||
diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT; | ||
// Throw away the equality we just deleted. | ||
equalitiesLength--; | ||
// Throw away the previous equality (it needs to be reevaluated). | ||
equalitiesLength--; | ||
pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1; | ||
length_insertions1 = 0; // Reset the counters. | ||
length_deletions1 = 0; | ||
length_insertions2 = 0; | ||
length_deletions2 = 0; | ||
lastequality = null; | ||
changes = true; | ||
} | ||
} | ||
pointer++; | ||
} | ||
// Normalize the diff. | ||
if (changes) { | ||
diff_cleanupMerge(diffs); | ||
} | ||
diff_cleanupSemanticLossless(diffs); | ||
// Find any overlaps between deletions and insertions. | ||
// e.g: <del>abcxxx</del><ins>xxxdef</ins> | ||
// -> <del>abc</del>xxx<ins>def</ins> | ||
// e.g: <del>xxxabc</del><ins>defxxx</ins> | ||
// -> <ins>def</ins>xxx<del>abc</del> | ||
// Only extract an overlap if it is as big as the edit ahead or behind it. | ||
pointer = 1; | ||
while (pointer < diffs.length) { | ||
if ( | ||
diffs[pointer - 1][0] == DIFF_DELETE && | ||
diffs[pointer][0] == DIFF_INSERT | ||
) { | ||
var deletion = diffs[pointer - 1][1]; | ||
var insertion = diffs[pointer][1]; | ||
var overlap_length1 = diff_commonOverlap_(deletion, insertion); | ||
var overlap_length2 = diff_commonOverlap_(insertion, deletion); | ||
if (overlap_length1 >= overlap_length2) { | ||
if ( | ||
overlap_length1 >= deletion.length / 2 || | ||
overlap_length1 >= insertion.length / 2 | ||
) { | ||
// Overlap found. Insert an equality and trim the surrounding edits. | ||
diffs.splice(pointer, 0, [ | ||
DIFF_EQUAL, | ||
insertion.substring(0, overlap_length1), | ||
]); | ||
diffs[pointer - 1][1] = deletion.substring( | ||
0, | ||
deletion.length - overlap_length1 | ||
); | ||
diffs[pointer + 1][1] = insertion.substring(overlap_length1); | ||
pointer++; | ||
} | ||
} else { | ||
if ( | ||
overlap_length2 >= deletion.length / 2 || | ||
overlap_length2 >= insertion.length / 2 | ||
) { | ||
// Reverse overlap found. | ||
// Insert an equality and swap and trim the surrounding edits. | ||
diffs.splice(pointer, 0, [ | ||
DIFF_EQUAL, | ||
deletion.substring(0, overlap_length2), | ||
]); | ||
diffs[pointer - 1][0] = DIFF_INSERT; | ||
diffs[pointer - 1][1] = insertion.substring( | ||
0, | ||
insertion.length - overlap_length2 | ||
); | ||
diffs[pointer + 1][0] = DIFF_DELETE; | ||
diffs[pointer + 1][1] = deletion.substring(overlap_length2); | ||
pointer++; | ||
} | ||
} | ||
pointer++; | ||
} | ||
pointer++; | ||
} | ||
} | ||
var nonAlphaNumericRegex_ = /[^a-zA-Z0-9]/; | ||
var whitespaceRegex_ = /\s/; | ||
var linebreakRegex_ = /[\r\n]/; | ||
var blanklineEndRegex_ = /\n\r?\n$/; | ||
var blanklineStartRegex_ = /^\r?\n\r?\n/; | ||
/** | ||
* Look for single edits surrounded on both sides by equalities | ||
* which can be shifted sideways to align the edit to a word boundary. | ||
* e.g: The c<ins>at c</ins>ame. -> The <ins>cat </ins>came. | ||
* @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples. | ||
*/ | ||
function diff_cleanupSemanticLossless(diffs) { | ||
/** | ||
* Given two strings, compute a score representing whether the internal | ||
* boundary falls on logical boundaries. | ||
* Scores range from 6 (best) to 0 (worst). | ||
* Closure, but does not reference any external variables. | ||
* @param {string} one First string. | ||
* @param {string} two Second string. | ||
* @return {number} The score. | ||
* @private | ||
*/ | ||
function diff_cleanupSemanticScore_(one, two) { | ||
if (!one || !two) { | ||
// Edges are the best. | ||
return 6; | ||
} | ||
// Each port of this function behaves slightly differently due to | ||
// subtle differences in each language's definition of things like | ||
// 'whitespace'. Since this function's purpose is largely cosmetic, | ||
// the choice has been made to use each language's native features | ||
// rather than force total conformity. | ||
var char1 = one.charAt(one.length - 1); | ||
var char2 = two.charAt(0); | ||
var nonAlphaNumeric1 = char1.match(nonAlphaNumericRegex_); | ||
var nonAlphaNumeric2 = char2.match(nonAlphaNumericRegex_); | ||
var whitespace1 = nonAlphaNumeric1 && char1.match(whitespaceRegex_); | ||
var whitespace2 = nonAlphaNumeric2 && char2.match(whitespaceRegex_); | ||
var lineBreak1 = whitespace1 && char1.match(linebreakRegex_); | ||
var lineBreak2 = whitespace2 && char2.match(linebreakRegex_); | ||
var blankLine1 = lineBreak1 && one.match(blanklineEndRegex_); | ||
var blankLine2 = lineBreak2 && two.match(blanklineStartRegex_); | ||
if (blankLine1 || blankLine2) { | ||
// Five points for blank lines. | ||
return 5; | ||
} else if (lineBreak1 || lineBreak2) { | ||
// Four points for line breaks. | ||
return 4; | ||
} else if (nonAlphaNumeric1 && !whitespace1 && whitespace2) { | ||
// Three points for end of sentences. | ||
return 3; | ||
} else if (whitespace1 || whitespace2) { | ||
// Two points for whitespace. | ||
return 2; | ||
} else if (nonAlphaNumeric1 || nonAlphaNumeric2) { | ||
// One point for non-alphanumeric. | ||
return 1; | ||
} | ||
return 0; | ||
} | ||
var pointer = 1; | ||
// Intentionally ignore the first and last element (don't need checking). | ||
while (pointer < diffs.length - 1) { | ||
if ( | ||
diffs[pointer - 1][0] == DIFF_EQUAL && | ||
diffs[pointer + 1][0] == DIFF_EQUAL | ||
) { | ||
// This is a single edit surrounded by equalities. | ||
var equality1 = diffs[pointer - 1][1]; | ||
var edit = diffs[pointer][1]; | ||
var equality2 = diffs[pointer + 1][1]; | ||
// First, shift the edit as far left as possible. | ||
var commonOffset = diff_commonSuffix(equality1, edit); | ||
if (commonOffset) { | ||
var commonString = edit.substring(edit.length - commonOffset); | ||
equality1 = equality1.substring(0, equality1.length - commonOffset); | ||
edit = commonString + edit.substring(0, edit.length - commonOffset); | ||
equality2 = commonString + equality2; | ||
} | ||
// Second, step character by character right, looking for the best fit. | ||
var bestEquality1 = equality1; | ||
var bestEdit = edit; | ||
var bestEquality2 = equality2; | ||
var bestScore = | ||
diff_cleanupSemanticScore_(equality1, edit) + | ||
diff_cleanupSemanticScore_(edit, equality2); | ||
while (edit.charAt(0) === equality2.charAt(0)) { | ||
equality1 += edit.charAt(0); | ||
edit = edit.substring(1) + equality2.charAt(0); | ||
equality2 = equality2.substring(1); | ||
var score = | ||
diff_cleanupSemanticScore_(equality1, edit) + | ||
diff_cleanupSemanticScore_(edit, equality2); | ||
// The >= encourages trailing rather than leading whitespace on edits. | ||
if (score >= bestScore) { | ||
bestScore = score; | ||
bestEquality1 = equality1; | ||
bestEdit = edit; | ||
bestEquality2 = equality2; | ||
} | ||
} | ||
if (diffs[pointer - 1][1] != bestEquality1) { | ||
// We have an improvement, save it back to the diff. | ||
if (bestEquality1) { | ||
diffs[pointer - 1][1] = bestEquality1; | ||
} else { | ||
diffs.splice(pointer - 1, 1); | ||
pointer--; | ||
} | ||
diffs[pointer][1] = bestEdit; | ||
if (bestEquality2) { | ||
diffs[pointer + 1][1] = bestEquality2; | ||
} else { | ||
diffs.splice(pointer + 1, 1); | ||
pointer--; | ||
} | ||
} | ||
} | ||
pointer++; | ||
} | ||
} | ||
/** | ||
* Reorder and merge like edit sections. Merge equalities. | ||
@@ -489,8 +819,8 @@ * Any edit section can move as long as it doesn't cross an equality. | ||
function diff_cleanupMerge(diffs, fix_unicode) { | ||
diffs.push([DIFF_EQUAL, '']); // Add a dummy entry at the end. | ||
diffs.push([DIFF_EQUAL, ""]); // Add a dummy entry at the end. | ||
var pointer = 0; | ||
var count_delete = 0; | ||
var count_insert = 0; | ||
var text_delete = ''; | ||
var text_insert = ''; | ||
var text_delete = ""; | ||
var text_insert = ""; | ||
var commonlength; | ||
@@ -504,3 +834,2 @@ while (pointer < diffs.length) { | ||
case DIFF_INSERT: | ||
count_insert++; | ||
@@ -528,5 +857,11 @@ text_insert += diffs[pointer][1]; | ||
// and we keep scanning for the next equality before rewriting the tuples. | ||
if (previous_equality >= 0 && ends_with_pair_start(diffs[previous_equality][1])) { | ||
if ( | ||
previous_equality >= 0 && | ||
ends_with_pair_start(diffs[previous_equality][1]) | ||
) { | ||
var stray = diffs[previous_equality][1].slice(-1); | ||
diffs[previous_equality][1] = diffs[previous_equality][1].slice(0, -1); | ||
diffs[previous_equality][1] = diffs[previous_equality][1].slice( | ||
0, | ||
-1 | ||
); | ||
text_delete = stray + text_delete; | ||
@@ -571,5 +906,11 @@ text_insert = stray + text_insert; | ||
if (previous_equality >= 0) { | ||
diffs[previous_equality][1] += text_insert.substring(0, commonlength); | ||
diffs[previous_equality][1] += text_insert.substring( | ||
0, | ||
commonlength | ||
); | ||
} else { | ||
diffs.splice(0, 0, [DIFF_EQUAL, text_insert.substring(0, commonlength)]); | ||
diffs.splice(0, 0, [ | ||
DIFF_EQUAL, | ||
text_insert.substring(0, commonlength), | ||
]); | ||
pointer++; | ||
@@ -584,5 +925,12 @@ } | ||
diffs[pointer][1] = | ||
text_insert.substring(text_insert.length - commonlength) + diffs[pointer][1]; | ||
text_insert = text_insert.substring(0, text_insert.length - commonlength); | ||
text_delete = text_delete.substring(0, text_delete.length - commonlength); | ||
text_insert.substring(text_insert.length - commonlength) + | ||
diffs[pointer][1]; | ||
text_insert = text_insert.substring( | ||
0, | ||
text_insert.length - commonlength | ||
); | ||
text_delete = text_delete.substring( | ||
0, | ||
text_delete.length - commonlength | ||
); | ||
} | ||
@@ -602,3 +950,8 @@ } | ||
} else { | ||
diffs.splice(pointer - n, n, [DIFF_DELETE, text_delete], [DIFF_INSERT, text_insert]); | ||
diffs.splice( | ||
pointer - n, | ||
n, | ||
[DIFF_DELETE, text_delete], | ||
[DIFF_INSERT, text_insert] | ||
); | ||
pointer = pointer - n + 2; | ||
@@ -616,9 +969,9 @@ } | ||
count_delete = 0; | ||
text_delete = ''; | ||
text_insert = ''; | ||
text_delete = ""; | ||
text_insert = ""; | ||
break; | ||
} | ||
} | ||
if (diffs[diffs.length - 1][1] === '') { | ||
diffs.pop(); // Remove the dummy entry at the end. | ||
if (diffs[diffs.length - 1][1] === "") { | ||
diffs.pop(); // Remove the dummy entry at the end. | ||
} | ||
@@ -633,16 +986,26 @@ | ||
while (pointer < diffs.length - 1) { | ||
if (diffs[pointer - 1][0] === DIFF_EQUAL && | ||
diffs[pointer + 1][0] === DIFF_EQUAL) { | ||
if ( | ||
diffs[pointer - 1][0] === DIFF_EQUAL && | ||
diffs[pointer + 1][0] === DIFF_EQUAL | ||
) { | ||
// This is a single edit surrounded by equalities. | ||
if (diffs[pointer][1].substring(diffs[pointer][1].length - | ||
diffs[pointer - 1][1].length) === diffs[pointer - 1][1]) { | ||
if ( | ||
diffs[pointer][1].substring( | ||
diffs[pointer][1].length - diffs[pointer - 1][1].length | ||
) === diffs[pointer - 1][1] | ||
) { | ||
// Shift the edit over the previous equality. | ||
diffs[pointer][1] = diffs[pointer - 1][1] + | ||
diffs[pointer][1].substring(0, diffs[pointer][1].length - | ||
diffs[pointer - 1][1].length); | ||
diffs[pointer][1] = | ||
diffs[pointer - 1][1] + | ||
diffs[pointer][1].substring( | ||
0, | ||
diffs[pointer][1].length - diffs[pointer - 1][1].length | ||
); | ||
diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1]; | ||
diffs.splice(pointer - 1, 1); | ||
changes = true; | ||
} else if (diffs[pointer][1].substring(0, diffs[pointer + 1][1].length) == | ||
diffs[pointer + 1][1]) { | ||
} else if ( | ||
diffs[pointer][1].substring(0, diffs[pointer + 1][1].length) == | ||
diffs[pointer + 1][1] | ||
) { | ||
// Shift the edit over the next equality. | ||
@@ -663,10 +1026,10 @@ diffs[pointer - 1][1] += diffs[pointer + 1][1]; | ||
} | ||
}; | ||
} | ||
function is_surrogate_pair_start(charCode) { | ||
return charCode >= 0xD800 && charCode <= 0xDBFF; | ||
return charCode >= 0xd800 && charCode <= 0xdbff; | ||
} | ||
function is_surrogate_pair_end(charCode) { | ||
return charCode >= 0xDC00 && charCode <= 0xDFFF; | ||
return charCode >= 0xdc00 && charCode <= 0xdfff; | ||
} | ||
@@ -700,3 +1063,3 @@ | ||
[DIFF_INSERT, newMiddle], | ||
[DIFF_EQUAL, after] | ||
[DIFF_EQUAL, after], | ||
]); | ||
@@ -707,6 +1070,7 @@ } | ||
// note: this runs after equality check has ruled out exact equality | ||
var oldRange = typeof cursor_pos === 'number' ? | ||
{ index: cursor_pos, length: 0 } : cursor_pos.oldRange; | ||
var newRange = typeof cursor_pos === 'number' ? | ||
null : cursor_pos.newRange; | ||
var oldRange = | ||
typeof cursor_pos === "number" | ||
? { index: cursor_pos, length: 0 } | ||
: cursor_pos.oldRange; | ||
var newRange = typeof cursor_pos === "number" ? null : cursor_pos.newRange; | ||
// take into account the old and new selection to generate the best diff | ||
@@ -794,6 +1158,6 @@ // possible for a text edit. for example, a text change from "xxx" to "xx" | ||
function diff(text1, text2, cursor_pos) { | ||
function diff(text1, text2, cursor_pos, cleanup) { | ||
// only pass fix_unicode=true at the top level, not when diff_main is | ||
// recursively invoked | ||
return diff_main(text1, text2, cursor_pos, true); | ||
return diff_main(text1, text2, cursor_pos, cleanup, true); | ||
} | ||
@@ -800,0 +1164,0 @@ |
{ | ||
"name": "fast-diff", | ||
"version": "1.2.0", | ||
"version": "1.3.0", | ||
"description": "Fast Javascript text diff", | ||
@@ -8,5 +8,9 @@ "author": "Jason Chen <jhchen7@gmail.com>", | ||
"types": "diff.d.ts", | ||
"files": [ | ||
"diff.d.ts" | ||
], | ||
"devDependencies": { | ||
"lodash": "~3.9.3", | ||
"seedrandom": "~2.4.0" | ||
"lodash": "~4.17.21", | ||
"nyc": "~15.1.0", | ||
"seedrandom": "~3.0.5" | ||
}, | ||
@@ -21,3 +25,3 @@ "repository": { | ||
"scripts": { | ||
"test": "node test.js" | ||
"test": "nyc node test.js" | ||
}, | ||
@@ -24,0 +28,0 @@ "license": "Apache-2.0", |
@@ -1,2 +0,2 @@ | ||
# Fast Diff [![Build Status](https://travis-ci.org/jhchen/fast-diff.svg)](https://travis-ci.org/jhchen/fast-diff) | ||
# Fast Diff ![Build Status](https://github.com/jhchen/fast-diff/actions/workflows/test.yml/badge.svg) | ||
@@ -3,0 +3,0 @@ This is a simplified import of the excellent [diff-match-patch](https://code.google.com/p/google-diff-match-patch/) library by [Neil Fraser](https://neil.fraser.name/) into the Node.js environment. The match and patch parts are removed, as well as all the extra diff options. What remains is incredibly fast diffing between two strings. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
52314
1100
3
5