3520397e41
F10 (#29) made the rendered preview the single review surface and hid the F6 two-pane vscode.diff view (command + ctrl+alt+d set when:false). This removes that now-unreachable view code: - DiffViewController: drop toggle/findDiffTab/epochLabel/isDiffOpen, the `cowriting-baseline:` TextDocumentContentProvider + BASELINE_SCHEME + baselineUri + the content-provider change emitter, and the toggleDiffView command. The baseline DATA layer is fully intact — ensureBaseline/advance/pin/capture, getBaseline, baselineFilePath, onDidChangeBaseline, persistence (INV-19), and the machine-landing auto-advance (INV-18) that F7/F10 consume. - package.json: remove the toggleDiffView command, its commandPalette entry, and the ctrl+alt+d keybinding. - E2E: diffView suite keeps the baseline-data-layer tests, drops the two-pane view tests; the F10 + no-workspace suites assert toggleDiffView is now absent (was: declared-but-hidden). Deliberate deviation from the issue's literal acceptance: pinDiffBaseline is KEPT. The canonical Solution Design (coauthoring-interactive-review.md §6.7) scopes the removal to the two-pane VIEW only ("keep the controller + baseline store"); pin() lives in the baseline lifecycle (§6.4), never touches vscode.diff, and is exercised by live F7 baseline-reset tests. Where the P3 capture draft and the approved spec conflict, the spec wins (documentation-leads-automation). 194 unit + 49 E2E green; typecheck + build clean. No F7/F10 behavior change. Closes #34 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
154 lines
7.7 KiB
TypeScript
154 lines
7.7 KiB
TypeScript
import * as assert from "assert";
|
||
import * as fs from "fs";
|
||
import * as os from "os";
|
||
import * as path from "path";
|
||
import * as vscode from "vscode";
|
||
import type { CowritingApi } from "../../../src/extension";
|
||
|
||
const WS = process.env.E2E_WORKSPACE!;
|
||
const DOC_REL = "docs/diffview.md";
|
||
const docUri = () => vscode.Uri.file(path.join(WS, DOC_REL)).toString();
|
||
|
||
async function openDoc(): Promise<vscode.TextDocument> {
|
||
const uri = vscode.Uri.file(path.join(WS, DOC_REL));
|
||
const doc = await vscode.workspace.openTextDocument(uri);
|
||
await vscode.window.showTextDocument(doc);
|
||
return doc;
|
||
}
|
||
async function getApi(): Promise<CowritingApi> {
|
||
const ext = vscode.extensions.getExtension("benstull.vscode-cowriting-plugin")!;
|
||
const api = (await ext.activate()) as CowritingApi;
|
||
assert.ok(api?.diffViewController, "extension exports diffViewController");
|
||
return api;
|
||
}
|
||
const settle = () => new Promise((r) => setTimeout(r, 300));
|
||
|
||
// The F6 two-pane vscode.diff VIEW was removed in #34 (F10's rendered preview is
|
||
// the single review surface); only the baseline DATA layer survives, consumed by
|
||
// F7/F10. This suite covers that data layer. Order-dependent (F2–F4 pattern):
|
||
// later tests consume earlier state. Owns docs/diffview.md exclusively. The
|
||
// baseline works on ANY file (#19), so the last two tests use an out-of-workspace
|
||
// file and an untitled buffer.
|
||
suite("F6 baseline data layer (host E2E — any file, programmatic seam ingress, no LLM)", () => {
|
||
const TARGET = "A target sentence Claude will rewrite via the seam.";
|
||
const REPLACEMENT = "A SENTENCE CLAUDE REWROTE via the seam.";
|
||
|
||
test("opening a tracked doc captures an `opened` baseline equal to the buffer (INV-18)", async () => {
|
||
const doc = await openDoc();
|
||
await getApi();
|
||
const api = await getApi();
|
||
const baseline = api.diffViewController.getBaseline(docUri());
|
||
assert.ok(baseline, "baseline captured on first sight");
|
||
assert.strictEqual(baseline!.reason, "opened");
|
||
assert.strictEqual(baseline!.text, doc.getText(), "baseline = open-time buffer");
|
||
});
|
||
|
||
test("typing leaves the baseline unchanged while the buffer diverges", async () => {
|
||
const doc = await openDoc();
|
||
const api = await getApi();
|
||
const before = api.diffViewController.getBaseline(docUri())!.text;
|
||
const edit = new vscode.WorkspaceEdit();
|
||
edit.insert(doc.uri, new vscode.Position(0, 0), "OPERATOR ADDED LINE\n");
|
||
assert.ok(await vscode.workspace.applyEdit(edit), "operator edit applied");
|
||
await settle();
|
||
assert.strictEqual(api.diffViewController.getBaseline(docUri())!.text, before, "baseline unchanged by typing");
|
||
assert.notStrictEqual(doc.getText(), before, "buffer diverged");
|
||
});
|
||
|
||
test("accepting a proposal advances the baseline past the landed text (PUC-2, INV-18)", async () => {
|
||
const doc = await openDoc();
|
||
const api = await getApi();
|
||
const start = doc.getText().indexOf(TARGET);
|
||
assert.ok(start >= 0, "fixture contains the target");
|
||
const id = await vscode.commands.executeCommand<string>("cowriting.proposeAgentEdit", {
|
||
uri: doc.uri.toString(),
|
||
start,
|
||
end: start + TARGET.length,
|
||
newText: REPLACEMENT,
|
||
model: "sonnet",
|
||
sessionId: "e2e-diff",
|
||
turnId: "turn-d1",
|
||
});
|
||
assert.ok(id, "propose returns an id");
|
||
assert.ok(await api.proposalController.acceptById(DOC_REL, id!), "accept applies via the seam");
|
||
await settle();
|
||
const baseline = api.diffViewController.getBaseline(docUri())!;
|
||
assert.strictEqual(baseline.reason, "machine-landing", "baseline advanced on the landing");
|
||
assert.ok(baseline.text.includes(REPLACEMENT), "landed text is in the baseline (won't show as a change)");
|
||
assert.ok(!baseline.text.includes(TARGET), "old target gone from the baseline too");
|
||
assert.strictEqual(baseline.text, doc.getText(), "baseline == buffer right after the landing");
|
||
});
|
||
|
||
test("an operator edit after the landing makes baseline ≠ buffer (the operator delta)", async () => {
|
||
const doc = await openDoc();
|
||
const api = await getApi();
|
||
const edit = new vscode.WorkspaceEdit();
|
||
edit.insert(doc.uri, new vscode.Position(0, 0), "POST-LANDING OPERATOR LINE\n");
|
||
assert.ok(await vscode.workspace.applyEdit(edit));
|
||
await settle();
|
||
assert.notStrictEqual(
|
||
api.diffViewController.getBaseline(docUri())!.text,
|
||
doc.getText(),
|
||
"operator changes show against the advanced baseline",
|
||
);
|
||
});
|
||
|
||
test("pin resets the baseline to now: baseline == buffer, reason pinned", async () => {
|
||
const doc = await openDoc();
|
||
const api = await getApi();
|
||
await vscode.commands.executeCommand("cowriting.pinDiffBaseline");
|
||
await settle();
|
||
const baseline = api.diffViewController.getBaseline(docUri())!;
|
||
assert.strictEqual(baseline.reason, "pinned");
|
||
assert.strictEqual(baseline.text, doc.getText(), "pinned baseline == current buffer (change-marks empty)");
|
||
});
|
||
|
||
test("the baseline is persisted in GLOBAL storage, never the repo (INV-19)", async () => {
|
||
await openDoc();
|
||
const api = await getApi();
|
||
const p = api.diffViewController.baselineFilePath(docUri());
|
||
assert.ok(p, "storage-backed baseline path is available for a file: doc");
|
||
assert.ok(fs.existsSync(p!), `baseline file exists at ${p}`);
|
||
const onDisk = JSON.parse(fs.readFileSync(p!, "utf8"));
|
||
assert.strictEqual(onDisk.uri, docUri(), "baseline records the document URI");
|
||
assert.strictEqual(onDisk.reason, "pinned", "last epoch (pin) persisted");
|
||
assert.strictEqual(onDisk.text, api.diffViewController.getBaseline(docUri())!.text, "on-disk == in-memory");
|
||
// INV-19: baseline lives under the extension's storage dir, not the repo.
|
||
assert.ok(!p!.includes(`${path.sep}.threads${path.sep}`), "baseline is NOT in the sidecar tree");
|
||
assert.ok(!p!.startsWith(WS + path.sep), "baseline is NOT under the workspace folder");
|
||
assert.ok(p!.includes(`${path.sep}baselines${path.sep}`), "baseline lives under <globalStorage>/baselines/");
|
||
});
|
||
|
||
test("a baseline is captured + persisted for a file OUTSIDE the workspace folder (#19)", async () => {
|
||
const api = await getApi();
|
||
const outsideDir = fs.mkdtempSync(path.join(os.tmpdir(), "cowriting-outside-"));
|
||
const outsidePath = path.join(outsideDir, "outside.md");
|
||
fs.writeFileSync(outsidePath, "# Outside the workspace\n\nThe operator edits this too.\n", "utf8");
|
||
const outsideUri = vscode.Uri.file(outsidePath);
|
||
const doc = await vscode.workspace.openTextDocument(outsideUri);
|
||
await vscode.window.showTextDocument(doc);
|
||
await settle();
|
||
// Captured on open even though it is NOT under the workspace folder.
|
||
const baseline = api.diffViewController.getBaseline(outsideUri.toString());
|
||
assert.ok(baseline, "baseline captured for an out-of-folder file");
|
||
assert.strictEqual(baseline!.reason, "opened");
|
||
const fp = api.diffViewController.baselineFilePath(outsideUri.toString());
|
||
assert.ok(fp && fs.existsSync(fp), "outside-file baseline persisted in global storage");
|
||
assert.ok(!fp!.startsWith(WS + path.sep), "not under the workspace folder");
|
||
fs.rmSync(outsideDir, { recursive: true, force: true });
|
||
void doc;
|
||
});
|
||
|
||
test("an UNTITLED buffer gets an in-memory baseline, never persisted to disk (#19)", async () => {
|
||
const api = await getApi();
|
||
const untitled = await vscode.workspace.openTextDocument({ content: "scratch line\n", language: "markdown" });
|
||
await vscode.window.showTextDocument(untitled);
|
||
await settle();
|
||
const key = untitled.uri.toString();
|
||
assert.strictEqual(untitled.uri.scheme, "untitled", "it really is an untitled buffer");
|
||
const baseline = api.diffViewController.getBaseline(key);
|
||
assert.ok(baseline, "untitled buffer got an in-memory baseline");
|
||
assert.strictEqual(api.diffViewController.baselineFilePath(key), undefined, "untitled is NOT persisted to disk");
|
||
});
|
||
});
|