Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
parse.js148 linesDownload Raw Back to patch
1/**
2 * Parses a patch into structured data, in the same structure returned by `structuredPatch`.
3 *
4 * @return a JSON object representation of the a patch, suitable for use with the `applyPatch` method.
5 */
6export function parsePatch(uniDiff) {
7    const diffstr = uniDiff.split(/\n/), list = [];
8    let i = 0;
9    function parseIndex() {
10        const index = {};
11        list.push(index);
12        // Parse diff metadata
13        while (i < diffstr.length) {
14            const line = diffstr[i];
15            // File header found, end parsing diff metadata
16            if ((/^(---|\+\+\+|@@)\s/).test(line)) {
17                break;
18            }
19            // Try to parse the line as a diff header, like
20            //     Index: README.md
21            // or
22            //     diff -r 9117c6561b0b -r 273ce12ad8f1 .hgignore
23            // or
24            //     Index: something with multiple words
25            // and extract the filename (or whatever else is used as an index name)
26            // from the end (i.e. 'README.md', '.hgignore', or
27            // 'something with multiple words' in the examples above).
28            //
29            // TODO: It seems awkward that we indiscriminately trim off trailing
30            //       whitespace here. Theoretically, couldn't that be meaningful -
31            //       e.g. if the patch represents a diff of a file whose name ends
32            //       with a space? Seems wrong to nuke it.
33            //       But this behaviour has been around since v2.2.1 in 2015, so if
34            //       it's going to change, it should be done cautiously and in a new
35            //       major release, for backwards-compat reasons.
36            //       -- ExplodingCabbage
37            const headerMatch = (/^(?:Index:|diff(?: -r \w+)+)\s+/).exec(line);
38            if (headerMatch) {
39                index.index = line.substring(headerMatch[0].length).trim();
40            }
41            i++;
42        }
43        // Parse file headers if they are defined. Unified diff requires them, but
44        // there's no technical issues to have an isolated hunk without file header
45        parseFileHeader(index);
46        parseFileHeader(index);
47        // Parse hunks
48        index.hunks = [];
49        while (i < diffstr.length) {
50            const line = diffstr[i];
51            if ((/^(Index:\s|diff\s|---\s|\+\+\+\s|===================================================================)/).test(line)) {
52                break;
53            }
54            else if ((/^@@/).test(line)) {
55                index.hunks.push(parseHunk());
56            }
57            else if (line) {
58                throw new Error('Unknown line ' + (i + 1) + ' ' + JSON.stringify(line));
59            }
60            else {
61                i++;
62            }
63        }
64    }
65    // Parses the --- and +++ headers, if none are found, no lines
66    // are consumed.
67    function parseFileHeader(index) {
68        const fileHeaderMatch = (/^(---|\+\+\+)\s+/).exec(diffstr[i]);
69        if (fileHeaderMatch) {
70            const prefix = fileHeaderMatch[1], data = diffstr[i].substring(3).trim().split('\t', 2), header = (data[1] || '').trim();
71            let fileName = data[0].replace(/\\\\/g, '\\');
72            if (fileName.startsWith('"') && fileName.endsWith('"')) {
73                fileName = fileName.substr(1, fileName.length - 2);
74            }
75            if (prefix === '---') {
76                index.oldFileName = fileName;
77                index.oldHeader = header;
78            }
79            else {
80                index.newFileName = fileName;
81                index.newHeader = header;
82            }
83            i++;
84        }
85    }
86    // Parses a hunk
87    // This assumes that we are at the start of a hunk.
88    function parseHunk() {
89        var _a;
90        const chunkHeaderIndex = i, chunkHeaderLine = diffstr[i++], chunkHeader = chunkHeaderLine.split(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/);
91        const hunk = {
92            oldStart: +chunkHeader[1],
93            oldLines: typeof chunkHeader[2] === 'undefined' ? 1 : +chunkHeader[2],
94            newStart: +chunkHeader[3],
95            newLines: typeof chunkHeader[4] === 'undefined' ? 1 : +chunkHeader[4],
96            lines: []
97        };
98        // Unified Diff Format quirk: If the chunk size is 0,
99        // the first number is one lower than one would expect.
100        // https://www.artima.com/weblogs/viewpost.jsp?thread=164293
101        if (hunk.oldLines === 0) {
102            hunk.oldStart += 1;
103        }
104        if (hunk.newLines === 0) {
105            hunk.newStart += 1;
106        }
107        let addCount = 0, removeCount = 0;
108        for (; i < diffstr.length && (removeCount < hunk.oldLines || addCount < hunk.newLines || ((_a = diffstr[i]) === null || _a === void 0 ? void 0 : _a.startsWith('\\'))); i++) {
109            const operation = (diffstr[i].length == 0 && i != (diffstr.length - 1)) ? ' ' : diffstr[i][0];
110            if (operation === '+' || operation === '-' || operation === ' ' || operation === '\\') {
111                hunk.lines.push(diffstr[i]);
112                if (operation === '+') {
113                    addCount++;
114                }
115                else if (operation === '-') {
116                    removeCount++;
117                }
118                else if (operation === ' ') {
119                    addCount++;
120                    removeCount++;
121                }
122            }
123            else {
124                throw new Error(`Hunk at line ${chunkHeaderIndex + 1} contained invalid line ${diffstr[i]}`);
125            }
126        }
127        // Handle the empty block count case
128        if (!addCount && hunk.newLines === 1) {
129            hunk.newLines = 0;
130        }
131        if (!removeCount && hunk.oldLines === 1) {
132            hunk.oldLines = 0;
133        }
134        // Perform sanity checking
135        if (addCount !== hunk.newLines) {
136            throw new Error('Added line count did not match for hunk at line ' + (chunkHeaderIndex + 1));
137        }
138        if (removeCount !== hunk.oldLines) {
139            throw new Error('Removed line count did not match for hunk at line ' + (chunkHeaderIndex + 1));
140        }
141        return hunk;
142    }
143    while (i < diffstr.length) {
144        parseIndex();
145    }
146    return list;
147}
148 
codekingpro/portable-devtools · Team Ai