Files
CodeNomad/packages/ui/src/lib/unified-diff-reverse.ts
Shantur Rathore bd9a8d9788 feat(ui): add Git Changes tab
Adds repo-wide git changes view with refresh controls and keeps right drawer shortcuts fixed while tabs scroll.
2026-02-10 21:44:08 +00:00

87 lines
2.7 KiB
TypeScript

import { applyPatch, parsePatch } from "diff"
type ParsedPatchIndex = ReturnType<typeof parsePatch>[number]
type ParsedHunk = ParsedPatchIndex["hunks"][number]
type SdkPatch = {
oldFileName: string
newFileName: string
oldHeader?: string
newHeader?: string
hunks: Array<{
oldStart: number
oldLines: number
newStart: number
newLines: number
lines: Array<string>
}>
index?: string
}
function invertPatchLine(line: string): string {
if (!line) return line
const op = line[0]
if (op === "+") return `-${line.slice(1)}`
if (op === "-") return `+${line.slice(1)}`
return line
}
function reverseParsedHunk(hunk: ParsedHunk): ParsedHunk {
return {
oldStart: hunk.newStart,
oldLines: hunk.newLines,
newStart: hunk.oldStart,
newLines: hunk.oldLines,
lines: hunk.lines.map(invertPatchLine),
linedelimiters: Array.isArray((hunk as any).linedelimiters) ? (hunk as any).linedelimiters : [],
} as ParsedHunk
}
function reverseParsedIndex(index: ParsedPatchIndex): ParsedPatchIndex {
const hunks = Array.isArray(index.hunks) ? index.hunks : []
return {
...index,
oldFileName: (index as any).newFileName,
newFileName: (index as any).oldFileName,
oldHeader: (index as any).newHeader,
newHeader: (index as any).oldHeader,
hunks: hunks.map(reverseParsedHunk),
} as ParsedPatchIndex
}
export function buildUnifiedDiffFromSdkPatch(patch: SdkPatch): string {
const oldName = patch.oldFileName || "a/file"
const newName = patch.newFileName || "b/file"
const oldHeader = patch.oldHeader ? `\t${patch.oldHeader}` : ""
const newHeader = patch.newHeader ? `\t${patch.newHeader}` : ""
const lines: string[] = []
if (patch.index) {
// jsdiff can parse arbitrary metadata lines before file headers.
lines.push(`Index: ${patch.index}`)
}
lines.push(`--- ${oldName}${oldHeader}`)
lines.push(`+++ ${newName}${newHeader}`)
for (const hunk of patch.hunks || []) {
const oldRange = hunk.oldLines === 1 ? `${hunk.oldStart}` : `${hunk.oldStart},${hunk.oldLines}`
const newRange = hunk.newLines === 1 ? `${hunk.newStart}` : `${hunk.newStart},${hunk.newLines}`
lines.push(`@@ -${oldRange} +${newRange} @@`)
for (const line of hunk.lines || []) {
lines.push(line)
}
}
return `${lines.join("\n")}\n`
}
export function tryReverseApplyUnifiedDiff(afterText: string, diffText: string): string | null {
const normalized = (diffText ?? "").trim()
if (!normalized) return null
const parsed = parsePatch(diffText)
if (!Array.isArray(parsed) || parsed.length === 0) return null
const reversed = reverseParsedIndex(parsed[0])
const result = applyPatch(afterText ?? "", reversed)
return typeof result === "string" ? result : null
}