Native surfaces migration — evolve the extension onto VS Code's native review surfaces (9-task plan, spec v0.2.1) (#72)
This commit was merged in pull request #72.
This commit is contained in:
@@ -17,6 +17,7 @@ import type { DiffViewController } from "./diffViewController";
|
||||
import type { AttributionController } from "./attributionController";
|
||||
import { decorationPlan, wordEditHunks, authorAt } from "./trackChangesModel";
|
||||
import { isAuthorable } from "./workspacePath";
|
||||
import type { CoeditingRegistry } from "./coeditingRegistry";
|
||||
|
||||
export class EditorProposalController implements vscode.Disposable, vscode.CodeLensProvider {
|
||||
private readonly disposables: vscode.Disposable[] = [];
|
||||
@@ -51,13 +52,14 @@ export class EditorProposalController implements vscode.Disposable, vscode.CodeL
|
||||
private readonly proposals: ProposalController,
|
||||
private readonly diffView: DiffViewController,
|
||||
private readonly attribution: AttributionController,
|
||||
private readonly registry: CoeditingRegistry,
|
||||
) {
|
||||
this.disposables.push(
|
||||
this.insDeco.human, this.insDeco.claude, this.delDeco.human, this.delDeco.claude, this.delDeco.none, this.lensEmitter,
|
||||
vscode.languages.registerCodeLensProvider({ language: "markdown" }, this),
|
||||
this.proposals.onDidChangeProposals(({ uri }) => this.scheduleApply(uri)),
|
||||
vscode.window.onDidChangeActiveTextEditor((ed) => ed && this.renderEditor(ed)),
|
||||
// Refresh committed decorations when the baseline advances (pin / machine-landing / open).
|
||||
// Refresh committed decorations when the baseline changes (establish / pin / head-refresh).
|
||||
this.diffView.onDidChangeBaseline(({ uri }) => {
|
||||
const ed = vscode.window.visibleTextEditors.find((e) => e.document.uri.toString() === uri);
|
||||
if (ed) this.renderEditor(ed);
|
||||
@@ -67,6 +69,13 @@ export class EditorProposalController implements vscode.Disposable, vscode.CodeL
|
||||
const ed = vscode.window.activeTextEditor;
|
||||
if (ed && e.document === ed.document) this.scheduleRender(ed);
|
||||
}),
|
||||
// INV-10: re-render (and refresh CodeLens) on every gate change — exit clears
|
||||
// decorations/CodeLens via renderEditor's own gate check, re-entry redraws.
|
||||
this.registry.onDidChange(({ uri }) => {
|
||||
const ed = vscode.window.visibleTextEditors.find((e) => e.document.uri.toString() === uri);
|
||||
if (ed) this.renderEditor(ed);
|
||||
this.lensEmitter.fire();
|
||||
}),
|
||||
// the four QuickPick-backed menu commands
|
||||
vscode.commands.registerCommand("cowriting.proposalAcceptMenu", (id?: string) => this.menu("accept", id)),
|
||||
vscode.commands.registerCommand("cowriting.proposalRejectMenu", (id?: string) => this.menu("reject", id)),
|
||||
@@ -106,6 +115,9 @@ export class EditorProposalController implements vscode.Disposable, vscode.CodeL
|
||||
private async onProposalsChanged(uri: string): Promise<void> {
|
||||
const doc = vscode.workspace.textDocuments.find((d) => d.uri.toString() === uri);
|
||||
if (!doc || doc.languageId !== "markdown" || !isAuthorable(doc.uri.scheme)) return;
|
||||
// INV-10: a doc that exited coediting between the schedule and this tick gets
|
||||
// no optimistic-apply/decorations/CodeLens.
|
||||
if (!this.registry.isCoediting(doc.uri)) return;
|
||||
const key = this.proposals.keyFor(doc);
|
||||
for (const v of this.proposals.listProposals(doc)) {
|
||||
if (v.anchorStart !== null && !this.proposals.isApplied(key, v.id)) {
|
||||
@@ -125,6 +137,8 @@ export class EditorProposalController implements vscode.Disposable, vscode.CodeL
|
||||
for (const a of ["human", "claude", "none"] as const) editor.setDecorations(this.delDeco[a], []);
|
||||
};
|
||||
if (doc.languageId !== "markdown") return clearAll();
|
||||
// INV-10: a non-entered doc gets no decorations at all.
|
||||
if (!this.registry.isCoediting(doc.uri)) return clearAll();
|
||||
const key = this.proposals.keyFor(doc);
|
||||
const ins: Record<"human" | "claude", vscode.Range[]> = { human: [], claude: [] };
|
||||
const del: Record<"human" | "claude" | "none", vscode.DecorationOptions[]> = { human: [], claude: [], none: [] };
|
||||
@@ -202,18 +216,33 @@ export class EditorProposalController implements vscode.Disposable, vscode.CodeL
|
||||
}
|
||||
}
|
||||
|
||||
/** CodeLensProvider: a `Accept ▾` / `Reject ▾` pair above each applied block. */
|
||||
/**
|
||||
* CodeLensProvider (Task 8, PUC-2 wording): a `✓ Keep` / `✗ Reject` pair above
|
||||
* each applied block, plus — when ≥2 proposals are pending on the doc — a
|
||||
* top-of-file `✓ Keep all (N)` / `✗ Reject all` pair routed directly at the
|
||||
* batch commands (no QuickPick detour for the common "review is done" case).
|
||||
*/
|
||||
provideCodeLenses(document: vscode.TextDocument): vscode.CodeLens[] {
|
||||
if (document.languageId !== "markdown") return [];
|
||||
if (!this.registry.isCoediting(document.uri)) return [];
|
||||
const key = this.proposals.keyFor(document);
|
||||
const applied = this.proposals
|
||||
.listProposals(document)
|
||||
.filter((v) => v.anchorStart !== null && this.proposals.isApplied(key, v.id));
|
||||
const lenses: vscode.CodeLens[] = [];
|
||||
for (const v of this.proposals.listProposals(document)) {
|
||||
if (v.anchorStart === null || !this.proposals.isApplied(key, v.id)) continue;
|
||||
const pos = document.positionAt(v.anchorStart);
|
||||
if (applied.length >= 2) {
|
||||
const top = new vscode.Range(0, 0, 0, 0);
|
||||
lenses.push(
|
||||
new vscode.CodeLens(top, { title: `✓ Keep all (${applied.length})`, command: "cowriting.acceptAllProposals" }),
|
||||
new vscode.CodeLens(top, { title: "✗ Reject all", command: "cowriting.rejectAllProposals" }),
|
||||
);
|
||||
}
|
||||
for (const v of applied) {
|
||||
const pos = document.positionAt(v.anchorStart!);
|
||||
const line = new vscode.Range(pos.line, 0, pos.line, 0);
|
||||
lenses.push(
|
||||
new vscode.CodeLens(line, { title: "Accept ▾", command: "cowriting.proposalAcceptMenu", arguments: [v.id] }),
|
||||
new vscode.CodeLens(line, { title: "Reject ▾", command: "cowriting.proposalRejectMenu", arguments: [v.id] }),
|
||||
new vscode.CodeLens(line, { title: "✓ Keep", command: "cowriting.proposalAcceptMenu", arguments: [v.id] }),
|
||||
new vscode.CodeLens(line, { title: "✗ Reject", command: "cowriting.proposalRejectMenu", arguments: [v.id] }),
|
||||
);
|
||||
}
|
||||
return lenses;
|
||||
|
||||
Reference in New Issue
Block a user