-
Notifications
You must be signed in to change notification settings - Fork 510
Allow patchs in diff format #83
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
Changes from 9 commits
0c9dd6d
d501881
c238f85
54edda7
836a2f8
720f5ae
0c2af7b
d723875
c6b6a02
3908658
09b7efe
f91668b
3958558
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,19 +20,21 @@ export function applyPatch(source, uniDiff, options = {}) { | |
compareLine = options.compareLine || ((lineNumber, line, operation, patchContent) => line === patchContent), | ||
errorCount = 0, | ||
fuzzFactor = options.fuzzFactor || 0, | ||
minLine = 0, | ||
offset = 0, | ||
|
||
removeEOFNL, | ||
addEOFNL; | ||
|
||
for (let i = 0; i < hunks.length; i++) { | ||
let hunk = hunks[i], | ||
toPos = hunk.newStart - 1; | ||
|
||
// Sanity check the input string. Bail if we don't match. | ||
/** | ||
* Checks if the hunk exactly fits on the provided location | ||
*/ | ||
function hunkFits(hunk, toPos) { | ||
for (let j = 0; j < hunk.lines.length; j++) { | ||
let line = hunk.lines[j], | ||
operation = line[0], | ||
content = line.substr(1); | ||
|
||
if (operation === ' ' || operation === '-') { | ||
// Context sanity check | ||
if (!compareLine(toPos + 1, lines[toPos], operation, content)) { | ||
|
@@ -42,8 +44,66 @@ export function applyPatch(source, uniDiff, options = {}) { | |
return false; | ||
} | ||
} | ||
toPos++; | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
|
||
// Search best fit offsets for each hunk based on the previous ones | ||
for (let i = 0; i < hunks.length; i++) { | ||
let hunk = hunks[i], | ||
outOfLimits = 0, | ||
localOffset = 0, | ||
toPos = offset + hunk.oldStart - 1; | ||
|
||
for (;;) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After coming back to this a few days later, I'm still worried that I'm not going to be able to maintain this. What if instead we extract out the iteration logic into an iterator? Something like:
Which can live in it's own file and we can unit test in isolation. This loop here then becomes something like:
(I have not actually tested or even run this code, so it might need some work to get it running) I think separating general flip flopping iteration and checking the hunk itself into separate code will help with clarity as well as give us a nice reusable component for this iteration should we need it in other locations. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I find this code a little bit more complicated and less eficient, but I like the idea of separate the flip-flop logic from the main loop. I'll fetch your code and try to check it out :-) |
||
// Check if trying to fit beyond text length, and if not, check it fits | ||
// after offset location (or desired location on first iteration) | ||
if (lines.length < toPos + localOffset + hunk.oldLines) { | ||
outOfLimits++; | ||
} else if (hunkFits(hunk, toPos + localOffset)) { | ||
hunk.offset = offset += localOffset; | ||
break; | ||
} | ||
|
||
// If we tried to fit hunk before text beginning and beyond text lenght, | ||
// then hunk can't be fit on the text so we raise an error | ||
if (outOfLimits === 2) { | ||
return false; | ||
} | ||
|
||
// Reset checks of trying to fit outside text limits and increase offset | ||
// of the current hunk relative to its desired location | ||
outOfLimits = 0; | ||
localOffset++; | ||
|
||
// Check if trying to fit before text beginning, and if not, check it fits | ||
// before offset location | ||
if (toPos - localOffset < minLine) { | ||
outOfLimits++; | ||
} else if (hunkFits(hunk, toPos - localOffset)) { | ||
hunk.offset = offset -= localOffset; | ||
break; | ||
} | ||
} | ||
|
||
// Set lower text limit to end of the current hunk, so next ones don't try | ||
// to fit over already patched text | ||
minLine = hunk.offset + hunk.oldStart + hunk.oldLines; | ||
} | ||
|
||
// Apply patch hunks | ||
for (let i = 0; i < hunks.length; i++) { | ||
let hunk = hunks[i], | ||
toPos = hunk.offset + hunk.newStart - 1; | ||
|
||
for (let j = 0; j < hunk.lines.length; j++) { | ||
let line = hunk.lines[j], | ||
operation = line[0], | ||
content = line.substr(1); | ||
|
||
if (operation === ' ') { | ||
toPos++; | ||
} else if (operation === '-') { | ||
|
@@ -84,7 +144,7 @@ export function applyPatches(uniDiff, options) { | |
function processIndex() { | ||
let index = uniDiff[currentIndex++]; | ||
if (!index) { | ||
options.complete(); | ||
return options.complete(); | ||
} | ||
|
||
options.loadFile(index, function(err, data) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic here is really hard to follow and I worry that it's going to lead to confusion down the road. Rather than having side effects that terminate the loop in the middle of the block and the loop written as an infinite loop, I think it would be clearer to have the termination case expressed in the
for
/while
statement.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's an optimization, the alternative is to have the old code and use a conditional to check it, or do the check twice on the most common case (the hunk fits at the first try, when
localOffset
is zero). This has only moved the block to check the current or next line before the loop, so it's checked always in a loop rollback fashion. The same would be achieved by doing the check previously to enter the loop, but code is duplicated. I can add more comments if you don't see it clear.