forked from nodejs/node
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathparse.js
More file actions
147 lines (147 loc) · 6.07 KB
/
Copy pathparse.js
File metadata and controls
147 lines (147 loc) · 6.07 KB
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/**
* Parses a patch into structured data, in the same structure returned by `structuredPatch`.
*
* @return a JSON object representation of the a patch, suitable for use with the `applyPatch` method.
*/
export function parsePatch(uniDiff) {
const diffstr = uniDiff.split(/\n/), list = [];
let i = 0;
function parseIndex() {
const index = {};
list.push(index);
// Parse diff metadata
while (i < diffstr.length) {
const line = diffstr[i];
// File header found, end parsing diff metadata
if ((/^(---|\+\+\+|@@)\s/).test(line)) {
break;
}
// Try to parse the line as a diff header, like
// Index: README.md
// or
// diff -r 9117c6561b0b -r 273ce12ad8f1 .hgignore
// or
// Index: something with multiple words
// and extract the filename (or whatever else is used as an index name)
// from the end (i.e. 'README.md', '.hgignore', or
// 'something with multiple words' in the examples above).
//
// TODO: It seems awkward that we indiscriminately trim off trailing
// whitespace here. Theoretically, couldn't that be meaningful -
// e.g. if the patch represents a diff of a file whose name ends
// with a space? Seems wrong to nuke it.
// But this behaviour has been around since v2.2.1 in 2015, so if
// it's going to change, it should be done cautiously and in a new
// major release, for backwards-compat reasons.
// -- ExplodingCabbage
const headerMatch = (/^(?:Index:|diff(?: -r \w+)+)\s+/).exec(line);
if (headerMatch) {
index.index = line.substring(headerMatch[0].length).trim();
}
i++;
}
// Parse file headers if they are defined. Unified diff requires them, but
// there's no technical issues to have an isolated hunk without file header
parseFileHeader(index);
parseFileHeader(index);
// Parse hunks
index.hunks = [];
while (i < diffstr.length) {
const line = diffstr[i];
if ((/^(Index:\s|diff\s|---\s|\+\+\+\s|===================================================================)/).test(line)) {
break;
}
else if ((/^@@/).test(line)) {
index.hunks.push(parseHunk());
}
else if (line) {
throw new Error('Unknown line ' + (i + 1) + ' ' + JSON.stringify(line));
}
else {
i++;
}
}
}
// Parses the --- and +++ headers, if none are found, no lines
// are consumed.
function parseFileHeader(index) {
const fileHeaderMatch = (/^(---|\+\+\+)\s+/).exec(diffstr[i]);
if (fileHeaderMatch) {
const prefix = fileHeaderMatch[1], data = diffstr[i].substring(3).trim().split('\t', 2), header = (data[1] || '').trim();
let fileName = data[0].replace(/\\\\/g, '\\');
if (fileName.startsWith('"') && fileName.endsWith('"')) {
fileName = fileName.substr(1, fileName.length - 2);
}
if (prefix === '---') {
index.oldFileName = fileName;
index.oldHeader = header;
}
else {
index.newFileName = fileName;
index.newHeader = header;
}
i++;
}
}
// Parses a hunk
// This assumes that we are at the start of a hunk.
function parseHunk() {
var _a;
const chunkHeaderIndex = i, chunkHeaderLine = diffstr[i++], chunkHeader = chunkHeaderLine.split(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/);
const hunk = {
oldStart: +chunkHeader[1],
oldLines: typeof chunkHeader[2] === 'undefined' ? 1 : +chunkHeader[2],
newStart: +chunkHeader[3],
newLines: typeof chunkHeader[4] === 'undefined' ? 1 : +chunkHeader[4],
lines: []
};
// Unified Diff Format quirk: If the chunk size is 0,
// the first number is one lower than one would expect.
// https://www.artima.com/weblogs/viewpost.jsp?thread=164293
if (hunk.oldLines === 0) {
hunk.oldStart += 1;
}
if (hunk.newLines === 0) {
hunk.newStart += 1;
}
let addCount = 0, removeCount = 0;
for (; i < diffstr.length && (removeCount < hunk.oldLines || addCount < hunk.newLines || ((_a = diffstr[i]) === null || _a === void 0 ? void 0 : _a.startsWith('\\'))); i++) {
const operation = (diffstr[i].length == 0 && i != (diffstr.length - 1)) ? ' ' : diffstr[i][0];
if (operation === '+' || operation === '-' || operation === ' ' || operation === '\\') {
hunk.lines.push(diffstr[i]);
if (operation === '+') {
addCount++;
}
else if (operation === '-') {
removeCount++;
}
else if (operation === ' ') {
addCount++;
removeCount++;
}
}
else {
throw new Error(`Hunk at line ${chunkHeaderIndex + 1} contained invalid line ${diffstr[i]}`);
}
}
// Handle the empty block count case
if (!addCount && hunk.newLines === 1) {
hunk.newLines = 0;
}
if (!removeCount && hunk.oldLines === 1) {
hunk.oldLines = 0;
}
// Perform sanity checking
if (addCount !== hunk.newLines) {
throw new Error('Added line count did not match for hunk at line ' + (chunkHeaderIndex + 1));
}
if (removeCount !== hunk.oldLines) {
throw new Error('Removed line count did not match for hunk at line ' + (chunkHeaderIndex + 1));
}
return hunk;
}
while (i < diffstr.length) {
parseIndex();
}
return list;
}