mirror of
https://github.com/backnotprop/plannotator.git
synced 2026-09-14 14:17:26 +08:00
3245310aa8
* feat(review): add optional CallDiff call-flow analysis * fix(review): harden CallDiff integration
80 lines
2.3 KiB
TypeScript
80 lines
2.3 KiB
TypeScript
/**
|
|
* Extract line content from a unified diff patch by line number range and side.
|
|
*/
|
|
export function extractLinesFromPatch(
|
|
patch: string,
|
|
lineStart: number,
|
|
lineEnd: number,
|
|
side: 'old' | 'new'
|
|
): string {
|
|
const lines = patch.split('\n');
|
|
const result: string[] = [];
|
|
|
|
let oldLine = 0;
|
|
let newLine = 0;
|
|
|
|
for (const line of lines) {
|
|
// Parse hunk header: @@ -oldStart,oldCount +newStart,newCount @@
|
|
const hunkMatch = line.match(/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
|
|
if (hunkMatch) {
|
|
oldLine = parseInt(hunkMatch[1], 10) - 1;
|
|
newLine = parseInt(hunkMatch[2], 10) - 1;
|
|
continue;
|
|
}
|
|
|
|
// Skip diff headers
|
|
if (line.startsWith('diff ') || line.startsWith('index ') ||
|
|
line.startsWith('--- ') || line.startsWith('+++ ')) {
|
|
continue;
|
|
}
|
|
|
|
const prefix = line[0];
|
|
const content = line.substring(1);
|
|
|
|
if (prefix === ' ') {
|
|
// Context line — exists on both sides
|
|
oldLine++;
|
|
newLine++;
|
|
const lineNum = side === 'old' ? oldLine : newLine;
|
|
if (lineNum >= lineStart && lineNum <= lineEnd) {
|
|
result.push(content);
|
|
}
|
|
} else if (prefix === '-') {
|
|
// Deletion — old side only
|
|
oldLine++;
|
|
if (side === 'old' && oldLine >= lineStart && oldLine <= lineEnd) {
|
|
result.push(content);
|
|
}
|
|
} else if (prefix === '+') {
|
|
// Addition — new side only
|
|
newLine++;
|
|
if (side === 'new' && newLine >= lineStart && newLine <= lineEnd) {
|
|
result.push(content);
|
|
}
|
|
}
|
|
}
|
|
|
|
return result.join('\n');
|
|
}
|
|
|
|
/** Return true only when the complete source range is represented by one diff hunk. */
|
|
export function isLineRangeInPatch(
|
|
patch: string,
|
|
lineStart: number,
|
|
lineEnd: number,
|
|
side: 'old' | 'new',
|
|
): boolean {
|
|
if (!Number.isInteger(lineStart) || !Number.isInteger(lineEnd) || lineStart < 1 || lineEnd < lineStart) {
|
|
return false;
|
|
}
|
|
for (const line of patch.split('\n')) {
|
|
const match = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/.exec(line);
|
|
if (!match) continue;
|
|
const start = Number(side === 'old' ? match[1] : match[3]);
|
|
const count = Number((side === 'old' ? match[2] : match[4]) ?? '1');
|
|
if (count === 0) continue;
|
|
if (lineStart >= start && lineEnd <= start + count - 1) return true;
|
|
}
|
|
return false;
|
|
}
|