diff --git a/diff.js b/diff.js index 00fde392..ec48c8bc 100644 --- a/diff.js +++ b/diff.js @@ -17,7 +17,7 @@ */ /** - * Modified by Piotr Kaminski. + * Modified by Piotr Kaminski and Sergei Vorobev. */ /** @@ -131,13 +131,21 @@ diff.prototype.diff_main = function(text1, text2, opt_checklines, var checklines = opt_checklines; // Trim off common prefix (speedup). - var commonlength = this.diff_commonPrefix(text1, text2); + if (checklines) { + var commonlength = this.diff_commonPrefix_newLine(text1, text2); + } else { + var commonlength = this.diff_commonPrefix(text1, text2); + } 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); + if (checklines) { + commonlength = this.diff_commonSuffix_newLine(text1, text2); + } else { + commonlength = this.diff_commonSuffix(text1, text2); + } var commonsuffix = text1.substring(text1.length - commonlength); text1 = text1.substring(0, text1.length - commonlength); text2 = text2.substring(0, text2.length - commonlength); @@ -570,6 +578,32 @@ diff.prototype.diff_commonPrefix = function(text1, text2) { return pointermid; }; +/** + * Determine the common prefix of two strings making delimitors only at new lines. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the start of each + * string. + */ + diff.prototype.diff_commonPrefix_newLine = function(text1, text2) { + var idx = this.diff_commonPrefix(text1, text2); + // special handling for the case when the whole string is matching + if (idx == text1.length && idx == text2.length) { + return idx; + } + if (idx == text1.length && text2[idx] == "\n") { + return idx + } + if (idx == text2.length && text1[idx] == "\n") { + return idx + } + var offset = text1.substring(0, idx).lastIndexOf("\n"); + if (offset == -1) { + return 0; + } + return offset + 1; +}; + /** * Determine the common suffix of two strings. @@ -602,6 +636,31 @@ diff.prototype.diff_commonSuffix = function(text1, text2) { return pointermid; }; +/** + * Determine the common suffix of two strings making delimitors only at new lines. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the end of each string. + */ + diff.prototype.diff_commonSuffix_newLine = function(text1, text2) { + var idx = this.diff_commonSuffix(text1, text2); + // special handling for the case when the whole string is matching + if (idx == text1.length && idx == text2.length) { + return idx; + } + if (idx == text1.length && text2[text2.length - idx - 1] == "\n") { + return idx + } + if (idx == text2.length && text1[text1.length - idx - 1] == "\n") { + return idx + } + var offset = text1.substring(text1.length - idx).indexOf("\n"); + if (offset == -1) { + return 0; + } + return idx - offset; +}; + /** * Determine if the suffix of one string is the prefix of another. diff --git a/tests/diff_test.html b/tests/diff_test.html index a6af22cf..56919649 100644 --- a/tests/diff_test.html +++ b/tests/diff_test.html @@ -86,7 +86,9 @@ var tests = [ 'testDiffCommonPrefix', + 'testDiffCommonPrefixNewLine', 'testDiffCommonSuffix', + 'testDiffCommonSuffixNewLine', 'testDiffCommonOverlap', 'testDiffHalfMatch', 'testDiffLinesToChars', diff --git a/tests/diff_test.js b/tests/diff_test.js index c95c6366..966cbb94 100644 --- a/tests/diff_test.js +++ b/tests/diff_test.js @@ -91,6 +91,22 @@ function testDiffCommonPrefix() { assertEquals(4, dmp.diff_commonPrefix('1234', '1234xyz')); } +function testDiffCommonPrefixNewLine() { + // Detect any common prefix in repsect to new lines. + // Null case. + assertEquals(0, dmp.diff_commonPrefix_newLine('abc', 'abx')); + + // Non-null case. + assertEquals(3, dmp.diff_commonPrefix_newLine('12\n34abcdef', '12\n34xyz')); + + // Multiply \n case. + assertEquals(6, dmp.diff_commonPrefix_newLine('12\n34\n1abcdef', '12\n34\n1xyz')); + + // Whole case. + assertEquals(4, dmp.diff_commonPrefix_newLine('1234', '1234\nxyz')); + assertEquals(0, dmp.diff_commonPrefix_newLine('1234', '12345\nxyz')); +} + function testDiffCommonSuffix() { // Detect any common suffix. // Null case. @@ -103,6 +119,22 @@ function testDiffCommonSuffix() { assertEquals(4, dmp.diff_commonSuffix('1234', 'xyz1234')); } +function testDiffCommonSuffixNewLine() { + // Detect any common suffix in respect to new lines. + // Null case. + assertEquals(0, dmp.diff_commonSuffix_newLine('abc', 'xbc')); + + // Non-null case. + assertEquals(3, dmp.diff_commonSuffix_newLine('abcdef12\n34', 'xyz12\n34')); + + // Multiply \n case. + assertEquals(6, dmp.diff_commonSuffix_newLine('abcdef1\n12\n34', 'xyz1\n12\n34')); + + // Whole case. + assertEquals(4, dmp.diff_commonSuffix_newLine('1234', 'xyz\n1234')); + assertEquals(0, dmp.diff_commonSuffix_newLine('1234', 'xyz\n01234')); +} + function testDiffCommonOverlap() { // Detect any suffix/prefix overlap. // Null case. @@ -587,6 +619,11 @@ function testDiffMain() { // Large equality. assertEquivalent([[DIFF_INSERT, ' '], [DIFF_EQUAL, 'a'], [DIFF_INSERT, 'nd'], [DIFF_EQUAL, ' [[Pennsylvania]]'], [DIFF_DELETE, ' and [[New']], dmp.diff_main('a [[Pennsylvania]] and [[New', ' and [[Pennsylvania]]', false)); + // Codifying a suboptimal but a technically correct diff + assertEquivalent([[DIFF_EQUAL, 'def x():\n pass\n\ndef '], [DIFF_DELETE, 'y():\n pass\n\ndef '], [DIFF_EQUAL, 'z():\n pass\n']], dmp.diff_main('def x():\n pass\n\ndef y():\n pass\n\ndef z():\n pass\n', 'def x():\n pass\n\ndef z():\n pass\n', false)); + // Better diff when multiline heuristic is used + assertEquivalent([[DIFF_EQUAL, 'def x():\n pass\n\n'], [DIFF_DELETE, 'def y():\n pass\n\n'], [DIFF_EQUAL, 'def z():\n pass\n']], dmp.diff_main('def x():\n pass\n\ndef y():\n pass\n\ndef z():\n pass\n', 'def x():\n pass\n\ndef z():\n pass\n', true)); + // Timeout. dmp.Diff_Timeout = 0.1; // 100ms var a = '`Twas brillig, and the slithy toves\nDid gyre and gimble in the wabe:\nAll mimsy were the borogoves,\nAnd the mome raths outgrabe.\n'; diff --git a/tests/speedtest.html b/tests/speedtest.html index 77b79bc3..db861817 100644 --- a/tests/speedtest.html +++ b/tests/speedtest.html @@ -20,7 +20,7 @@

Diff Speed Test

// No warmup loop since it risks triggering an 'unresponsive script' dialog // in client-side JavaScript var ms_start = (new Date()).getTime(); - var d = dmp.diff_main(text1, text2, false); + var d = dmp.diff_main(text1, text2, true); var ms_end = (new Date()).getTime(); var ds = dmp.diff_prettyHtml(d);