Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133import { diffFileName, type DiffFile, type NiceDiff, type TextFragment } from "$lib/api/diff";import { hexFromBytes } from "$lib/api/repo";
// port of appview/repo/rawdiff.go
const OP_CHARS = [" ", "-", "+"] as const;
const opChar = (op: number): string => OP_CHARS[op] ?? "?";
const noEol = (line: string): boolean => line.length === 0 || !line.endsWith("\n");
const fragmentHeader = (fragment: TextFragment): string => { const header = `@@ -${fragment.OldPosition ?? 0},${fragment.OldLines ?? 0} +${fragment.NewPosition ?? 0},${fragment.NewLines ?? 0} @@`; return fragment.Comment ? `${header} ${fragment.Comment}` : header;};
const fragmentString = (fragment: TextFragment): string => { let out = `${fragmentHeader(fragment)}\n`; for (const line of fragment.Lines ?? []) { out += opChar(line.Op) + line.Line; if (noEol(line.Line)) out += "\n\\ No newline at end of file\n"; } return out;};
export const fileStats = (file: DiffFile): { insertions: number; deletions: number } => { let insertions = 0; let deletions = 0; for (const fragment of file.text_fragments ?? []) { insertions += fragment.LinesAdded ?? 0; deletions += fragment.LinesDeleted ?? 0; } return { insertions, deletions };};
export const renderUnifiedDiff = (niceDiff: NiceDiff | null | undefined): string => { if (!niceDiff) return ""; let out = ""; for (const file of niceDiff.diff ?? []) { const oldName = file.name.old || file.name.new; const newName = file.name.new || file.name.old;
out += `diff --git a/${oldName} b/${newName}\n`; if (file.is_new) { out += "new file mode 100644\n"; out += "--- /dev/null\n"; out += `+++ b/${newName}\n`; } else if (file.is_delete) { out += "deleted file mode 100644\n"; out += `--- a/${oldName}\n`; out += "+++ /dev/null\n"; } else if (file.is_rename) { out += `rename from ${oldName}\n`; out += `rename to ${newName}\n`; out += `--- a/${oldName}\n`; out += `+++ b/${newName}\n`; } else { out += `--- a/${oldName}\n`; out += `+++ b/${newName}\n`; }
for (const fragment of file.text_fragments ?? []) { out += fragmentString(fragment); } } return out;};
const WEEKDAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] as const;const MONTHS = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] as const;
const pad = (value: number): string => String(value).padStart(2, "0");
// RFC1123Z, always utcconst formatPatchDate = (input: string): string => { const date = new Date(input); // a missing When is go's zero time, formats to this exact string if (Number.isNaN(date.getTime())) return "Mon, 01 Jan 0001 00:00:00 +0000"; return ( `${WEEKDAYS[date.getUTCDay()]}, ${pad(date.getUTCDate())} ${MONTHS[date.getUTCMonth()]} ` + `${String(date.getUTCFullYear()).padStart(4, "0")} ${pad(date.getUTCHours())}:${pad(date.getUTCMinutes())}:${pad(date.getUTCSeconds())} +0000` );};
export const renderFormatPatch = (niceDiff: NiceDiff | null | undefined): string => { if (!niceDiff) return ""; const commit = niceDiff.commit ?? {}; const message = commit.message ?? "";
// format-patch takes the first LINE as the subject, unlike splitMessage's // first paragraph const newline = message.indexOf("\n"); const subject = newline === -1 ? message : message.slice(0, newline);
const separator = message.indexOf("\n\n"); const body = separator === -1 ? "" : message.slice(separator + 2).replace(/\n+$/, "");
const hash = commit.this || hexFromBytes(commit.hash ?? []);
let out = `From ${hash} Mon Sep 17 00:00:00 2001\n`; out += `From: ${commit.author?.Name ?? ""} <${commit.author?.Email ?? ""}>\n`; out += `Date: ${formatPatchDate(commit.author?.When ?? "")}\n`; out += `Subject: [PATCH] ${subject}\n`; out += "\n"; if (body !== "") out += `${body}\n`; out += "---\n";
for (const file of niceDiff.diff ?? []) { const name = diffFileName(file); const { insertions, deletions } = fileStats(file); out += ` ${name} | ${insertions + deletions} ${"+".repeat(insertions)}${"-".repeat(deletions)}\n`; } const stat = niceDiff.stat ?? { insertions: 0, deletions: 0, files_changed: 0 }; out += ` ${stat.files_changed} file(s) changed, ${stat.insertions} insertion(s)(+), ${stat.deletions} deletion(s)(-)\n\n`;
out += renderUnifiedDiff(niceDiff); out += "\n--\ntangled.sh\n"; return out;};