Files
gstack/lib/cso/history.ts
T
garrytan d93d61f7ba style(cso): format lib/cso TypeScript with pinned Prettier
Mechanical reformat only. Minified transpile output is byte-identical for
21 of 22 files; witness.ts differs only in three regex flag orders
(/mi -> /im), which JavaScript canonicalizes. Source-text assertions over
lib/cso now compare whitespace-insensitively with the same tokens.
2026-09-29 14:22:48 +00:00

101 lines
2.9 KiB
TypeScript

/** Decode the two Git path tokens in a `diff --git` header. */
function token(source: string, offset: number): { value: string; next: number } | undefined {
if (source[offset] !== '"') {
const end = source.indexOf(' ', offset),
next = end < 0 ? source.length : end;
if (next === offset) return;
return { value: source.slice(offset, next), next };
}
const bytes: number[] = [];
let at = offset + 1;
const append = (value: string) => bytes.push(...new TextEncoder().encode(value));
while (at < source.length) {
const value = source[at++];
if (value === '"')
return { value: new TextDecoder('utf-8', { fatal: true }).decode(Uint8Array.from(bytes)), next: at };
if (value !== '\\') {
append(value);
continue;
}
if (at >= source.length) return;
const escaped = source[at++],
mapped: { [key: string]: string } = {
a: '\x07',
b: '\b',
f: '\f',
n: '\n',
r: '\r',
t: '\t',
v: '\v',
'\\': '\\',
'"': '"',
};
if (mapped[escaped] !== undefined) {
append(mapped[escaped]);
continue;
}
if (/[0-7]/.test(escaped) && /^[0-7]{2}/.test(source.slice(at, at + 2))) {
bytes.push(Number.parseInt(escaped + source.slice(at, at + 2), 8));
at += 2;
continue;
}
return;
}
}
export function gitDiffHeaderPaths(line: string): [string, string] | undefined {
const prefix = 'diff --git ';
if (!line.startsWith(prefix)) return;
try {
const left = token(line, prefix.length);
if (!left || line[left.next] !== ' ') return;
const right = token(line, left.next + 1);
if (!right || right.next !== line.length) return;
return [left.value, right.value];
} catch {
return;
}
}
/** Return only exact path hunks, keeping one commit preamble per matching commit. */
export function historyForPath(raw: string, path: string): string | undefined {
const expected = new Set([`a/${path}`, `b/${path}`]),
output: string[] = [],
lines = raw.split('\n');
let preamble: string[] = [],
section: string[] | undefined,
include = false,
preambleEmitted = false;
const flush = () => {
if (section && include) {
if (!preambleEmitted) {
output.push(...preamble);
preambleEmitted = true;
}
output.push(...section);
}
section = undefined;
include = false;
};
for (const line of lines) {
if (line.startsWith('commit ')) {
flush();
preamble = [line];
preambleEmitted = false;
continue;
}
if (line.startsWith('diff --git ')) {
flush();
section = [line];
const paths = gitDiffHeaderPaths(line);
include = Boolean(paths && (expected.has(paths[0]) || expected.has(paths[1])));
continue;
}
if (section) section.push(line);
else preamble.push(line);
}
flush();
while (output.at(-1) === '') output.pop();
return output.length ? output.join('\n') : undefined;
}