97 lines
2.9 KiB
TypeScript
97 lines
2.9 KiB
TypeScript
const HUNK_PATTERN = /(^|\n)@@/m
|
|
const FILE_MARKER_PATTERN = /(^|\n)(diff --git |--- |\+\+\+)/
|
|
const BEGIN_PATCH_PATTERN = /^\*\*\* (Begin|End) Patch/
|
|
const UPDATE_FILE_PATTERN = /^\*\*\* Update File: (.+)$/
|
|
const HUNK_HEADER_PATTERN = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/
|
|
|
|
function stripCodeFence(value: string): string {
|
|
const trimmed = value.trim()
|
|
if (!trimmed.startsWith("```")) return trimmed
|
|
const lines = trimmed.split("\n")
|
|
if (lines.length < 2) return ""
|
|
const lastLine = lines[lines.length - 1]
|
|
if (!lastLine.startsWith("```")) return trimmed
|
|
return lines.slice(1, -1).join("\n")
|
|
}
|
|
|
|
export function normalizeDiffText(raw: string): string {
|
|
if (!raw) return ""
|
|
const withoutFence = stripCodeFence(raw.replace(/\r\n/g, "\n"))
|
|
const lines = withoutFence.split("\n").map((line) => line.replace(/\s+$/u, ""))
|
|
|
|
let pendingFilePath: string | null = null
|
|
const cleanedLines: string[] = []
|
|
|
|
for (const line of lines) {
|
|
if (!line) continue
|
|
if (BEGIN_PATCH_PATTERN.test(line)) {
|
|
continue
|
|
}
|
|
const updateMatch = line.match(UPDATE_FILE_PATTERN)
|
|
if (updateMatch) {
|
|
pendingFilePath = updateMatch[1]?.trim() || null
|
|
continue
|
|
}
|
|
cleanedLines.push(line)
|
|
}
|
|
|
|
if (pendingFilePath && !FILE_MARKER_PATTERN.test(cleanedLines.join("\n"))) {
|
|
cleanedLines.unshift(`+++ b/${pendingFilePath}`)
|
|
cleanedLines.unshift(`--- a/${pendingFilePath}`)
|
|
}
|
|
|
|
return cleanedLines.join("\n").trim()
|
|
}
|
|
|
|
export function isRenderableDiffText(raw?: string | null): raw is string {
|
|
if (!raw) return false
|
|
const normalized = normalizeDiffText(raw)
|
|
if (!normalized) return false
|
|
return HUNK_PATTERN.test(normalized)
|
|
}
|
|
|
|
export function parsePatchToBeforeAfter(patch: string): { before: string; after: string } {
|
|
if (!patch || patch.trim().length === 0) {
|
|
return { before: "", after: "" }
|
|
}
|
|
|
|
const lines = patch.replace(/\r\n/g, "\n").split("\n")
|
|
const beforeLines: string[] = []
|
|
const afterLines: string[] = []
|
|
|
|
for (const line of lines) {
|
|
if (line.startsWith("---") || line.startsWith("+++") || line.startsWith("diff --git")) {
|
|
continue
|
|
}
|
|
if (HUNK_HEADER_PATTERN.test(line)) {
|
|
continue
|
|
}
|
|
if (line.startsWith("-") && !line.startsWith("---")) {
|
|
beforeLines.push(line.slice(1))
|
|
} else if (line.startsWith("+") && !line.startsWith("+++")) {
|
|
afterLines.push(line.slice(1))
|
|
} else if (line.startsWith(" ")) {
|
|
beforeLines.push(line.slice(1))
|
|
afterLines.push(line.slice(1))
|
|
} else if (line === "") {
|
|
beforeLines.push("")
|
|
afterLines.push("")
|
|
} else {
|
|
beforeLines.push(line)
|
|
afterLines.push(line)
|
|
}
|
|
}
|
|
|
|
while (beforeLines.length > 0 && beforeLines[beforeLines.length - 1] === "") {
|
|
beforeLines.pop()
|
|
}
|
|
while (afterLines.length > 0 && afterLines[afterLines.length - 1] === "") {
|
|
afterLines.pop()
|
|
}
|
|
|
|
return {
|
|
before: beforeLines.join("\n"),
|
|
after: afterLines.join("\n"),
|
|
}
|
|
}
|