forked from kpdecker/jsdiff
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcreate.js
128 lines (112 loc) · 4.38 KB
/
create.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import {diffLines} from '../diff/line';
export function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
if (!options) {
options = {};
}
if (typeof options.context === 'undefined') {
options.context = 4;
}
const diff = diffLines(oldStr, newStr, options);
diff.push({value: '', lines: []}); // Append an empty value to make cleanup easier
function contextLines(lines) {
return lines.map(function(entry) { return ' ' + entry; });
}
let hunks = [];
let oldRangeStart = 0, newRangeStart = 0, curRange = [],
oldLine = 1, newLine = 1;
for (let i = 0; i < diff.length; i++) {
const current = diff[i],
lines = current.lines || current.value.replace(/\n$/, '').split('\n');
current.lines = lines;
if (current.added || current.removed) {
// If we have previous context, start with that
if (!oldRangeStart) {
const prev = diff[i - 1];
oldRangeStart = oldLine;
newRangeStart = newLine;
if (prev) {
curRange = options.context > 0 ? contextLines(prev.lines.slice(-options.context)) : [];
oldRangeStart -= curRange.length;
newRangeStart -= curRange.length;
}
}
// Output our changes
curRange.push(... lines.map(function(entry) {
return (current.added ? '+' : '-') + entry;
}));
// Track the updated file position
if (current.added) {
newLine += lines.length;
} else {
oldLine += lines.length;
}
} else {
// Identical context lines. Track line changes
if (oldRangeStart) {
// Close out any changes that have been output (or join overlapping)
if (lines.length <= options.context * 2 && i < diff.length - 2) {
// Overlapping
curRange.push(... contextLines(lines));
} else {
// end the range and output
let contextSize = Math.min(lines.length, options.context);
curRange.push(... contextLines(lines.slice(0, contextSize)));
let hunk = {
oldStart: oldRangeStart,
oldLines: (oldLine - oldRangeStart + contextSize),
newStart: newRangeStart,
newLines: (newLine - newRangeStart + contextSize),
lines: curRange
};
if (i >= diff.length - 2 && lines.length <= options.context) {
// EOF is inside this hunk
let oldEOFNewline = (/\n$/.test(oldStr));
let newEOFNewline = (/\n$/.test(newStr));
let noNlBeforeAdds = lines.length == 0 && curRange.length > hunk.oldLines;
if (!oldEOFNewline && noNlBeforeAdds) {
// special case: old has no eol and no trailing context; no-nl can end up before adds
curRange.splice(hunk.oldLines, 0, '\\ No newline at end of file');
}
if ((!oldEOFNewline && !noNlBeforeAdds) || !newEOFNewline) {
curRange.push('\\ No newline at end of file');
}
}
hunks.push(hunk);
oldRangeStart = 0;
newRangeStart = 0;
curRange = [];
}
}
oldLine += lines.length;
newLine += lines.length;
}
}
return {
oldFileName: oldFileName, newFileName: newFileName,
oldHeader: oldHeader, newHeader: newHeader,
hunks: hunks
};
}
export function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
const diff = structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options);
const 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 (let i = 0; i < diff.hunks.length; i++) {
const 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';
}
export function createPatch(fileName, oldStr, newStr, oldHeader, newHeader, options) {
return createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader, options);
}