Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
create.js229 linesDownload Raw Back to patch
1import { diffLines } from '../diff/line.js';
2export const INCLUDE_HEADERS = {
3    includeIndex: true,
4    includeUnderline: true,
5    includeFileHeaders: true
6};
7export const FILE_HEADERS_ONLY = {
8    includeIndex: false,
9    includeUnderline: false,
10    includeFileHeaders: true
11};
12export const OMIT_HEADERS = {
13    includeIndex: false,
14    includeUnderline: false,
15    includeFileHeaders: false
16};
17export function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
18    let optionsObj;
19    if (!options) {
20        optionsObj = {};
21    }
22    else if (typeof options === 'function') {
23        optionsObj = { callback: options };
24    }
25    else {
26        optionsObj = options;
27    }
28    if (typeof optionsObj.context === 'undefined') {
29        optionsObj.context = 4;
30    }
31    // We copy this into its own variable to placate TypeScript, which thinks
32    // optionsObj.context might be undefined in the callbacks below.
33    const context = optionsObj.context;
34    // @ts-expect-error (runtime check for something that is correctly a static type error)
35    if (optionsObj.newlineIsToken) {
36        throw new Error('newlineIsToken may not be used with patch-generation functions, only with diffing functions');
37    }
38    if (!optionsObj.callback) {
39        return diffLinesResultToPatch(diffLines(oldStr, newStr, optionsObj));
40    }
41    else {
42        const { callback } = optionsObj;
43        diffLines(oldStr, newStr, Object.assign(Object.assign({}, optionsObj), { callback: (diff) => {
44                const patch = diffLinesResultToPatch(diff);
45                // TypeScript is unhappy without the cast because it does not understand that `patch` may
46                // be undefined here only if `callback` is StructuredPatchCallbackAbortable:
47                callback(patch);
48            } }));
49    }
50    function diffLinesResultToPatch(diff) {
51        // STEP 1: Build up the patch with no "\ No newline at end of file" lines and with the arrays
52        //         of lines containing trailing newline characters. We'll tidy up later...
53        if (!diff) {
54            return;
55        }
56        diff.push({ value: '', lines: [] }); // Append an empty value to make cleanup easier
57        function contextLines(lines) {
58            return lines.map(function (entry) { return ' ' + entry; });
59        }
60        const hunks = [];
61        let oldRangeStart = 0, newRangeStart = 0, curRange = [], oldLine = 1, newLine = 1;
62        for (let i = 0; i < diff.length; i++) {
63            const current = diff[i], lines = current.lines || splitLines(current.value);
64            current.lines = lines;
65            if (current.added || current.removed) {
66                // If we have previous context, start with that
67                if (!oldRangeStart) {
68                    const prev = diff[i - 1];
69                    oldRangeStart = oldLine;
70                    newRangeStart = newLine;
71                    if (prev) {
72                        curRange = context > 0 ? contextLines(prev.lines.slice(-context)) : [];
73                        oldRangeStart -= curRange.length;
74                        newRangeStart -= curRange.length;
75                    }
76                }
77                // Output our changes
78                for (const line of lines) {
79                    curRange.push((current.added ? '+' : '-') + line);
80                }
81                // Track the updated file position
82                if (current.added) {
83                    newLine += lines.length;
84                }
85                else {
86                    oldLine += lines.length;
87                }
88            }
89            else {
90                // Identical context lines. Track line changes
91                if (oldRangeStart) {
92                    // Close out any changes that have been output (or join overlapping)
93                    if (lines.length <= context * 2 && i < diff.length - 2) {
94                        // Overlapping
95                        for (const line of contextLines(lines)) {
96                            curRange.push(line);
97                        }
98                    }
99                    else {
100                        // end the range and output
101                        const contextSize = Math.min(lines.length, context);
102                        for (const line of contextLines(lines.slice(0, contextSize))) {
103                            curRange.push(line);
104                        }
105                        const hunk = {
106                            oldStart: oldRangeStart,
107                            oldLines: (oldLine - oldRangeStart + contextSize),
108                            newStart: newRangeStart,
109                            newLines: (newLine - newRangeStart + contextSize),
110                            lines: curRange
111                        };
112                        hunks.push(hunk);
113                        oldRangeStart = 0;
114                        newRangeStart = 0;
115                        curRange = [];
116                    }
117                }
118                oldLine += lines.length;
119                newLine += lines.length;
120            }
121        }
122        // Step 2: eliminate the trailing `\n` from each line of each hunk, and, where needed, add
123        //         "\ No newline at end of file".
124        for (const hunk of hunks) {
125            for (let i = 0; i < hunk.lines.length; i++) {
126                if (hunk.lines[i].endsWith('\n')) {
127                    hunk.lines[i] = hunk.lines[i].slice(0, -1);
128                }
129                else {
130                    hunk.lines.splice(i + 1, 0, '\\ No newline at end of file');
131                    i++; // Skip the line we just added, then continue iterating
132                }
133            }
134        }
135        return {
136            oldFileName: oldFileName, newFileName: newFileName,
137            oldHeader: oldHeader, newHeader: newHeader,
138            hunks: hunks
139        };
140    }
141}
142/**
143 * creates a unified diff patch.
144 * @param patch either a single structured patch object (as returned by `structuredPatch`) or an array of them (as returned by `parsePatch`)
145 */
146export function formatPatch(patch, headerOptions) {
147    if (!headerOptions) {
148        headerOptions = INCLUDE_HEADERS;
149    }
150    if (Array.isArray(patch)) {
151        if (patch.length > 1 && !headerOptions.includeFileHeaders) {
152            throw new Error('Cannot omit file headers on a multi-file patch. '
153                + '(The result would be unparseable; how would a tool trying to apply '
154                + 'the patch know which changes are to which file?)');
155        }
156        return patch.map(p => formatPatch(p, headerOptions)).join('\n');
157    }
158    const ret = [];
159    if (headerOptions.includeIndex && patch.oldFileName == patch.newFileName) {
160        ret.push('Index: ' + patch.oldFileName);
161    }
162    if (headerOptions.includeUnderline) {
163        ret.push('===================================================================');
164    }
165    if (headerOptions.includeFileHeaders) {
166        ret.push('--- ' + patch.oldFileName + (typeof patch.oldHeader === 'undefined' ? '' : '\t' + patch.oldHeader));
167        ret.push('+++ ' + patch.newFileName + (typeof patch.newHeader === 'undefined' ? '' : '\t' + patch.newHeader));
168    }
169    for (let i = 0; i < patch.hunks.length; i++) {
170        const hunk = patch.hunks[i];
171        // Unified Diff Format quirk: If the chunk size is 0,
172        // the first number is one lower than one would expect.
173        // https://www.artima.com/weblogs/viewpost.jsp?thread=164293
174        if (hunk.oldLines === 0) {
175            hunk.oldStart -= 1;
176        }
177        if (hunk.newLines === 0) {
178            hunk.newStart -= 1;
179        }
180        ret.push('@@ -' + hunk.oldStart + ',' + hunk.oldLines
181            + ' +' + hunk.newStart + ',' + hunk.newLines
182            + ' @@');
183        for (const line of hunk.lines) {
184            ret.push(line);
185        }
186    }
187    return ret.join('\n') + '\n';
188}
189export function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
190    if (typeof options === 'function') {
191        options = { callback: options };
192    }
193    if (!(options === null || options === void 0 ? void 0 : options.callback)) {
194        const patchObj = structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options);
195        if (!patchObj) {
196            return;
197        }
198        return formatPatch(patchObj, options === null || options === void 0 ? void 0 : options.headerOptions);
199    }
200    else {
201        const { callback } = options;
202        structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, Object.assign(Object.assign({}, options), { callback: patchObj => {
203                if (!patchObj) {
204                    callback(undefined);
205                }
206                else {
207                    callback(formatPatch(patchObj, options.headerOptions));
208                }
209            } }));
210    }
211}
212export function createPatch(fileName, oldStr, newStr, oldHeader, newHeader, options) {
213    return createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader, options);
214}
215/**
216 * Split `text` into an array of lines, including the trailing newline character (where present)
217 */
218function splitLines(text) {
219    const hasTrailingNl = text.endsWith('\n');
220    const result = text.split('\n').map(line => line + '\n');
221    if (hasTrailingNl) {
222        result.pop();
223    }
224    else {
225        result.push(result.pop().slice(0, -1));
226    }
227    return result;
228}
229 
codekingpro/portable-devtools · Team Ai