PRIX / lib /src /utils /patch-utils.js
yamxxx1's picture
Upload 139 files
7da7e2c verified
Raw
History Blame
6.46 kB
"use strict";
/**
* Patch parsing utilities
* Extracted from review.ts to improve modularity
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractPatternType = exports.parseReview = exports.shouldIgnoreContent = exports.shouldIncludeFile = exports.checkIfDocumentationOnly = exports.parsePatch = exports.patchStartEndLine = exports.splitPatch = exports.validateRemedyAST = void 0;
const ts_morph_1 = require("ts-morph");
/**
* Validate remedy code AST
* Ensures the remedy doesn't create invalid TypeScript/JS
*/
const validateRemedyAST = (filename, remedy, project) => {
if (!filename.endsWith('.ts') && !filename.endsWith('.js'))
return true;
try {
const sf = project.createSourceFile(`test_${Date.now()}.ts`, remedy, {
overwrite: true
});
// Check for basic validity - no unexpected class declarations in remedy
return !sf.getDescendantsOfKind(ts_morph_1.SyntaxKind.ClassDeclaration).length;
}
catch (e) {
return false;
}
};
exports.validateRemedyAST = validateRemedyAST;
/**
* Split a patch into individual hunks
*/
const splitPatch = (patch) => {
if (patch == null)
return [];
const pattern = /(^@@ -(\d+),(\d+) \+(\d+),(\d+) @@).*$/gm;
const result = [];
let match = pattern.exec(patch);
if (match != null) {
const first = match.index + match[0].length;
let last = first;
while ((match = pattern.exec(patch)) !== null) {
result.push(patch.substring(last, match.index).trim());
last = match.index + match[0].length;
}
result.push(patch.substring(last).trim());
}
return result;
};
exports.splitPatch = splitPatch;
/**
* Extract start/end line numbers from patch header
* Exported for use by review.ts
*/
const patchStartEndLine = (patch) => {
const pattern = /(^@@ -(\d+),(\d+) \+(\d+),(\d+) @@).*$/gm;
const match = pattern.exec(patch);
if (match != null) {
const oldStart = parseInt(match[2]);
const oldLength = parseInt(match[3]);
const newStart = parseInt(match[4]);
const newLength = parseInt(match[5]);
return {
oldStart,
oldLength,
newStart,
newLength,
oldHunk: { startLine: oldStart, endLine: oldStart + oldLength - 1 },
newHunk: { startLine: newStart, endLine: newStart + newLength - 1 }
};
}
return null;
};
exports.patchStartEndLine = patchStartEndLine;
/**
* Parse a patch into old and new hunks
*/
const parsePatch = (patch) => {
const hunkInfo = (0, exports.patchStartEndLine)(patch);
if (hunkInfo == null)
return null;
const oldHunkLines = [];
const newHunkLines = [];
const lines = patch.split('\n');
for (const line of lines) {
if (line.startsWith('-')) {
oldHunkLines.push(line.substring(1));
}
else if (line.startsWith('+')) {
newHunkLines.push(line.substring(1));
}
else if (!line.startsWith('@@')) {
oldHunkLines.push(line);
newHunkLines.push(line);
}
}
return {
oldHunk: oldHunkLines.join('\n'),
newHunk: newHunkLines.join('\n'),
oldStart: hunkInfo.oldStart,
newStart: hunkInfo.newStart
};
};
exports.parsePatch = parsePatch;
/**
* Check if a patch contains only documentation changes (comments)
*/
const checkIfDocumentationOnly = (diff) => {
const content = diff
.split('\n')
.filter(l => l.startsWith('+') || l.startsWith('-'))
.map(l => l.substring(1).trim())
.join('');
return content.length > 0 && !/[a-zA-Z0-9]/.test(content.replace(/\/\/|\/\*|\*|#|"""|'''/g, ''));
};
exports.checkIfDocumentationOnly = checkIfDocumentationOnly;
/**
* Check if a file should be included based on path filters
*/
const shouldIncludeFile = (filename, pathFilters) => {
if (pathFilters == null)
return true;
return pathFilters.check(filename);
};
exports.shouldIncludeFile = shouldIncludeFile;
/**
* Check if content contains the ignore keyword
*/
const shouldIgnoreContent = (content, ignoreKeyword) => {
return content.includes(ignoreKeyword);
};
exports.shouldIgnoreContent = shouldIgnoreContent;
const parseReview = (response, patches, filename, debug = false) => {
const reviews = [];
// Match patterns like:
// [LINE_START-LINE_END] | [SEVERITY] | [TYPE] message
// or
// [LINE] | [SEVERITY] message
const pattern = /\[(\d+)(-(\d+))?\]\s*\|\s*(critical|major|minor|info)?\s*\|\s*(\w+)?\s*[:|-]?\s*(.+?)(?=\[(\d+)|$)/gis;
let match;
while ((match = pattern.exec(response)) !== null) {
const startLine = parseInt(match[1]);
const endLine = match[2] ? parseInt(match[2]) : startLine;
const severity = (match[4] || 'minor');
const type = match[5] || 'general';
const message = match[6].trim();
// Validate line numbers against patch
const validLine = patches.some((p) => startLine >= p.newStart && startLine <= p.newStart + p.newLength);
if (validLine) {
reviews.push({
startLine,
endLine,
comment: `[${severity.toUpperCase()}] | ${type.toUpperCase()}\n\n${message}`,
severity,
confidence: 80
});
}
}
// Also look for remedy suggestions in code blocks
const remedyPattern = /```(?:\w+)?\s*\n([\s\S]*?)```/g;
let remedyMatch;
while ((remedyMatch = remedyPattern.exec(response)) !== null) {
const remedy = remedyMatch[1].trim();
// Associate remedy with last review if any
if (reviews.length > 0) {
reviews[reviews.length - 1].remedy = remedy;
}
}
return reviews;
};
exports.parseReview = parseReview;
/**
* Extract pattern type from a review message
*/
const extractPatternType = (message) => {
const patternTypes = [
'Security',
'Bug',
'Performance',
'Architecture',
'Style',
'Documentation',
'Testing',
'Refactoring'
];
for (const type of patternTypes) {
if (message.toLowerCase().includes(type.toLowerCase())) {
return type.toLowerCase();
}
}
return 'general';
};
exports.extractPatternType = extractPatternType;
//# sourceMappingURL=patch-utils.js.map