diff --git a/src/diff/line.js b/src/diff/line.js index f9b4f499..5852f75e 100644 --- a/src/diff/line.js +++ b/src/diff/line.js @@ -12,22 +12,60 @@ lineDiff.tokenize = function(value) { } // Merge the content and line separators into single tokens - for (let i = 0; i < linesAndNewlines.length; i++) { - let line = linesAndNewlines[i]; - - if (i % 2 && !this.options.newlineIsToken) { - retLines[retLines.length - 1] += line; - } else { - if (this.options.ignoreWhitespace) { - line = line.trim(); + if (this.options.ignoreWhitespace) { + for (let i = 0; i < linesAndNewlines.length; i++) { + let line = linesAndNewlines[i]; + + if (i % 2 && !this.options.newlineIsToken) { + let last = retLines[retLines.length - 1]; + last.key += line; + last.payload += line; + } else { + retLines.push({ key: line.trim(), payload: line }); + } + } + } else { + for (let i = 0; i < linesAndNewlines.length; i++) { + let line = linesAndNewlines[i]; + + if (i % 2 && !this.options.newlineIsToken) { + retLines[retLines.length - 1] += line; + } else { + retLines.push(line); } - retLines.push(line); } } return retLines; }; +lineDiff.removeEmpty = function(array) { + if (this.options.ignoreWhitespace) { + return array.filter(v => v.key); + } + return array.filter(v => v); +}; + +lineDiff.equals = function(left, right) { + if (this.options.ignoreWhitespace) { + // Special case handle for when one terminal is ignored (i.e. whitespace). + // For this case we merge the terminal into the prior string and drop the change. + // This is only available for string mode. + if (left === '') { + return Diff.prototype.equals.call(this, left, right.trim()); + } + return Diff.prototype.equals.call(this, left.key, right.key); + } + return Diff.prototype.equals.call(this, left, right); +}; + +lineDiff.join = function(result) { + if (this.options.ignoreWhitespace) { + return result.map(v => v.payload).join(''); + } + return result.join(''); +}; + export function diffLines(oldStr, newStr, callback) { return lineDiff.diff(oldStr, newStr, callback); } export function diffTrimmedLines(oldStr, newStr, callback) { let options = generateOptions(callback, {ignoreWhitespace: true}); diff --git a/test/diff/line.js b/test/diff/line.js index 5c2c7657..48d96f15 100644 --- a/test/diff/line.js +++ b/test/diff/line.js @@ -64,17 +64,52 @@ describe('diff/line', function() { }); it('should ignore leading and trailing whitespace', function() { - const diffResult = diffTrimmedLines( + const diffResult1 = diffTrimmedLines( 'line\nvalue \nline', 'line\nvalue\nline'); - expect(convertChangesToXML(diffResult)).to.equal('line\nvalue\nline'); + expect(convertChangesToXML(diffResult1)).to.equal('line\nvalue\nline'); + + const diffResult2 = diffTrimmedLines( + 'line\nvalue\nline', + 'line\nvalue \nline'); + expect(convertChangesToXML(diffResult2)).to.equal('line\nvalue \nline'); + + const diffResult3 = diffTrimmedLines( + 'line\n value\nline', + 'line\nvalue\nline'); + expect(convertChangesToXML(diffResult3)).to.equal('line\nvalue\nline'); + + const diffResult4 = diffTrimmedLines( + 'line\nvalue\nline', + 'line\n value\nline'); + expect(convertChangesToXML(diffResult4)).to.equal('line\n value\nline'); + }); + + it('should keep leading and trailing whitespace in the output', function() { + function stringify(value) { + return JSON.stringify(value, null, 2); + } + const diffResult = diffTrimmedLines( + stringify([10, 20, 30]), + stringify({ data: [10, 42, 30] })); + expect(convertChangesToXML(diffResult)).to.equal([ + '[\n', + '{\n', + ' "data": [\n', + ' 10,\n', + ' 20,\n', + ' 42,\n', + ' 30\n', + '] ]\n', + '}' + ].join('').replace(/"/g, '"')); }); it('should handle windows line endings', function() { const diffResult = diffTrimmedLines( 'line\r\nold value \r\nline', 'line\r\nnew value\r\nline'); - expect(convertChangesToXML(diffResult)).to.equal('line\r\nold value\r\nnew value\r\nline'); + expect(convertChangesToXML(diffResult)).to.equal('line\r\nold value \r\nnew value\r\nline'); }); });