Skip to content

Commit 532bcf9

Browse files
committed
Implement patch parser
This provides better support of multiple indexes, etc and will allow for multi-file patching.
1 parent ed26acd commit 532bcf9

3 files changed

Lines changed: 388 additions & 0 deletions

File tree

src/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {diffCss} from './diff/css';
2424
import {diffJson, canonicalize} from './diff/json';
2525

2626
import {applyPatch} from './patch/apply';
27+
import {parsePatch} from './patch/parse';
2728
import {structuredPatch, createTwoFilesPatch, createPatch} from './patch/create';
2829

2930
import {convertChangesToDMP} from './convert/dmp';
@@ -46,6 +47,7 @@ export {
4647
createTwoFilesPatch,
4748
createPatch,
4849
applyPatch,
50+
parsePatch,
4951
convertChangesToDMP,
5052
convertChangesToXML,
5153
canonicalize

src/patch/parse.js

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
export function parsePatch(uniDiff, options = {}) {
2+
let diffstr = uniDiff.split('\n'),
3+
list = [],
4+
i = 0;
5+
6+
function parseIndex() {
7+
let index = {};
8+
list.push(index);
9+
10+
let header = (/^Index: (.*)/.exec(diffstr[i]));
11+
if (header) {
12+
index.index = header[1];
13+
i++;
14+
15+
if (/^===/.test(diffstr[i])) {
16+
i++;
17+
}
18+
19+
parseFileHeader(index);
20+
parseFileHeader(index);
21+
} else {
22+
// Ignore erant header components that might occur at the start of the file
23+
parseFileHeader({});
24+
parseFileHeader({});
25+
}
26+
27+
index.hunks = [];
28+
29+
while (i < diffstr.length) {
30+
if (/^Index:/.test(diffstr[i])) {
31+
break;
32+
} else if (/^@@/.test(diffstr[i])) {
33+
index.hunks.push(parseHunk(index));
34+
} else if (!diffstr[i]) {
35+
i++;
36+
} else {
37+
throw new Error('Unknown line ' + (i + 1) + ' ' + JSON.stringify(diffstr[i]));
38+
}
39+
}
40+
}
41+
42+
// Parses the --- and +++ headers, if none are found, no lines
43+
// are consumed.
44+
function parseFileHeader(index) {
45+
let fileHeader = (/^(\-\-\-|\+\+\+)\s(\S+)\s?(.*)/.exec(diffstr[i]));
46+
if (fileHeader) {
47+
index[fileHeader[1] === '---' ? 'from' : 'to'] = {
48+
file: fileHeader[2],
49+
header: fileHeader[3]
50+
};
51+
i++;
52+
}
53+
}
54+
55+
// Parses a hunk
56+
// This assumes that we are at the start of a hunk.
57+
function parseHunk(index) {
58+
let chunkHeaderIndex = i,
59+
chunkHeaderLine = diffstr[i++],
60+
chunkHeader = chunkHeaderLine.split(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/);
61+
62+
let hunk = {
63+
from: {
64+
line: +chunkHeader[1],
65+
count: +chunkHeader[2] || 1
66+
},
67+
to: {
68+
line: +chunkHeader[3],
69+
count: +chunkHeader[4] || 1
70+
},
71+
lines: []
72+
};
73+
74+
let addCount = 0,
75+
removeCount = 0;
76+
for (; i < diffstr.length; i++) {
77+
let operation = diffstr[i][0],
78+
content = diffstr[i].substr(1);
79+
80+
if (operation === '+' || operation === '-' || operation === ' ') {
81+
hunk.lines.push({operation, content});
82+
83+
if (operation === '+') {
84+
addCount++;
85+
} else if (operation === '-') {
86+
removeCount++;
87+
} else {
88+
addCount++;
89+
removeCount++;
90+
}
91+
} else if (operation === '\\') {
92+
if (diffstr[i - 1][0] === '+') {
93+
index.removeEOFNL = true;
94+
} else if (diffstr[i - 1][0] === '-') {
95+
index.addEOFNL = true;
96+
}
97+
} else {
98+
break;
99+
}
100+
}
101+
102+
// Handle the empty block count case
103+
if (!addCount && hunk.to.count === 1) {
104+
hunk.to.count = 0;
105+
}
106+
if (!removeCount && hunk.from.count === 1) {
107+
hunk.from.count = 0;
108+
}
109+
110+
// Perform optional sanity checking
111+
if (options.strict) {
112+
if (addCount !== hunk.to.count) {
113+
throw new Error('Added line count did not match for hunk at line ' + (chunkHeaderIndex + 1));
114+
}
115+
if (removeCount !== hunk.from.count) {
116+
throw new Error('Removed line count did not match for hunk at line ' + (chunkHeaderIndex + 1));
117+
}
118+
}
119+
120+
return hunk;
121+
}
122+
123+
while (i < diffstr.length) {
124+
parseIndex();
125+
}
126+
127+
return list;
128+
}

0 commit comments

Comments
 (0)