447a1170ce
Task 2 of the native-surfaces migration plan. GitBaselineAdapter resolves a
document's HEAD blob via the built-in vscode.git extension (hardened with a
.git/logs/HEAD watch that nudges repo.status(), since the git extension's own
watcher doesn't reliably notice out-of-band commits on non-workspace-folder
repos in the E2E host). DiffViewController is reworked into a baseline router:
head mode (git-tracked, never persisted, re-read on commit) vs snapshot mode
(captured on CoeditingRegistry entry, re-pinned by "Mark Changes as Reviewed"
— renamed from cowriting.pinDiffBaseline, D14). The shipped machine-landing
baseline advance (#48/INV-18) is retired: a landed Claude edit now stays a
visible change-since-baseline until commit or review (INV-7/D21). Legacy
on-disk reasons ("opened"/"machine-landing") migrate to "entered" via
BaselineStore.normalizeReason on load.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
62 lines
2.7 KiB
TypeScript
62 lines
2.7 KiB
TypeScript
/**
|
|
* BaselineStore — load/save one diff-view baseline JSON per document (F6 §6.3).
|
|
* The CoauthorStore shape (src/store.ts): vscode-free (Node fs only, unit-
|
|
* testable), one file per storage key. INV-19: the storage dir is VS Code's
|
|
* per-extension GLOBAL storage, NEVER the repo — so the baseline can never be
|
|
* committed, merged, or read by another rung. F6 works on *any* file, so the
|
|
* key is a hash of the document URI (the controller derives it), not a
|
|
* workspace-relative path; an untitled buffer has no durable identity and is
|
|
* never persisted here (in-memory only — see DiffViewController).
|
|
*
|
|
* INV-7 (native-surfaces migration, spec §6.4): the baseline is now either the
|
|
* git-HEAD blob (never persisted — see GitBaselineAdapter/DiffViewController)
|
|
* or a snapshot captured on coediting-entry / re-pinned by "Mark Changes as
|
|
* Reviewed". `"opened"`/`"machine-landing"` are legacy reasons from the
|
|
* shipped #17/#48 baseline; `normalizeReason` migrates them on load so old
|
|
* on-disk baselines keep round-tripping under the new vocabulary.
|
|
*/
|
|
import * as fs from "node:fs";
|
|
import * as path from "node:path";
|
|
|
|
export type BaselineReason = "entered" | "pinned" | "head";
|
|
type LegacyBaselineReason = BaselineReason | "opened" | "machine-landing";
|
|
|
|
export interface Baseline {
|
|
/** the document URI (the identity the key is derived from) — for round-trip/debug. */
|
|
uri: string;
|
|
/** full document text captured at the epoch (from the buffer, not disk — §6.3). */
|
|
text: string;
|
|
capturedAt: string;
|
|
reason: BaselineReason;
|
|
}
|
|
|
|
/** Migrate legacy on-disk reasons to the current vocabulary (§6.4 INV-7). */
|
|
export function normalizeReason(reason: LegacyBaselineReason): BaselineReason {
|
|
if (reason === "opened" || reason === "machine-landing") return "entered";
|
|
return reason;
|
|
}
|
|
|
|
export class BaselineStore {
|
|
/** @param storageDir absolute VS Code GLOBAL-storage dir (context.globalStorageUri.fsPath). */
|
|
constructor(private readonly storageDir: string) {}
|
|
|
|
/** `<storageDir>/baselines/<key>.json` — `key` is a filesystem-safe hash (§6.3). */
|
|
baselinePath(key: string): string {
|
|
return path.join(this.storageDir, "baselines", `${key}.json`);
|
|
}
|
|
|
|
load(key: string): Baseline | null {
|
|
const p = this.baselinePath(key);
|
|
if (!fs.existsSync(p)) return null;
|
|
const raw = JSON.parse(fs.readFileSync(p, "utf8")) as Baseline;
|
|
return { ...raw, reason: normalizeReason(raw.reason) };
|
|
}
|
|
|
|
/** Newest epoch wins — overwrite in place, no history (state-not-history, the F3/F4 precedent). */
|
|
save(key: string, baseline: Baseline): void {
|
|
const p = this.baselinePath(key);
|
|
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
fs.writeFileSync(p, JSON.stringify(baseline, null, 2) + "\n", "utf8");
|
|
}
|
|
}
|