repositories / pi-ext
pi-ext
bugabingas pi extensions
owned by admin
extensions/strata/draft.ts
Rawimport type { Draft, Finding, Hunk, Snapshot } from "./types.js";
type CarriedDraft = {
draft: Draft;
orphaned: Finding[];
};
export function carryDraft(
previous: Snapshot,
next: Snapshot,
draft: Draft,
): CarriedDraft {
const previousByKey = uniqueHunks(previous.hunks);
const nextByKey = uniqueHunks(next.hunks);
const previousById = new Map(previous.hunks.map((hunk) => [hunk.id, hunk]));
const mapped = new Map<string, Hunk>();
for (const [key, hunk] of previousByKey) {
const target = nextByKey.get(key);
if (hunk && target) mapped.set(hunk.id, target);
}
const reviewed = draft.reviewed.flatMap((id) => {
const target = mapped.get(id);
return target ? [target.id] : [];
});
const orphaned: Finding[] = [];
const findings = draft.findings.flatMap((finding) => {
const previousHunk = previousById.get(finding.hunkId);
const target = mapped.get(finding.hunkId);
const mappedFinding =
previousHunk && target
? mapFinding(previousHunk, target, finding)
: undefined;
if (!mappedFinding) {
orphaned.push(finding);
return [];
}
return [mappedFinding];
});
return { draft: { reviewed, findings, notes: draft.notes }, orphaned };
}
function uniqueHunks(hunks: Hunk[]): Map<string, Hunk | null> {
const unique = new Map<string, Hunk | null>();
for (const hunk of hunks) {
const header = /^@@ -\d+(?:,\d+)? \+\d+(?:,\d+)? @@(.*)$/u.exec(
hunk.header,
);
const key = JSON.stringify([
hunk.path,
hunk.oldPath,
header?.[1] ?? hunk.header,
hunk.lines.map((line) => [
line.kind,
line.text,
line.oldLine !== undefined,
line.newLine !== undefined,
]),
]);
unique.set(key, unique.has(key) ? null : hunk);
}
return unique;
}
function mapFinding(
previous: Hunk,
next: Hunk,
finding: Finding,
): Finding | undefined {
const from = sideLines(previous, finding.side);
const to = sideLines(next, finding.side);
const endLine = finding.endLine ?? finding.line;
const start = from.indexOf(finding.line);
const end = from.indexOf(endLine);
if (start < 0 || end < start || end - start !== endLine - finding.line)
return;
const targetStart = to[start];
const targetEnd = to[end];
if (targetStart === undefined || targetEnd === undefined) return;
if (targetEnd - targetStart !== endLine - finding.line) return;
return {
...finding,
hunkId: next.id,
line: targetStart,
...(finding.endLine === undefined ? {} : { endLine: targetEnd }),
};
}
function sideLines(hunk: Hunk, side: Finding["side"]): number[] {
return hunk.lines.flatMap((line) => {
const number = side === "old" ? line.oldLine : line.newLine;
return number === undefined ? [] : [number];
});
}