Skip to content

Allow access to structured object representation of patch data #62

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Aug 4, 2015
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 62 additions & 39 deletions diff.js
Original file line number Diff line number Diff line change
Expand Up @@ -398,39 +398,22 @@
callback
);
},

createTwoFilesPatch: function(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader) {
var ret = [];

if (oldFileName == newFileName) {
ret.push('Index: ' + oldFileName);

structuredPatch: function(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
if(!options) {
options = { context: 4 };
}
ret.push('===================================================================');
ret.push('--- ' + oldFileName + (typeof oldHeader === 'undefined' ? '' : '\t' + oldHeader));
ret.push('+++ ' + newFileName + (typeof newHeader === 'undefined' ? '' : '\t' + newHeader));


var diff = PatchDiff.diff(oldStr, newStr);
diff.push({value: '', lines: []}); // Append an empty value to make cleanup easier

// Formats a given set of lines for printing as context lines in a patch

function contextLines(lines) {
return map(lines, function(entry) { return ' ' + entry; });
}

// Outputs the no newline at end of file warning if needed
function eofNL(curRange, i, current) {
var last = diff[diff.length - 2],
isLast = i === diff.length - 2,
isLastOfType = i === diff.length - 3 && current.added !== last.added;

// Figure out if this is the last line for the given file and missing NL
if (!(/\n$/.test(current.value)) && (isLast || isLastOfType)) {
curRange.push('\\ No newline at end of file');
}
}

var hunks = [];
var oldRangeStart = 0, newRangeStart = 0, curRange = [],
oldLine = 1, newLine = 1;
oldLine = 1, newLine = 1;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This formatting was intentional. Style is to chain vars with , and to double indent when newlines are used to line up the var names.

for (var i = 0; i < diff.length; i++) {
var current = diff[i],
lines = current.lines || current.value.replace(/\n$/, '').split('\n');
Expand All @@ -444,7 +427,7 @@
newRangeStart = newLine;

if (prev) {
curRange = contextLines(prev.lines.slice(-4));
curRange = options.context > 0 ? contextLines(prev.lines.slice(-options.context)) : [];
oldRangeStart -= curRange.length;
newRangeStart -= curRange.length;
}
Expand All @@ -454,7 +437,6 @@
curRange.push.apply(curRange, map(lines, function(entry) {
return (current.added ? '+' : '-') + entry;
}));
eofNL(curRange, i, current);

// Track the updated file position
if (current.added) {
Expand All @@ -466,21 +448,34 @@
// Identical context lines. Track line changes
if (oldRangeStart) {
// Close out any changes that have been output (or join overlapping)
if (lines.length <= 8 && i < diff.length - 2) {
if (lines.length <= options.context * 2 && i < diff.length - 2) {
// Overlapping
curRange.push.apply(curRange, contextLines(lines));
} else {
// end the range and output
var contextSize = Math.min(lines.length, 4);
ret.push(
'@@ -' + oldRangeStart + ',' + (oldLine - oldRangeStart + contextSize)
+ ' +' + newRangeStart + ',' + (newLine - newRangeStart + contextSize)
+ ' @@');
ret.push.apply(ret, curRange);
ret.push.apply(ret, contextLines(lines.slice(0, contextSize)));
if (lines.length <= 4) {
eofNL(ret, i, current);
var contextSize = Math.min(lines.length, options.context);
var hunklines = [];
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had trouble adapting its logic to the hunk'd format. Since trying to add no-nl each time we have an add/delete entry seemed inefficient, I opted for a rewrite.

hunklines.push.apply(hunklines, curRange);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just do a curRange.slice() or even utilize curRange if not used elsewhere here.

hunklines.push.apply(hunklines, contextLines(lines.slice(0, contextSize)));

var hunk = {
oldStart: oldRangeStart,
oldLines: (oldLine - oldRangeStart + contextSize),
newStart: newRangeStart,
newLines: (newLine - newRangeStart + contextSize),
lines: hunklines
}
if(i >= diff.length - 2 && lines.length <= options.context) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style is to have spaces after if, before (.

// EOF is inside this hunk
var oldEOFNewline = /\n$/.test(oldStr);
var newEOFNewline = /\n$/.test(newStr);
if(lines.length == 0 && !oldEOFNewline) {
hunklines.splice(hunk.oldLines, 0, '\\ No newline at end of file')
} else if (!oldEOFNewline || !newEOFNewline) {
hunklines.push('\\ No newline at end of file')
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the difference between these two cases here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lines.length == 0 && !oldEOFNewline is the special case where no-nl ends up in the middle of hunk. Will add comment to this effect.

}
}
hunks.push(hunk);

oldRangeStart = 0;
newRangeStart = 0;
Expand All @@ -491,12 +486,40 @@
newLine += lines.length;
}
}

return {
oldFileName: oldFileName, newFileName: newFileName,
oldHeader: oldHeader, newHeader: newHeader,
hunks: hunks
};
},

createTwoFilesPatch: function(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
var diff = JsDiff.structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options);

var ret = [];
if (oldFileName == newFileName) {
ret.push('Index: ' + oldFileName);
}
ret.push('===================================================================');
ret.push('--- ' + diff.oldFileName + (typeof diff.oldHeader === 'undefined' ? '' : '\t' + diff.oldHeader));
ret.push('+++ ' + diff.newFileName + (typeof diff.newHeader === 'undefined' ? '' : '\t' + diff.newHeader));

for(var i = 0; i < diff.hunks.length; i++) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Space after for

var hunk = diff.hunks[i];
ret.push(
'@@ -' + hunk.oldStart + ',' + hunk.oldLines
+ ' +' + hunk.newStart + ',' + hunk.newLines
+ ' @@'
);
ret.push.apply(ret, hunk.lines);
}

return ret.join('\n') + '\n';
},

createPatch: function(fileName, oldStr, newStr, oldHeader, newHeader) {
return JsDiff.createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader);
createPatch: function(fileName, oldStr, newStr, oldHeader, newHeader, options) {
return JsDiff.createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader, options);
},

applyPatch: function(oldStr, uniDiff) {
Expand Down
76 changes: 74 additions & 2 deletions test/createPatch.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ describe('#createPatch', function() {
+ '+line5\n');
});

it('should output no newline at end of file message', function() {
it('should output "no newline" at end of file message on new missing nl', function() {
diff.createPatch('test', 'line1\nline2\nline3\nline4\n', 'line1\nline2\nline3\nline4', 'header1', 'header2').should.equal(
'Index: test\n'
+ '===================================================================\n'
Expand All @@ -69,7 +69,9 @@ describe('#createPatch', function() {
+ '-line4\n'
+ '+line4\n'
+ '\\ No newline at end of file\n');
});

it('should output "no newline" at end of file message on old missing nl', function() {
diff.createPatch('test', 'line1\nline2\nline3\nline4', 'line1\nline2\nline3\nline4\n', 'header1', 'header2').should.equal(
'Index: test\n'
+ '===================================================================\n'
Expand All @@ -82,7 +84,9 @@ describe('#createPatch', function() {
+ '-line4\n'
+ '\\ No newline at end of file\n'
+ '+line4\n');
});

it('should output "no newline" at end of file message on context missing nl', function() {
diff.createPatch('test', 'line11\nline2\nline3\nline4', 'line1\nline2\nline3\nline4', 'header1', 'header2').should.equal(
'Index: test\n'
+ '===================================================================\n'
Expand All @@ -95,7 +99,9 @@ describe('#createPatch', function() {
+ ' line3\n'
+ ' line4\n'
+ '\\ No newline at end of file\n');
});

it('should not output no newline at end of file message when eof outside hunk', function() {
diff.createPatch('test', 'line11\nline2\nline3\nline4\nline4\nline4\nline4', 'line1\nline2\nline3\nline4\nline4\nline4\nline4', 'header1', 'header2').should.equal(
'Index: test\n'
+ '===================================================================\n'
Expand Down Expand Up @@ -422,7 +428,7 @@ describe('#createPatch', function() {
+ 'context\n'
+ 'context';

it('should generate a patch', function() {
it('should generate a patch with default context size', function() {
var expectedResult =
'Index: testFileName\n'
+ '===================================================================\n'
Expand Down Expand Up @@ -475,6 +481,72 @@ describe('#createPatch', function() {
var diffResult = diff.createPatch('testFileName', oldFile, newFile, 'Old Header', 'New Header');
diffResult.should.equal(expectedResult);
});

it('should generatea a patch with context size 0', function() {
var expectedResult =
'Index: testFileName\n'
+ '===================================================================\n'
+ '--- testFileName\tOld Header\n'
+ '+++ testFileName\tNew Header\n'
+ '@@ -1,1 +1,2 @@\n'
+ '-value\n'
+ '+new value\n'
+ '+new value 2\n'
+ '@@ -11,1 +12,0 @@\n'
+ '-remove value\n'
+ '@@ -21,1 +21,0 @@\n'
+ '-remove value\n'
+ '@@ -30,0 +29,1 @@\n'
+ '+add value\n'
+ '@@ -34,1 +34,2 @@\n'
+ '-value\n'
+ '+new value\n'
+ '+new value 2\n';
var diffResult = diff.createPatch('testFileName', oldFile, newFile, 'Old Header', 'New Header', { context: 0 });
diffResult.should.equal(expectedResult);
});

it('should generate a patch with context size 2', function() {
var expectedResult =
'Index: testFileName\n'
+ '===================================================================\n'
+ '--- testFileName\tOld Header\n'
+ '+++ testFileName\tNew Header\n'
+ '@@ -1,3 +1,4 @@\n'
+ '-value\n'
+ '+new value\n'
+ '+new value 2\n'
+ ' context\n'
+ ' context\n'
+ '@@ -9,5 +10,4 @@\n'
+ ' context\n'
+ ' context\n'
+ '-remove value\n'
+ ' context\n'
+ ' context\n'
+ '@@ -19,5 +19,4 @@\n'
+ ' context\n'
+ ' context\n'
+ '-remove value\n'
+ ' context\n'
+ ' context\n'
+ '@@ -28,9 +27,11 @@\n'
+ ' context\n'
+ ' context\n'
+ '+add value\n'
+ ' context\n'
+ ' context\n'
+ ' context\n'
+ ' context\n'
+ '-value\n'
+ '+new value\n'
+ '+new value 2\n'
+ ' context\n'
+ ' context\n'
+ '\\ No newline at end of file\n';
var diffResult = diff.createPatch('testFileName', oldFile, newFile, 'Old Header', 'New Header', { context: 2 });
diffResult.should.equal(expectedResult);
});

it('should output headers only for identical files', function() {
var expectedResult =
Expand Down
25 changes: 25 additions & 0 deletions test/structuredPatch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const VERBOSE = false;

var diff = require('../diff');

function log() {
VERBOSE && console.log.apply(console, arguments);
}

describe('#structuredPatch', function() {
it('should handle files with the last line changed', function() {
var res = diff.structuredPatch(
'oldfile', 'newfile',
'line2\nline3\nline4\n', 'line2\nline3\nline5',
'header1', 'header2'
);
res.should.eql({
oldFileName: 'oldfile', newFileName: 'newfile',
oldHeader: 'header1', newHeader: 'header2',
hunks: [{
oldStart: 1, oldLines: 3, newStart: 1, newLines: 3,
lines: [' line2', ' line3', '-line4', '+line5', '\\ No newline at end of file'],
}]
});
});
});