@gitsense/gsc-utils 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/gitsense-chat-utils.cjs.js +10977 -0
- package/dist/gitsense-chat-utils.esm.js +10975 -0
- package/dist/gsc-utils.cjs.js +11043 -0
- package/dist/gsc-utils.esm.js +11041 -0
- package/package.json +37 -0
- package/src/AnalysisBlockUtils.js +151 -0
- package/src/ChatUtils.js +126 -0
- package/src/CodeBlockUtils/blockExtractor.js +277 -0
- package/src/CodeBlockUtils/blockProcessor.js +559 -0
- package/src/CodeBlockUtils/blockProcessor.js.rej +8 -0
- package/src/CodeBlockUtils/constants.js +62 -0
- package/src/CodeBlockUtils/continuationUtils.js +191 -0
- package/src/CodeBlockUtils/headerParser.js +175 -0
- package/src/CodeBlockUtils/headerUtils.js +236 -0
- package/src/CodeBlockUtils/index.js +83 -0
- package/src/CodeBlockUtils/lineNumberFormatter.js +117 -0
- package/src/CodeBlockUtils/markerRemover.js +89 -0
- package/src/CodeBlockUtils/patchIntegration.js +38 -0
- package/src/CodeBlockUtils/relationshipUtils.js +159 -0
- package/src/CodeBlockUtils/updateCodeBlock.js +372 -0
- package/src/CodeBlockUtils/uuidUtils.js +48 -0
- package/src/ContextUtils.js +180 -0
- package/src/GSToolBlockUtils.js +108 -0
- package/src/GitSenseChatUtils.js +386 -0
- package/src/JsonUtils.js +101 -0
- package/src/LLMUtils.js +31 -0
- package/src/MessageUtils.js +460 -0
- package/src/PatchUtils/constants.js +72 -0
- package/src/PatchUtils/diagnosticReporter.js +213 -0
- package/src/PatchUtils/enhancedPatchProcessor.js +390 -0
- package/src/PatchUtils/fuzzyMatcher.js +252 -0
- package/src/PatchUtils/hunkCorrector.js +204 -0
- package/src/PatchUtils/hunkValidator.js +305 -0
- package/src/PatchUtils/index.js +135 -0
- package/src/PatchUtils/patchExtractor.js +175 -0
- package/src/PatchUtils/patchHeaderFormatter.js +143 -0
- package/src/PatchUtils/patchParser.js +289 -0
- package/src/PatchUtils/patchProcessor.js +389 -0
- package/src/PatchUtils/patchVerifier/constants.js +23 -0
- package/src/PatchUtils/patchVerifier/detectAndFixOverlappingHunks.js +281 -0
- package/src/PatchUtils/patchVerifier/detectAndFixRedundantChanges.js +404 -0
- package/src/PatchUtils/patchVerifier/formatAndAddLineNumbers.js +165 -0
- package/src/PatchUtils/patchVerifier/index.js +25 -0
- package/src/PatchUtils/patchVerifier/verifyAndCorrectHunkHeaders.js +202 -0
- package/src/PatchUtils/patchVerifier/verifyAndCorrectLineNumbers.js +254 -0
- package/src/SharedUtils/timestampUtils.js +41 -0
- package/src/SharedUtils/versionUtils.js +58 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component: PatchUtils Hunk Validator
|
|
3
|
+
* Block-UUID: 7f9e4d2c-1b6a-4f85-9c3d-8e2a5b7f9d0e
|
|
4
|
+
* Parent-UUID: 6d830314-b75d-48df-b23a-8467672c9503
|
|
5
|
+
* Version: 1.3.0
|
|
6
|
+
* Description: Validates individual hunks with detailed diagnostics and fuzzy matching capabilities.
|
|
7
|
+
* Language: JavaScript
|
|
8
|
+
* Created-at: 2025-05-14T18:07:21.058Z
|
|
9
|
+
* Authors: Claude 3.7 Sonnet (v1.0.0), Claude 3.5 Sonnet (v1.1.0, v1.2.0), Claude 3.5 Sonnet (v1.3.0), Gemini 2.5 Flash (v1.3.0)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
const jsdiff = require('diff');
|
|
14
|
+
const { findBestContextMatch, findBestMatchWithSlidingWindow } = require('./fuzzyMatcher');
|
|
15
|
+
const { removeLineNumbers } = require('../CodeBlockUtils/lineNumberFormatter');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Parses a hunk into its components
|
|
19
|
+
* @param {string} hunkText - The full text of a single hunk
|
|
20
|
+
* @returns {object} Parsed hunk with header, context lines, added lines, and removed lines
|
|
21
|
+
*/
|
|
22
|
+
function parseHunk(hunkText) {
|
|
23
|
+
const lines = hunkText.split('\n');
|
|
24
|
+
const header = lines[0];
|
|
25
|
+
const contentLines = lines.slice(1);
|
|
26
|
+
|
|
27
|
+
// Arrays to store the different line types
|
|
28
|
+
const contextLines = [];
|
|
29
|
+
const contextLinesForMatching = [];
|
|
30
|
+
const addedLines = [];
|
|
31
|
+
const removedLines = [];
|
|
32
|
+
|
|
33
|
+
// Track current position in the hunk for building matching context
|
|
34
|
+
let currentPosition = 0;
|
|
35
|
+
|
|
36
|
+
for (const line of contentLines) {
|
|
37
|
+
// Clean line numbers from the content
|
|
38
|
+
const cleanLine = removeLineNumbers(line.substring(1));
|
|
39
|
+
|
|
40
|
+
if (line.startsWith(' ')) {
|
|
41
|
+
// Regular context line - add to both context arrays
|
|
42
|
+
contextLines.push(cleanLine);
|
|
43
|
+
contextLinesForMatching.push(cleanLine);
|
|
44
|
+
currentPosition++;
|
|
45
|
+
} else if (line.startsWith('+')) {
|
|
46
|
+
// Added line - only track in addedLines
|
|
47
|
+
addedLines.push(cleanLine);
|
|
48
|
+
currentPosition++;
|
|
49
|
+
} else if (line.startsWith('-')) {
|
|
50
|
+
// Removed line - add to removedLines and matching context
|
|
51
|
+
removedLines.push(cleanLine);
|
|
52
|
+
contextLinesForMatching.push(cleanLine);
|
|
53
|
+
currentPosition++;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
header,
|
|
59
|
+
// Original context lines (space-prefixed only)
|
|
60
|
+
contextLines,
|
|
61
|
+
// Context lines for matching against source (includes removed lines)
|
|
62
|
+
contextLinesForMatching,
|
|
63
|
+
// Lines being added and removed
|
|
64
|
+
addedLines,
|
|
65
|
+
removedLines,
|
|
66
|
+
// Original content with prefixes intact
|
|
67
|
+
originalLines: contentLines
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Parses a hunk header into its components
|
|
73
|
+
* @param {string} header - The hunk header (e.g., "@@ -1,3 +1,4 @@")
|
|
74
|
+
* @returns {object} Parsed header with oldStart, oldCount, newStart, newCount
|
|
75
|
+
*/
|
|
76
|
+
function parseHunkHeader(header) {
|
|
77
|
+
const match = header.match(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/);
|
|
78
|
+
|
|
79
|
+
if (!match) {
|
|
80
|
+
return {
|
|
81
|
+
valid: false,
|
|
82
|
+
error: "Invalid hunk header format"
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const oldStart = parseInt(match[1], 10);
|
|
87
|
+
const oldCount = match[2] ? parseInt(match[2], 10) : 1;
|
|
88
|
+
const newStart = parseInt(match[3], 10);
|
|
89
|
+
const newCount = match[4] ? parseInt(match[4], 10) : 1;
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
valid: true,
|
|
93
|
+
oldStart,
|
|
94
|
+
oldCount,
|
|
95
|
+
newStart,
|
|
96
|
+
newCount
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Attempts to apply a hunk directly at the specified position
|
|
102
|
+
* @param {string} sourceCode - Original source code
|
|
103
|
+
* @param {number} startLine - Starting line number (1-based)
|
|
104
|
+
* @param {string[]} contextLines - Context lines from the hunk
|
|
105
|
+
* @param {string[]} contextLinesForMatching - Context lines including removed lines for matching
|
|
106
|
+
* @param {string[]} addedLines - Lines to add
|
|
107
|
+
* @param {string[]} removedLines - Lines to remove
|
|
108
|
+
* @returns {object} Result of the direct application attempt
|
|
109
|
+
*/
|
|
110
|
+
function tryApplyHunkDirect(sourceCode, startLine, contextLines, contextLinesForMatching, addedLines, removedLines) {
|
|
111
|
+
const sourceLines = sourceCode.split('\n');
|
|
112
|
+
const startIndex = startLine - 1;
|
|
113
|
+
|
|
114
|
+
// If the starting position seems wrong, try to find the correct position
|
|
115
|
+
if (startIndex < 0 || startIndex >= sourceLines.length) {
|
|
116
|
+
const matchResult = findBestContextMatch(contextLinesForMatching, sourceCode);
|
|
117
|
+
|
|
118
|
+
if (matchResult.found) {
|
|
119
|
+
// Recursively try applying at the found position
|
|
120
|
+
return tryApplyHunkDirect(
|
|
121
|
+
sourceCode,
|
|
122
|
+
matchResult.position + 1,
|
|
123
|
+
contextLines,
|
|
124
|
+
contextLinesForMatching,
|
|
125
|
+
addedLines,
|
|
126
|
+
removedLines
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
valid: false,
|
|
132
|
+
reason: "Could not find matching context in source code"
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Verify the complete context (including removed lines) matches at the current position
|
|
137
|
+
let contextMatches = true;
|
|
138
|
+
let sourceIndex = startIndex;
|
|
139
|
+
|
|
140
|
+
// Verify the complete context matches
|
|
141
|
+
for (const expectedLine of contextLinesForMatching) {
|
|
142
|
+
if (sourceIndex >= sourceLines.length) {
|
|
143
|
+
contextMatches = false;
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const sourceLine = sourceLines[sourceIndex].trim();
|
|
148
|
+
const expectedTrimmed = expectedLine.trim();
|
|
149
|
+
|
|
150
|
+
if (sourceLine !== expectedTrimmed) {
|
|
151
|
+
contextMatches = false;
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
sourceIndex++;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (!contextMatches) {
|
|
159
|
+
return {
|
|
160
|
+
valid: false,
|
|
161
|
+
reason: "Context lines (including removed lines) don't match at specified position"
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Apply the changes
|
|
166
|
+
const resultLines = [...sourceLines];
|
|
167
|
+
|
|
168
|
+
// Remove the old lines
|
|
169
|
+
resultLines.splice(startIndex, contextLinesForMatching.length);
|
|
170
|
+
|
|
171
|
+
// Add context and new lines
|
|
172
|
+
const newContent = [];
|
|
173
|
+
for (let i = 0; i < contextLines.length; i++) {
|
|
174
|
+
newContent.push(contextLines[i]);
|
|
175
|
+
if (i < addedLines.length) {
|
|
176
|
+
newContent.push(addedLines[i]);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Add any remaining new lines
|
|
181
|
+
for (let i = contextLines.length; i < addedLines.length; i++) {
|
|
182
|
+
newContent.push(addedLines[i]);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Insert the new content
|
|
186
|
+
resultLines.splice(startIndex, 0, ...newContent);
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
valid: true,
|
|
190
|
+
result: resultLines.join('\n')
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Validates a single hunk against source code with detailed diagnostics
|
|
196
|
+
* @param {string} sourceCode - Original source code
|
|
197
|
+
* @param {string} hunkText - The full text of a single hunk
|
|
198
|
+
* @returns {object} Detailed validation results
|
|
199
|
+
*/
|
|
200
|
+
function validateHunk(sourceCode, hunkText) {
|
|
201
|
+
// Parse the hunk
|
|
202
|
+
const hunk = parseHunk(hunkText);
|
|
203
|
+
const headerInfo = parseHunkHeader(hunk.header);
|
|
204
|
+
|
|
205
|
+
if (!headerInfo.valid) {
|
|
206
|
+
return {
|
|
207
|
+
valid: false,
|
|
208
|
+
hunk,
|
|
209
|
+
diagnostics: {
|
|
210
|
+
headerValid: false,
|
|
211
|
+
headerError: headerInfo.error,
|
|
212
|
+
suggestedFix: null
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Try direct application first
|
|
218
|
+
const directResult = tryApplyHunkDirect(
|
|
219
|
+
sourceCode,
|
|
220
|
+
headerInfo.oldStart,
|
|
221
|
+
hunk.contextLines,
|
|
222
|
+
hunk.contextLinesForMatching,
|
|
223
|
+
hunk.addedLines,
|
|
224
|
+
hunk.removedLines
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
if (directResult.valid) {
|
|
228
|
+
return {
|
|
229
|
+
valid: true,
|
|
230
|
+
appliedText: directResult.result,
|
|
231
|
+
hunk,
|
|
232
|
+
headerInfo,
|
|
233
|
+
diagnostics: {
|
|
234
|
+
method: "direct",
|
|
235
|
+
confidence: 1.0,
|
|
236
|
+
message: "Hunk applied successfully at specified position"
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// If direct application failed, try finding the correct position
|
|
242
|
+
const matchResult = findBestContextMatch(hunk.contextLinesForMatching, sourceCode); // Use the new array for matching
|
|
243
|
+
|
|
244
|
+
if (matchResult.found) {
|
|
245
|
+
const adjustedResult = tryApplyHunkDirect(
|
|
246
|
+
sourceCode,
|
|
247
|
+
matchResult.position + 1,
|
|
248
|
+
hunk.contextLines,
|
|
249
|
+
hunk.contextLinesForMatching, // Use the new array for matching
|
|
250
|
+
hunk.addedLines,
|
|
251
|
+
hunk.removedLines
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
if (adjustedResult.valid) {
|
|
255
|
+
return {
|
|
256
|
+
valid: true,
|
|
257
|
+
appliedText: adjustedResult.result,
|
|
258
|
+
hunk,
|
|
259
|
+
headerInfo,
|
|
260
|
+
diagnostics: {
|
|
261
|
+
method: "adjusted",
|
|
262
|
+
confidence: matchResult.confidence,
|
|
263
|
+
message: `Hunk applied successfully at adjusted position (line ${matchResult.position + 1})`
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// If we get here, both direct and adjusted attempts failed
|
|
270
|
+
return {
|
|
271
|
+
valid: false,
|
|
272
|
+
hunk,
|
|
273
|
+
headerInfo,
|
|
274
|
+
diagnostics: {
|
|
275
|
+
method: "direct_failed",
|
|
276
|
+
error: directResult.reason,
|
|
277
|
+
message: `Failed to apply hunk: ${directResult.reason}`,
|
|
278
|
+
needsFuzzyMatching: true
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Validates all hunks in a patch
|
|
285
|
+
* @param {string} sourceCode - Original source code
|
|
286
|
+
* @param {string[]} hunks - Array of hunk texts
|
|
287
|
+
* @returns {object[]} Array of validation results for each hunk
|
|
288
|
+
*/
|
|
289
|
+
function validateHunks(sourceCode, hunks) {
|
|
290
|
+
return hunks.map((hunkText, index) => {
|
|
291
|
+
const result = validateHunk(sourceCode, hunkText);
|
|
292
|
+
return {
|
|
293
|
+
...result,
|
|
294
|
+
hunkIndex: index
|
|
295
|
+
};
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
module.exports = {
|
|
300
|
+
parseHunk,
|
|
301
|
+
parseHunkHeader,
|
|
302
|
+
validateHunk,
|
|
303
|
+
validateHunks,
|
|
304
|
+
tryApplyHunkDirect
|
|
305
|
+
};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component: PatchUtils Index
|
|
3
|
+
* Block-UUID: a7827938-bbfd-4c4b-83a7-fcf39ede8c6e
|
|
4
|
+
* Parent-UUID: f4a8495e-5612-40b5-8ec3-12c8e06886d5
|
|
5
|
+
* Version: 2.0.0
|
|
6
|
+
* Description: Aggregates and exports public functions from the enhanced PatchUtils modules.
|
|
7
|
+
* Language: JavaScript
|
|
8
|
+
* Created-at: 2025-05-14T16:55:00.000Z
|
|
9
|
+
* Authors: Gemini 2.5 Pro (v1.0.0), Claude 3.7 Sonnet (v2.0.0)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
// Import original modules (for backward compatibility)
|
|
14
|
+
const parser = require('./patchParser');
|
|
15
|
+
const processor = require('./patchProcessor');
|
|
16
|
+
const headerFormatter = require('./patchHeaderFormatter');
|
|
17
|
+
const verifier = require('./patchVerifier');
|
|
18
|
+
|
|
19
|
+
// Import new enhanced modules
|
|
20
|
+
const hunkValidator = require('./hunkValidator');
|
|
21
|
+
const fuzzyMatcher = require('./fuzzyMatcher');
|
|
22
|
+
const hunkCorrector = require('./hunkCorrector');
|
|
23
|
+
const diagnosticReporter = require('./diagnosticReporter');
|
|
24
|
+
const patchExtractor = require('./patchExtractor');
|
|
25
|
+
const enhancedProcessor = require('./enhancedPatchProcessor');
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @typedef {object} PatchValidationResult
|
|
29
|
+
* @property {boolean} valid - Whether the patch is valid
|
|
30
|
+
* @property {boolean} fixable - Whether invalid hunks can be fixed
|
|
31
|
+
* @property {object[]} hunkResults - Detailed results for each hunk
|
|
32
|
+
* @property {string} humanReadableDiagnostics - Human-readable diagnostic report
|
|
33
|
+
* @property {object} llmFeedback - Structured feedback for LLMs
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @typedef {object} PatchApplicationResult
|
|
38
|
+
* @property {boolean} success - Whether the patch was applied successfully
|
|
39
|
+
* @property {string} patchedText - The resulting text after applying the patch
|
|
40
|
+
* @property {boolean} patchWasCorrected - Whether the patch needed correction
|
|
41
|
+
* @property {string} correctedPatch - The corrected patch if applicable
|
|
42
|
+
* @property {object[]} hunkResults - Detailed results for each hunk
|
|
43
|
+
* @property {string} humanReadableDiagnostics - Human-readable diagnostic report
|
|
44
|
+
* @property {object} llmFeedback - Structured feedback for LLMs
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @typedef {object} HunkValidationResult
|
|
49
|
+
* @property {boolean} valid - Whether the hunk is valid
|
|
50
|
+
* @property {object} hunk - Parsed hunk information
|
|
51
|
+
* @property {object} headerInfo - Parsed header information
|
|
52
|
+
* @property {object} diagnostics - Detailed diagnostic information
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
// Export all modules
|
|
56
|
+
module.exports = {
|
|
57
|
+
// Original exports (for backward compatibility)
|
|
58
|
+
validateAndParseContextPatch: parser.validateAndParseContextPatch,
|
|
59
|
+
determinePatchFormat: parser.determinePatchFormat,
|
|
60
|
+
extractPatchMetadata: parser.extractPatchMetadata,
|
|
61
|
+
validatePatchMetadata: parser.validatePatchMetadata,
|
|
62
|
+
extractPatchContent: parser.extractPatchContent,
|
|
63
|
+
extractContextPatches: parser.extractContextPatches,
|
|
64
|
+
isPatchBlock: parser.isPatchBlock,
|
|
65
|
+
detectPatch: parser.detectPatch,
|
|
66
|
+
findAllPatches: parser.findAllPatches,
|
|
67
|
+
applyPatch: processor.applyPatch,
|
|
68
|
+
createPatch: processor.createPatch,
|
|
69
|
+
convertContextPatchToDiff: processor.convertContextPatchToDiff,
|
|
70
|
+
convertDiffToContextPatch: processor.convertDiffToContextPatch,
|
|
71
|
+
/**
|
|
72
|
+
* Creates a patch between two full code block strings (including their metadata headers).
|
|
73
|
+
* @param {string} sourceCodeBlockText - The complete string of the original code block.
|
|
74
|
+
* @param {string} targetCodeBlockText - The complete string of the modified code block.
|
|
75
|
+
* @param {object} patchMetadata - Metadata for the patch header.
|
|
76
|
+
* @returns {string} Patch in unified diff format.
|
|
77
|
+
*/
|
|
78
|
+
createPatchFromCodeBlocks: processor.createPatchFromCodeBlocks,
|
|
79
|
+
formatCodeBlockHeader: headerFormatter.formatCodeBlockHeader,
|
|
80
|
+
verifyAndCorrectLineNumbers: verifier.verifyAndCorrectLineNumbers,
|
|
81
|
+
|
|
82
|
+
// New enhanced exports
|
|
83
|
+
// Hunk validation
|
|
84
|
+
parseHunk: hunkValidator.parseHunk,
|
|
85
|
+
parseHunkHeader: hunkValidator.parseHunkHeader,
|
|
86
|
+
validateHunk: hunkValidator.validateHunk,
|
|
87
|
+
validateHunks: hunkValidator.validateHunks,
|
|
88
|
+
|
|
89
|
+
// Fuzzy matching
|
|
90
|
+
findBestContextMatch: fuzzyMatcher.findBestContextMatch,
|
|
91
|
+
findExactLineMatches: fuzzyMatcher.findExactLineMatches,
|
|
92
|
+
findBestMatchWithSlidingWindow: fuzzyMatcher.findBestMatchWithSlidingWindow,
|
|
93
|
+
|
|
94
|
+
// Hunk correction
|
|
95
|
+
recalculateHunkHeader: hunkCorrector.recalculateHunkHeader,
|
|
96
|
+
reconstructHunk: hunkCorrector.reconstructHunk,
|
|
97
|
+
generateCorrectedHunk: hunkCorrector.generateCorrectedHunk,
|
|
98
|
+
generateHunkCorrection: hunkCorrector.generateHunkCorrection,
|
|
99
|
+
|
|
100
|
+
// Diagnostic reporting
|
|
101
|
+
generateHumanReadableDiagnostics: diagnosticReporter.generateHumanReadableDiagnostics,
|
|
102
|
+
generateLLMFeedback: diagnosticReporter.generateLLMFeedback,
|
|
103
|
+
formatDiagnosticSummary: diagnosticReporter.formatDiagnosticSummary,
|
|
104
|
+
|
|
105
|
+
// Patch extraction
|
|
106
|
+
extractHunks: patchExtractor.extractHunks,
|
|
107
|
+
cleanHunkLineNumbers: patchExtractor.cleanHunkLineNumbers,
|
|
108
|
+
extractAndCleanHunks: patchExtractor.extractAndCleanHunks,
|
|
109
|
+
reconstructPatch: patchExtractor.reconstructPatch,
|
|
110
|
+
|
|
111
|
+
// Enhanced patch processing
|
|
112
|
+
/**
|
|
113
|
+
* Applies a patch with detailed diagnostics
|
|
114
|
+
* @param {string} sourceText - Original source code
|
|
115
|
+
* @param {string} patchText - Patch text
|
|
116
|
+
* @returns {PatchApplicationResult} Comprehensive result with diagnostics
|
|
117
|
+
*/
|
|
118
|
+
applyPatchWithDiagnostics: enhancedProcessor.applyPatchWithDiagnostics,
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Validates a patch without applying it
|
|
122
|
+
* @param {string} sourceText - Original source code
|
|
123
|
+
* @param {string} patchText - Patch text
|
|
124
|
+
* @returns {PatchValidationResult} Validation result with diagnostics
|
|
125
|
+
*/
|
|
126
|
+
validatePatch: enhancedProcessor.validatePatch,
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Applies a single hunk to source code
|
|
130
|
+
* @param {string} sourceText - Original source code
|
|
131
|
+
* @param {string} hunkText - Text of a single hunk
|
|
132
|
+
* @returns {object} Result of applying the hunk
|
|
133
|
+
*/
|
|
134
|
+
applyHunk: enhancedProcessor.applyHunk
|
|
135
|
+
};
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component: PatchUtils Patch Extractor
|
|
3
|
+
* Block-UUID: ad8cda91-244b-4f47-9847-d6c0d93424fc
|
|
4
|
+
* Parent-UUID: N/A
|
|
5
|
+
* Version: 1.0.0
|
|
6
|
+
* Description: Extracts individual hunks from a patch and cleans line numbers from content.
|
|
7
|
+
* Language: JavaScript
|
|
8
|
+
* Created-at: 2025-05-14T16:50:00.000Z
|
|
9
|
+
* Authors: Claude 3.7 Sonnet (v1.0.0)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
const { removeLineNumbers } = require('../CodeBlockUtils/lineNumberFormatter');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Extracts individual hunks from a patch
|
|
17
|
+
* @param {string} patchText - Full patch text including metadata and markers
|
|
18
|
+
* @returns {object} Object containing metadata and array of hunk texts
|
|
19
|
+
*/
|
|
20
|
+
function extractHunks(patchText) {
|
|
21
|
+
if (!patchText) {
|
|
22
|
+
return { metadata: {}, hunks: [] };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const lines = patchText.split('\n');
|
|
26
|
+
const metadata = {};
|
|
27
|
+
const hunks = [];
|
|
28
|
+
|
|
29
|
+
let inMetadata = true;
|
|
30
|
+
let inHunk = false;
|
|
31
|
+
let currentHunk = [];
|
|
32
|
+
let inPatchContent = false;
|
|
33
|
+
|
|
34
|
+
for (let i = 0; i < lines.length; i++) {
|
|
35
|
+
const line = lines[i];
|
|
36
|
+
|
|
37
|
+
// Check for patch start marker
|
|
38
|
+
if (line === '# --- PATCH START MARKER ---') {
|
|
39
|
+
inMetadata = false;
|
|
40
|
+
inPatchContent = true;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Check for patch end marker
|
|
45
|
+
if (line === '# --- PATCH END MARKER ---') {
|
|
46
|
+
// Save the last hunk if we were in one
|
|
47
|
+
if (inHunk && currentHunk.length > 0) {
|
|
48
|
+
hunks.push(currentHunk.join('\n'));
|
|
49
|
+
currentHunk = [];
|
|
50
|
+
}
|
|
51
|
+
inPatchContent = false;
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Process metadata
|
|
56
|
+
if (inMetadata) {
|
|
57
|
+
if (line.startsWith('# ') && line !== '# Patch Metadata') {
|
|
58
|
+
const colonIndex = line.indexOf(':');
|
|
59
|
+
if (colonIndex > 0) {
|
|
60
|
+
const key = line.substring(2, colonIndex).trim();
|
|
61
|
+
const value = line.substring(colonIndex + 1).trim();
|
|
62
|
+
metadata[key] = value;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Process patch content
|
|
69
|
+
if (inPatchContent) {
|
|
70
|
+
// Check for hunk header
|
|
71
|
+
if (line.startsWith('@@ ')) {
|
|
72
|
+
// If we were already in a hunk, save it before starting a new one
|
|
73
|
+
if (inHunk && currentHunk.length > 0) {
|
|
74
|
+
hunks.push(currentHunk.join('\n'));
|
|
75
|
+
currentHunk = [];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
inHunk = true;
|
|
79
|
+
currentHunk.push(line);
|
|
80
|
+
} else if (inHunk) {
|
|
81
|
+
// Add line to current hunk
|
|
82
|
+
currentHunk.push(line);
|
|
83
|
+
}
|
|
84
|
+
// Skip lines between hunks (like file headers)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Add the last hunk if there is one
|
|
89
|
+
if (inHunk && currentHunk.length > 0) {
|
|
90
|
+
hunks.push(currentHunk.join('\n'));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return { metadata, hunks };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Cleans line numbers from hunk content
|
|
98
|
+
* @param {string} hunkText - Text of a single hunk
|
|
99
|
+
* @returns {string} Hunk with line numbers removed from content lines
|
|
100
|
+
*/
|
|
101
|
+
function cleanHunkLineNumbers(hunkText) {
|
|
102
|
+
if (!hunkText) {
|
|
103
|
+
return '';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const lines = hunkText.split('\n');
|
|
107
|
+
const cleanedLines = [];
|
|
108
|
+
|
|
109
|
+
// First line is always the hunk header
|
|
110
|
+
if (lines.length > 0) {
|
|
111
|
+
cleanedLines.push(lines[0]);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Process content lines
|
|
115
|
+
for (let i = 1; i < lines.length; i++) {
|
|
116
|
+
const line = lines[i];
|
|
117
|
+
|
|
118
|
+
if (line.startsWith(' ') || line.startsWith('+') || line.startsWith('-')) {
|
|
119
|
+
const prefix = line[0];
|
|
120
|
+
const content = removeLineNumbers(line.substring(1));
|
|
121
|
+
cleanedLines.push(`${prefix}${content}`);
|
|
122
|
+
} else {
|
|
123
|
+
// Non-content line, keep as is
|
|
124
|
+
cleanedLines.push(line);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return cleanedLines.join('\n');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Extracts and cleans all hunks from a patch
|
|
133
|
+
* @param {string} patchText - Full patch text
|
|
134
|
+
* @returns {object} Object with metadata and cleaned hunks
|
|
135
|
+
*/
|
|
136
|
+
function extractAndCleanHunks(patchText) {
|
|
137
|
+
const { metadata, hunks } = extractHunks(patchText);
|
|
138
|
+
const cleanedHunks = hunks.map(cleanHunkLineNumbers);
|
|
139
|
+
|
|
140
|
+
return { metadata, hunks: cleanedHunks };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Reconstructs a patch with cleaned hunks
|
|
145
|
+
* @param {object} metadata - Patch metadata
|
|
146
|
+
* @param {string[]} hunks - Array of hunk texts
|
|
147
|
+
* @returns {string} Reconstructed patch
|
|
148
|
+
*/
|
|
149
|
+
function reconstructPatch(metadata, hunks) {
|
|
150
|
+
// Format metadata
|
|
151
|
+
let result = '# Patch Metadata\n';
|
|
152
|
+
|
|
153
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
154
|
+
result += `# ${key}: ${value}\n`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Add patch markers and hunks
|
|
158
|
+
result += '\n# --- PATCH START MARKER ---\n';
|
|
159
|
+
result += '--- Original\n';
|
|
160
|
+
result += '+++ Modified\n';
|
|
161
|
+
|
|
162
|
+
// Add hunks with a blank line between them
|
|
163
|
+
result += hunks.join('\n\n');
|
|
164
|
+
|
|
165
|
+
result += '\n# --- PATCH END MARKER ---';
|
|
166
|
+
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
module.exports = {
|
|
171
|
+
extractHunks,
|
|
172
|
+
cleanHunkLineNumbers,
|
|
173
|
+
extractAndCleanHunks,
|
|
174
|
+
reconstructPatch
|
|
175
|
+
};
|