phaser4-rex-plugins
Version:
154 lines (131 loc) • 4.68 kB
JavaScript
export function parsePatch(uniDiff, options = {}) {
let diffstr = uniDiff.split(/\r\n|[\n\v\f\r\x85]/),
delimiters = uniDiff.match(/\r\n|[\n\v\f\r\x85]/g) || [],
list = [],
i = 0;
function parseIndex() {
let index = {};
list.push(index);
// Parse diff metadata
while (i < diffstr.length) {
let line = diffstr[i];
// File header found, end parsing diff metadata
if ((/^(\-\-\-|\+\+\+|@@)\s/).test(line)) {
break;
}
// Diff index
let header = (/^(?:Index:|diff(?: -r \w+)+)\s+(.+?)\s*$/).exec(line);
if (header) {
index.index = header[1];
}
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) {
let line = diffstr[i];
if ((/^(Index:|diff|\-\-\-|\+\+\+)\s/).test(line)) {
break;
} else if ((/^@@/).test(line)) {
index.hunks.push(parseHunk());
} else if (line && options.strict) {
// Ignore unexpected content unless in strict mode
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 fileHeader = (/^(---|\+\+\+)\s+(.*)$/).exec(diffstr[i]);
if (fileHeader) {
let keyPrefix = fileHeader[1] === '---' ? 'old' : 'new';
const data = fileHeader[2].split('\t', 2);
let fileName = data[0].replace(/\\\\/g, '\\');
if ((/^".*"$/).test(fileName)) {
fileName = fileName.substr(1, fileName.length - 2);
}
index[keyPrefix + 'FileName'] = fileName;
index[keyPrefix + 'Header'] = (data[1] || '').trim();
i++;
}
}
// Parses a hunk
// This assumes that we are at the start of a hunk.
function parseHunk() {
let chunkHeaderIndex = i,
chunkHeaderLine = diffstr[i++],
chunkHeader = chunkHeaderLine.split(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/);
let hunk = {
oldStart: +chunkHeader[1],
oldLines: typeof chunkHeader[2] === 'undefined' ? 1 : +chunkHeader[2],
newStart: +chunkHeader[3],
newLines: typeof chunkHeader[4] === 'undefined' ? 1 : +chunkHeader[4],
lines: [],
linedelimiters: []
};
// 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; i++) {
// Lines starting with '---' could be mistaken for the "remove line" operation
// But they could be the header for the next file. Therefore prune such cases out.
if (diffstr[i].indexOf('--- ') === 0
&& (i + 2 < diffstr.length)
&& diffstr[i + 1].indexOf('+++ ') === 0
&& diffstr[i + 2].indexOf('@@') === 0) {
break;
}
let operation = (diffstr[i].length == 0 && i != (diffstr.length - 1)) ? ' ' : diffstr[i][0];
if (operation === '+' || operation === '-' || operation === ' ' || operation === '\\') {
hunk.lines.push(diffstr[i]);
hunk.linedelimiters.push(delimiters[i] || '\n');
if (operation === '+') {
addCount++;
} else if (operation === '-') {
removeCount++;
} else if (operation === ' ') {
addCount++;
removeCount++;
}
} else {
break;
}
}
// Handle the empty block count case
if (!addCount && hunk.newLines === 1) {
hunk.newLines = 0;
}
if (!removeCount && hunk.oldLines === 1) {
hunk.oldLines = 0;
}
// Perform optional sanity checking
if (options.strict) {
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;
}