ng-diff-match-patch
Version:
A Diff-Match-Patch component for your Angular 2 + applications
1,204 lines (1,193 loc) • 359 kB
JavaScript
import { Injectable, Component, Input, Directive, ElementRef, NgModule } from '@angular/core';
import { __values } from 'tslib';
import { CommonModule } from '@angular/common';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
/**
* Class containing the diff, match and patch methods.
*/
var /**
* Class containing the diff, match and patch methods.
*/
DiffMatchPatch = /** @class */ (function () {
function DiffMatchPatch() {
// Defaults.
// Redefine these in your program to override the defaults.
// Number of seconds to map a diff before giving up (0 for infinity).
this.Diff_Timeout = 1.0;
// Cost of an empty edit operation in terms of edit characters.
this.Diff_EditCost = 4;
// At what point is no match declared (0.0 = perfection, 1.0 = very loose).
this.Match_Threshold = 0.5;
// How far to search for a match (0 = exact location, 1000+ = broad match).
// A match this many characters away from the expected location will add
// 1.0 to the score (0.0 is a perfect match).
this.Match_Distance = 1000;
// When deleting a large block of text (over ~64 characters), how close do
// the contents have to be to match the expected contents. (0.0 = perfection,
// 1.0 = very loose). Note that Match_Threshold controls how closely the
// end points of a delete need to match.
this.Patch_DeleteThreshold = 0.5;
// Chunk size for context length.
this.Patch_Margin = 4;
// The number of bits in an int.
this.Match_MaxBits = 32;
/**
* The data structure representing a diff is an array of tuples:
* [[DiffOp.Delete, 'Hello'], [DiffOp.Insert, 'Goodbye'], [DiffOp.Equal, ' world.']]
* which means: delete 'Hello', add 'Goodbye' and keep ' world.'
*/
this.whitespaceRegex_ = new RegExp('/\s/');
this.linebreakRegex_ = new RegExp('/[\r\n]/');
this.blanklineEndRegex_ = new RegExp('/\n\r?\n$/');
this.blanklineStartRegex_ = new RegExp('/^\r?\n\r?\n/');
/**
* Convert a diff array into a pretty HTML report.
* @param diffs Array of diff tuples.
* @return HTML representation.
*/
this.diff_prettyHtml = function (diffs) {
/** @type {?} */
var html = [];
/** @type {?} */
var pattern_amp = /&/g;
/** @type {?} */
var pattern_lt = /</g;
/** @type {?} */
var pattern_gt = />/g;
/** @type {?} */
var pattern_para = /\n/g;
for (var x = 0; x < diffs.length; x++) {
/** @type {?} */
var op = diffs[x][0];
/** @type {?} */
var data = diffs[x][1];
/** @type {?} */
var text = data.replace(pattern_amp, '&').replace(pattern_lt, '<')
.replace(pattern_gt, '>').replace(pattern_para, '¶<br>');
switch (op) {
case 1 /* Insert */:
html[x] = '<ins style="background:#e6ffe6;">' + text + '</ins>';
break;
case -1 /* Delete */:
html[x] = '<del style="background:#ffe6e6;">' + text + '</del>';
break;
case 0 /* Equal */:
html[x] = '<span>' + text + '</span>';
break;
}
}
return html.join('');
};
/**
* Look through the patches and break up any which are longer than the maximum
* limit of the match algorithm.
* Intended to be called only from within patch_apply.
* @param patches Array of Patch objects.
*/
this.patch_splitMax = function (patches) {
/** @type {?} */
var patch_size = this.Match_MaxBits;
for (var x = 0; x < patches.length; x++) {
if (patches[x].length1 <= patch_size) {
continue;
}
/** @type {?} */
var bigpatch = patches[x];
// Remove the big old patch.
patches.splice(x--, 1);
/** @type {?} */
var start1 = bigpatch.start1;
/** @type {?} */
var start2 = bigpatch.start2;
/** @type {?} */
var precontext = '';
while (bigpatch.diffs.length !== 0) {
/** @type {?} */
var patch = new patch_obj();
/** @type {?} */
var empty = true;
patch.start1 = start1 - precontext.length;
patch.start2 = start2 - precontext.length;
if (precontext !== '') {
patch.length1 = patch.length2 = precontext.length;
patch.diffs.push([0 /* Equal */, precontext]);
}
while (bigpatch.diffs.length !== 0 &&
patch.length1 < patch_size - this.Patch_Margin) {
/** @type {?} */
var diff_type = bigpatch.diffs[0][0];
/** @type {?} */
var diff_text = bigpatch.diffs[0][1];
if (diff_type === 1 /* Insert */) {
// Insertions are harmless.
patch.length2 += diff_text.length;
start2 += diff_text.length;
patch.diffs.push(bigpatch.diffs.shift());
empty = false;
}
else if (diff_type === -1 /* Delete */ && patch.diffs.length == 1 &&
patch.diffs[0][0] == 0 /* Equal */ &&
diff_text.length > 2 * patch_size) {
// This is a large deletion. Let it pass in one chunk.
patch.length1 += diff_text.length;
start1 += diff_text.length;
empty = false;
patch.diffs.push([diff_type, diff_text]);
bigpatch.diffs.shift();
}
else {
// Deletion or equality. Only take as much as we can stomach.
diff_text = diff_text.substring(0, patch_size - patch.length1 - this.Patch_Margin);
patch.length1 += diff_text.length;
start1 += diff_text.length;
if (diff_type === 0 /* Equal */) {
patch.length2 += diff_text.length;
start2 += diff_text.length;
}
else {
empty = false;
}
patch.diffs.push([diff_type, diff_text]);
if (diff_text == bigpatch.diffs[0][1]) {
bigpatch.diffs.shift();
}
else {
bigpatch.diffs[0][1] =
bigpatch.diffs[0][1].substring(diff_text.length);
}
}
}
// Compute the head context for the next patch.
precontext = this.diff_text2(patch.diffs);
precontext =
precontext.substring(precontext.length - this.Patch_Margin);
/** @type {?} */
var postcontext = this.diff_text1(bigpatch.diffs)
.substring(0, this.Patch_Margin);
if (postcontext !== '') {
patch.length1 += postcontext.length;
patch.length2 += postcontext.length;
if (patch.diffs.length !== 0 &&
patch.diffs[patch.diffs.length - 1][0] === 0 /* Equal */) {
patch.diffs[patch.diffs.length - 1][1] += postcontext;
}
else {
patch.diffs.push([0 /* Equal */, postcontext]);
}
}
if (!empty) {
patches.splice(++x, 0, patch);
}
}
}
};
}
/**
* Find the differences between two texts. Simplifies the problem by stripping
* any common prefix or suffix off the texts before diffing.
* @param text1 Old string to be diffed.
* @param text2 New string to be diffed.
* @param opt_checklines 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.
* @param opt_deadline Optional time when the diff should be complete
* by. Used internally for recursive calls. Users should set DiffTimeout
* instead.
* @return Array of diff tuples.
*/
/**
* Find the differences between two texts. Simplifies the problem by stripping
* any common prefix or suffix off the texts before diffing.
* @param {?} text1 Old string to be diffed.
* @param {?} text2 New string to be diffed.
* @param {?=} opt_checklines 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.
* @param {?=} opt_deadline Optional time when the diff should be complete
* by. Used internally for recursive calls. Users should set DiffTimeout
* instead.
* @return {?} Array of diff tuples.
*/
DiffMatchPatch.prototype.diff_main = /**
* Find the differences between two texts. Simplifies the problem by stripping
* any common prefix or suffix off the texts before diffing.
* @param {?} text1 Old string to be diffed.
* @param {?} text2 New string to be diffed.
* @param {?=} opt_checklines 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.
* @param {?=} opt_deadline Optional time when the diff should be complete
* by. Used internally for recursive calls. Users should set DiffTimeout
* instead.
* @return {?} Array of diff tuples.
*/
function (text1, text2, opt_checklines, opt_deadline) {
// Set a deadline by which time the diff must be complete.
if (typeof opt_deadline == 'undefined') {
if (this.Diff_Timeout <= 0) {
opt_deadline = Number.MAX_VALUE;
}
else {
opt_deadline = (new Date).getTime() + this.Diff_Timeout * 1000;
}
}
/** @type {?} */
var deadline = opt_deadline;
// Check for null inputs.
if (text1 == null || text2 == null) {
throw new Error('Null input. (diff_main)');
}
// Check for equality (speedup).
if (text1 == text2) {
if (text1) {
return [[0 /* Equal */, text1]];
}
return [];
}
if (typeof opt_checklines == 'undefined') {
opt_checklines = true;
}
/** @type {?} */
var checklines = opt_checklines;
/** @type {?} */
var commonlength = this.diff_commonPrefix(text1, text2);
/** @type {?} */
var commonprefix = text1.substring(0, commonlength);
text1 = text1.substring(commonlength);
text2 = text2.substring(commonlength);
// Trim off common suffix (speedup).
commonlength = this.diff_commonSuffix(text1, text2);
/** @type {?} */
var commonsuffix = text1.substring(text1.length - commonlength);
text1 = text1.substring(0, text1.length - commonlength);
text2 = text2.substring(0, text2.length - commonlength);
/** @type {?} */
var diffs = this.diff_compute_(text1, text2, checklines, deadline);
// Restore the prefix and suffix.
if (commonprefix) {
diffs.unshift([0 /* Equal */, commonprefix]);
}
if (commonsuffix) {
diffs.push([0 /* Equal */, commonsuffix]);
}
this.diff_cleanupMerge(diffs);
return diffs;
};
/**
* Find the differences between two texts. Assumes that the texts do not
* have any common prefix or suffix.
* @param text1 Old string to be diffed.
* @param text2 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 Array of diff tuples.
*/
/**
* Find the differences between two texts. Assumes that the texts do not
* have any common prefix or suffix.
* @param {?} text1 Old string to be diffed.
* @param {?} text2 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 {?} Array of diff tuples.
*/
DiffMatchPatch.prototype.diff_compute_ = /**
* Find the differences between two texts. Assumes that the texts do not
* have any common prefix or suffix.
* @param {?} text1 Old string to be diffed.
* @param {?} text2 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 {?} Array of diff tuples.
*/
function (text1, text2, checklines, deadline) {
/** @type {?} */
var diffs;
if (!text1) {
// Just add some text (speedup).
return [[1 /* Insert */, text2]];
}
if (!text2) {
// Just delete some text (speedup).
return [[-1 /* Delete */, text1]];
}
/** @type {?} */
var longtext = text1.length > text2.length ? text1 : text2;
/** @type {?} */
var shorttext = text1.length > text2.length ? text2 : text1;
/** @type {?} */
var i = longtext.indexOf(shorttext);
if (i != -1) {
// Shorter text is inside the longer text (speedup).
diffs = [[1 /* Insert */, longtext.substring(0, i)],
[0 /* Equal */, shorttext],
[1 /* Insert */, longtext.substring(i + shorttext.length)]];
// Swap insertions for deletions if diff is reversed.
if (text1.length > text2.length) {
diffs[0][0] = diffs[2][0] = -1 /* Delete */;
}
return diffs;
}
if (shorttext.length == 1) {
// Single character string.
// After the previous speedup, the character can't be an equality.
return [[-1 /* Delete */, text1], [1 /* Insert */, text2]];
}
/** @type {?} */
var hm = this.diff_halfMatch_(text1, text2);
if (hm) {
/** @type {?} */
var text1_a = hm[0];
/** @type {?} */
var text1_b = hm[1];
/** @type {?} */
var text2_a = hm[2];
/** @type {?} */
var text2_b = hm[3];
/** @type {?} */
var mid_common = hm[4];
/** @type {?} */
var diffs_a = this.diff_main(text1_a, text2_a, checklines, deadline);
/** @type {?} */
var diffs_b = this.diff_main(text1_b, text2_b, checklines, deadline);
// Merge the results.
return diffs_a.concat([[0 /* Equal */, mid_common]], diffs_b);
}
if (checklines && text1.length > 100 && text2.length > 100) {
return this.diff_lineMode_(text1, text2, deadline);
}
return this.diff_bisect_(text1, text2, deadline);
};
/**
* 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 Array of diff tuples.
*/
/**
* 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 {?} Array of diff tuples.
*/
DiffMatchPatch.prototype.diff_lineMode_ = /**
* 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 {?} Array of diff tuples.
*/
function (text1, text2, deadline) {
/** @type {?} */
var a = this.diff_linesToChars_(text1, text2);
text1 = a.chars1;
text2 = a.chars2;
/** @type {?} */
var linearray = a.lineArray;
/** @type {?} */
var diffs = this.diff_main(text1, text2, false, deadline);
// Convert the diff back to original text.
this.diff_charsToLines_(diffs, linearray);
// Eliminate freak matches (e.g. blank lines)
this.diff_cleanupSemantic(diffs);
// Rediff any replacement blocks, this time character-by-character.
// Add a dummy entry at the end.
diffs.push([0 /* Equal */, '']);
/** @type {?} */
var pointer = 0;
/** @type {?} */
var count_delete = 0;
/** @type {?} */
var count_insert = 0;
/** @type {?} */
var text_delete = '';
/** @type {?} */
var text_insert = '';
while (pointer < diffs.length) {
switch (diffs[pointer][0]) {
case 1 /* Insert */:
count_insert++;
text_insert += diffs[pointer][1];
break;
case -1 /* Delete */:
count_delete++;
text_delete += diffs[pointer][1];
break;
case 0 /* Equal */:
// Upon reaching an equality, check for prior redundancies.
if (count_delete >= 1 && count_insert >= 1) {
// Delete the offending records and add the merged ones.
diffs.splice(pointer - count_delete - count_insert, count_delete + count_insert);
pointer = pointer - count_delete - count_insert;
/** @type {?} */
var b = this.diff_main(text_delete, text_insert, false, deadline);
for (var j = b.length - 1; j >= 0; j--) {
diffs.splice(pointer, 0, b[j]);
}
pointer = pointer + b.length;
}
count_insert = 0;
count_delete = 0;
text_delete = '';
text_insert = '';
break;
}
pointer++;
}
diffs.pop(); // Remove the dummy entry at the end.
return diffs;
};
/**
* 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 constiations.
* @param text1 Old string to be diffed.
* @param text2 New string to be diffed.
* @param deadline Time at which to bail if not yet complete.
* @return Array of diff tuples.
*/
/**
* 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 constiations.
* @param {?} text1 Old string to be diffed.
* @param {?} text2 New string to be diffed.
* @param {?} deadline Time at which to bail if not yet complete.
* @return {?} Array of diff tuples.
*/
DiffMatchPatch.prototype.diff_bisect_ = /**
* 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 constiations.
* @param {?} text1 Old string to be diffed.
* @param {?} text2 New string to be diffed.
* @param {?} deadline Time at which to bail if not yet complete.
* @return {?} Array of diff tuples.
*/
function (text1, text2, deadline) {
/** @type {?} */
var text1_length = text1.length;
/** @type {?} */
var text2_length = text2.length;
/** @type {?} */
var max_d = Math.ceil((text1_length + text2_length) / 2);
/** @type {?} */
var v_offset = max_d;
/** @type {?} */
var v_length = 2 * max_d;
/** @type {?} */
var v1 = new Array(v_length);
/** @type {?} */
var v2 = new Array(v_length);
// Setting all elements to -1 is faster in Chrome & Firefox than mixing
// integers and undefined.
for (var x = 0; x < v_length; x++) {
v1[x] = -1;
v2[x] = -1;
}
v1[v_offset + 1] = 0;
v2[v_offset + 1] = 0;
/** @type {?} */
var delta = text1_length - text2_length;
/** @type {?} */
var front = (delta % 2 != 0);
/** @type {?} */
var k1start = 0;
/** @type {?} */
var k1end = 0;
/** @type {?} */
var k2start = 0;
/** @type {?} */
var k2end = 0;
for (var d = 0; d < max_d; d++) {
// Bail out if deadline is reached.
if ((new Date()).getTime() > deadline) {
break;
}
// Walk the front path one step.
for (var k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {
/** @type {?} */
var k1_offset = v_offset + k1;
/** @type {?} */
var x1 = void 0;
if (k1 == -d || (k1 != d && v1[k1_offset - 1] < v1[k1_offset + 1])) {
x1 = v1[k1_offset + 1];
}
else {
x1 = v1[k1_offset - 1] + 1;
}
/** @type {?} */
var y1 = x1 - k1;
while (x1 < text1_length && y1 < text2_length &&
text1.charAt(x1) == text2.charAt(y1)) {
x1++;
y1++;
}
v1[k1_offset] = x1;
if (x1 > text1_length) {
// Ran off the right of the graph.
k1end += 2;
}
else if (y1 > text2_length) {
// Ran off the bottom of the graph.
k1start += 2;
}
else if (front) {
/** @type {?} */
var k2_offset = v_offset + delta - k1;
if (k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] != -1) {
/** @type {?} */
var x2 = text1_length - v2[k2_offset];
if (x1 >= x2) {
// Overlap detected.
return this.diff_bisectSplit_(text1, text2, x1, y1, deadline);
}
}
}
}
// Walk the reverse path one step.
for (var k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {
/** @type {?} */
var k2_offset = v_offset + k2;
/** @type {?} */
var x2 = void 0;
if (k2 == -d || (k2 != d && v2[k2_offset - 1] < v2[k2_offset + 1])) {
x2 = v2[k2_offset + 1];
}
else {
x2 = v2[k2_offset - 1] + 1;
}
/** @type {?} */
var y2 = x2 - k2;
while (x2 < text1_length && y2 < text2_length &&
text1.charAt(text1_length - x2 - 1) ==
text2.charAt(text2_length - y2 - 1)) {
x2++;
y2++;
}
v2[k2_offset] = x2;
if (x2 > text1_length) {
// Ran off the left of the graph.
k2end += 2;
}
else if (y2 > text2_length) {
// Ran off the top of the graph.
k2start += 2;
}
else if (!front) {
/** @type {?} */
var k1_offset = v_offset + delta - k2;
if (k1_offset >= 0 && k1_offset < v_length && v1[k1_offset] != -1) {
/** @type {?} */
var x1 = v1[k1_offset];
/** @type {?} */
var y1 = v_offset + x1 - k1_offset;
// Mirror x2 onto top-left coordinate system.
x2 = text1_length - x2;
if (x1 >= x2) {
// Overlap detected.
return this.diff_bisectSplit_(text1, text2, x1, y1, deadline);
}
}
}
}
}
// Diff took too long and hit the deadline or
// number of diffs equals number of characters, no commonality at all.
return [[-1 /* Delete */, text1], [1 /* Insert */, text2]];
};
/**
* 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 Array of diff tuples.
*/
/**
* 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 {?} Array of diff tuples.
*/
DiffMatchPatch.prototype.diff_bisectSplit_ = /**
* 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 {?} Array of diff tuples.
*/
function (text1, text2, x, y, deadline) {
/** @type {?} */
var text1a = text1.substring(0, x);
/** @type {?} */
var text2a = text2.substring(0, y);
/** @type {?} */
var text1b = text1.substring(x);
/** @type {?} */
var text2b = text2.substring(y);
/** @type {?} */
var diffs = this.diff_main(text1a, text2a, false, deadline);
/** @type {?} */
var diffsb = this.diff_main(text1b, text2b, false, deadline);
return diffs.concat(diffsb);
};
/**
* Split two texts into an array 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 }
* An object containing the encoded text1, the encoded text2 and
* the array of unique strings.
* The zeroth element of the array of unique strings is intentionally blank.
*/
/**
* Split two texts into an array 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 {?} }
* An object containing the encoded text1, the encoded text2 and
* the array of unique strings.
* The zeroth element of the array of unique strings is intentionally blank.
*/
DiffMatchPatch.prototype.diff_linesToChars_ = /**
* Split two texts into an array 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 {?} }
* An object containing the encoded text1, the encoded text2 and
* the array of unique strings.
* The zeroth element of the array of unique strings is intentionally blank.
*/
function (text1, text2) {
/** @type {?} */
var lineArray = [];
/** @type {?} */
var lineHash = {}; // e.g. lineHash['Hello\n'] == 4
// '\x00' is a valid character, but constious debuggers don't like it.
// So we'll insert a junk entry to avoid generating a null character.
lineArray[0] = '';
/** @type {?} */
var chars1 = this.diff_linesToCharsMunge_(text1, lineArray, lineHash);
/** @type {?} */
var chars2 = this.diff_linesToCharsMunge_(text2, lineArray, lineHash);
return { chars1: chars1, chars2: chars2, lineArray: lineArray };
};
/**
* Split a text into an array of strings. Reduce the texts to a string of
* hashes where each Unicode character represents one line.
* Modifies linearray and linehash through being a closure.
* @param text String to encode.
* @return Encoded string.
*/
/**
* Split a text into an array of strings. Reduce the texts to a string of
* hashes where each Unicode character represents one line.
* Modifies linearray and linehash through being a closure.
* @param {?} text String to encode.
* @param {?} lineArray
* @param {?} lineHash
* @return {?} Encoded string.
*/
DiffMatchPatch.prototype.diff_linesToCharsMunge_ = /**
* Split a text into an array of strings. Reduce the texts to a string of
* hashes where each Unicode character represents one line.
* Modifies linearray and linehash through being a closure.
* @param {?} text String to encode.
* @param {?} lineArray
* @param {?} lineHash
* @return {?} Encoded string.
*/
function (text, lineArray, lineHash) {
/** @type {?} */
var chars = '';
/** @type {?} */
var lineStart = 0;
/** @type {?} */
var lineEnd = -1;
/** @type {?} */
var lineArrayLength = lineArray.length;
while (lineEnd < text.length - 1) {
lineEnd = text.indexOf('\n', lineStart);
if (lineEnd == -1) {
lineEnd = text.length - 1;
}
/** @type {?} */
var line = text.substring(lineStart, lineEnd + 1);
lineStart = lineEnd + 1;
if (lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) :
(lineHash[line] !== undefined)) {
chars += String.fromCharCode(lineHash[line]);
}
else {
chars += String.fromCharCode(lineArrayLength);
lineHash[line] = lineArrayLength;
lineArray[lineArrayLength++] = line;
}
}
return chars;
};
/**
* Rehydrate the text in a diff from a string of line hashes to real lines of
* text.
* @param diffs Array of diff tuples.
* @param lineArray Array of unique strings.
*/
/**
* Rehydrate the text in a diff from a string of line hashes to real lines of
* text.
* @param {?} diffs Array of diff tuples.
* @param {?} lineArray Array of unique strings.
* @return {?}
*/
DiffMatchPatch.prototype.diff_charsToLines_ = /**
* Rehydrate the text in a diff from a string of line hashes to real lines of
* text.
* @param {?} diffs Array of diff tuples.
* @param {?} lineArray Array of unique strings.
* @return {?}
*/
function (diffs, lineArray) {
for (var x = 0; x < diffs.length; x++) {
/** @type {?} */
var chars = diffs[x][1];
/** @type {?} */
var text = [];
for (var y = 0; y < chars.length; y++) {
text[y] = lineArray[chars.charCodeAt(y)];
}
diffs[x][1] = text.join('');
}
};
/**
* Determine the common prefix of two strings.
* @param text1 First string.
* @param text2 Second string.
* @return The number of characters common to the start of each
* string.
*/
/**
* Determine the common prefix of two strings.
* @param {?} text1 First string.
* @param {?} text2 Second string.
* @return {?} The number of characters common to the start of each
* string.
*/
DiffMatchPatch.prototype.diff_commonPrefix = /**
* Determine the common prefix of two strings.
* @param {?} text1 First string.
* @param {?} text2 Second string.
* @return {?} The number of characters common to the start of each
* string.
*/
function (text1, text2) {
// Quick check for common null cases.
if (!text1 || !text2 || text1.charAt(0) != text2.charAt(0)) {
return 0;
}
/** @type {?} */
var pointermin = 0;
/** @type {?} */
var pointermax = Math.min(text1.length, text2.length);
/** @type {?} */
var pointermid = pointermax;
/** @type {?} */
var pointerstart = 0;
while (pointermin < pointermid) {
if (text1.substring(pointerstart, pointermid) ==
text2.substring(pointerstart, pointermid)) {
pointermin = pointermid;
pointerstart = pointermin;
}
else {
pointermax = pointermid;
}
pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
}
return pointermid;
};
/**
* Determine the common suffix of two strings.
* @param text1 First string.
* @param text2 Second string.
* @return The number of characters common to the end of each string.
*/
/**
* Determine the common suffix of two strings.
* @param {?} text1 First string.
* @param {?} text2 Second string.
* @return {?} The number of characters common to the end of each string.
*/
DiffMatchPatch.prototype.diff_commonSuffix = /**
* Determine the common suffix of two strings.
* @param {?} text1 First string.
* @param {?} text2 Second string.
* @return {?} The number of characters common to the end of each string.
*/
function (text1, text2) {
// Quick check for common null cases.
if (!text1 || !text2 ||
text1.charAt(text1.length - 1) != text2.charAt(text2.length - 1)) {
return 0;
}
/** @type {?} */
var pointermin = 0;
/** @type {?} */
var pointermax = Math.min(text1.length, text2.length);
/** @type {?} */
var pointermid = pointermax;
/** @type {?} */
var pointerend = 0;
while (pointermin < pointermid) {
if (text1.substring(text1.length - pointermid, text1.length - pointerend) ==
text2.substring(text2.length - pointermid, text2.length - pointerend)) {
pointermin = pointermid;
pointerend = pointermin;
}
else {
pointermax = pointermid;
}
pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
}
return pointermid;
};
/**
* 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.
*/
DiffMatchPatch.prototype.diff_commonOverlap_ = /**
* 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.
*/
function (text1, text2) {
/** @type {?} */
var text1_length = text1.length;
/** @type {?} */
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);
}
/** @type {?} */
var text_length = Math.min(text1_length, text2_length);
// Quick check for the worst case.
if (text1 == text2) {
return text_length;
}
/** @type {?} */
var best = 0;
/** @type {?} */
var length = 1;
while (true) {
/** @type {?} */
var pattern = text1.substring(text_length - length);
/** @type {?} */
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++;
}
}
};
/**
* 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 text1 First string.
* @param text2 Second string.
* @return Five element 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.
*/
/**
* 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 {?} text1 First string.
* @param {?} text2 Second string.
* @return {?} Five element 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.
*/
DiffMatchPatch.prototype.diff_halfMatch_ = /**
* 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 {?} text1 First string.
* @param {?} text2 Second string.
* @return {?} Five element 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.
*/
function (text1, text2) {
if (this.Diff_Timeout <= 0) {
// Don't risk returning a non-optimal diff if we have unlimited time.
return null;
}
/** @type {?} */
var longtext = text1.length > text2.length ? text1 : text2;
/** @type {?} */
var shorttext = text1.length > text2.length ? text2 : text1;
if (longtext.length < 4 || shorttext.length * 2 < longtext.length) {
return null; // Pointless.
}
/** @type {?} */
var dmp = this;
/** @type {?} */
var hm1 = this.diff_halfMatchI_(longtext, shorttext, Math.ceil(longtext.length / 4), dmp);
/** @type {?} */
var hm2 = this.diff_halfMatchI_(longtext, shorttext, Math.ceil(longtext.length / 2), dmp);
/** @type {?} */
var hm;
if (!hm1 && !hm2) {
return null;
}
else if (!hm2) {
hm = hm1;
}
else if (!hm1) {
hm = hm2;
}
else {
// Both matched. Select the longest.
hm = hm1[4].length > hm2[4].length ? hm1 : hm2;
}
/** @type {?} */
var text1_a;
/** @type {?} */
var text1_b;
/** @type {?} */
var text2_a;
/** @type {?} */
var text2_b;
if (text1.length > text2.length) {
text1_a = hm[0];
text1_b = hm[1];
text2_a = hm[2];
text2_b = hm[3];
}
else {
text2_a = hm[0];
text2_b = hm[1];
text1_a = hm[2];
text1_b = hm[3];
}
/** @type {?} */
var mid_common = hm[4];
return [text1_a, text1_b, text2_a, text2_b, mid_common];
};
/**
* Does a substring of shorttext exist within longtext such that the substring
* is at least half the length of longtext?
* Closure, but does not reference any external constiables.
* @param longtext Longer string.
* @param shorttext Shorter string.
* @param i Start index of quarter length substring within longtext.
* @return Five element 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.
*/
/**
* Does a substring of shorttext exist within longtext such that the substring
* is at least half the length of longtext?
* Closure, but does not reference any external constiables.
* @param {?} longtext Longer string.
* @param {?} shorttext Shorter string.
* @param {?} i Start index of quarter length substring within longtext.
* @param {?} dmp
* @return {?} Five element 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.
*/
DiffMatchPatch.prototype.diff_halfMatchI_ = /**
* Does a substring of shorttext exist within longtext such that the substring
* is at least half the length of longtext?
* Closure, but does not reference any external constiables.
* @param {?} longtext Longer string.
* @param {?} shorttext Shorter string.
* @param {?} i Start index of quarter length substring within longtext.
* @param {?} dmp
* @return {?} Five element 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.
*/
function (longtext, shorttext, i, dmp) {
/** @type {?} */
var seed = longtext.substring(i, i + Math.floor(longtext.length / 4));
/** @type {?} */
var j = -1;
/** @type {?} */
var best_common = '';
/** @type {?} */
var best_longtext_a;
/** @type {?} */
var best_longtext_b;
/** @type {?} */
var best_shorttext_a;
/** @type {?} */
var best_shorttext_b;
while ((j = shorttext.indexOf(seed, j + 1)) != -1) {
/** @type {?} */
var prefixLength = dmp.diff_commonPrefix(longtext.substring(i), shorttext.substring(j));
/** @type {?} */
var suffixLength = dmp.diff_commonSuffix(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_longtext_a = longtext.substring(0, i - suffixLength);
best_longtext_b = longtext.substring(i + prefixLength);
best_shorttext_a = shorttext.substring(0, j - suffixLength);
best_shorttext_b = shorttext.substring(j + prefixLength);
}
}
if (best_common.length * 2 >= longtext.length) {
return [best_longtext_a, best_longtext_b,
best_shorttext_a, best_shorttext_b, best_common];
}
else {
return null;
}
};
/**
* Reduce the number of edits by eliminating semantically trivial equalities.
* @param diffs Array of diff tuples.
*/
/**
* Reduce the number of edits by eliminating semantically trivial equalities.
* @param {?} diffs Array of diff tuples.
* @return {?}
*/
DiffMatchPatch.prototype.diff_cleanupSemantic = /**
* Reduce the number of edits by eliminating semantically trivial equalities.
* @param {?} diffs Array of diff tuples.
* @return {?}
*/
function (diffs) {
/** @type {?} */
var changes = false;
/** @type {?} */
var equalities = [];
/** @type {?} */
var equalitiesLength = 0;
/** @type {?} */
var lastequality = null;
/** @type {?} */
var pointer = 0;
/** @type {?} */
var length_insertions1 = 0;
/** @type {?} */
var length_deletions1 = 0;
/** @type {?} */
var length_insertions2 = 0;
/** @type {?} */
var length_deletions2 = 0;
while (pointer < diffs.length) {
if (diffs[pointer][0] == 0 /* 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] == 1 /* 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, [-1 /* Delete */, lastequality]);
// Change second copy to insert.
diffs[equalities[equalitiesLength - 1] + 1][0] = 1 /* 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) {
this.diff_cleanupMerge(diffs);
}
this.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]