refactor(f8): controllers depend on SidecarRouter + isAuthorable (key via keyOf)
Spec §6.4: constructor store CoauthorStore→SidecarRouter, rootDir→string|undefined; isInRoot/isTracked gate → isAuthorable(scheme); per-doc key via store.keyOf(docIdentity). Seam + artifact logic unchanged. currentAuthor omits git email when no root (fail-open). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+16
-19
@@ -8,13 +8,13 @@
|
||||
* orphaned thread (INV-1).
|
||||
*/
|
||||
import * as vscode from "vscode";
|
||||
import { CoauthorStore } from "./store";
|
||||
import { SidecarRouter, docIdentity } from "./sidecarRouter";
|
||||
import { emptyArtifact, type Artifact, type Provenance } from "./model";
|
||||
import { buildFingerprint, resolve, shift, type OffsetRange } from "./anchorer";
|
||||
import { gitUserEmail } from "./identity";
|
||||
import { addThread, appendMessage, setStatus } from "./threadModel";
|
||||
import type { VersionGuard } from "./versionGuard";
|
||||
import { isUnderRoot } from "./workspacePath";
|
||||
import { isAuthorable } from "./workspacePath";
|
||||
|
||||
/** Test-facing snapshot of what is currently rendered for a document. */
|
||||
export interface RenderedThread {
|
||||
@@ -42,14 +42,14 @@ export class ThreadController implements vscode.Disposable {
|
||||
private readonly docs = new Map<string, DocState>(); // keyed by docPath
|
||||
|
||||
constructor(
|
||||
private readonly store: CoauthorStore,
|
||||
private readonly rootDir: string,
|
||||
private readonly store: SidecarRouter,
|
||||
private readonly rootDir: string | undefined,
|
||||
private readonly guard: VersionGuard,
|
||||
) {
|
||||
this.controller = vscode.comments.createCommentController("cowriting.threads", "Coauthoring Threads");
|
||||
this.controller.commentingRangeProvider = {
|
||||
provideCommentingRanges: (document) => {
|
||||
if (!this.isInRoot(document.uri)) return [];
|
||||
if (!isAuthorable(document.uri.scheme)) return [];
|
||||
return [new vscode.Range(0, 0, Math.max(0, document.lineCount - 1), 0)];
|
||||
},
|
||||
};
|
||||
@@ -69,17 +69,14 @@ export class ThreadController implements vscode.Disposable {
|
||||
);
|
||||
}
|
||||
|
||||
private isInRoot(uri: vscode.Uri): boolean {
|
||||
return uri.scheme === "file" && isUnderRoot(uri.fsPath, this.rootDir);
|
||||
}
|
||||
|
||||
private docPathOf(uri: vscode.Uri): string {
|
||||
return vscode.workspace.asRelativePath(uri, false);
|
||||
/** The single document key (F8): repo-relative path in-workspace, URI string otherwise. */
|
||||
private keyOf(document: vscode.TextDocument): string {
|
||||
return this.store.keyOf(docIdentity(document));
|
||||
}
|
||||
|
||||
private currentAuthor(): Provenance {
|
||||
const id = vscode.workspace.getConfiguration("git").get<string>("user.name") || process.env.USER || "human";
|
||||
const email = gitUserEmail(this.rootDir);
|
||||
const email = this.rootDir !== undefined ? gitUserEmail(this.rootDir) : undefined;
|
||||
return { kind: "human", id, ...(email !== undefined ? { email } : {}) };
|
||||
}
|
||||
|
||||
@@ -93,7 +90,7 @@ export class ThreadController implements vscode.Disposable {
|
||||
}
|
||||
|
||||
private ensureState(document: vscode.TextDocument): DocState {
|
||||
const docPath = this.docPathOf(document.uri);
|
||||
const docPath = this.keyOf(document);
|
||||
let state = this.docs.get(docPath);
|
||||
if (!state) {
|
||||
state = {
|
||||
@@ -113,8 +110,8 @@ export class ThreadController implements vscode.Disposable {
|
||||
|
||||
async createThreadOnSelection(firstBody = "New thread"): Promise<string | undefined> {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (!editor || editor.selection.isEmpty || !this.isInRoot(editor.document.uri)) return undefined;
|
||||
if (this.guard.isReadOnly(this.docPathOf(editor.document.uri))) return undefined;
|
||||
if (!editor || editor.selection.isEmpty || !isAuthorable(editor.document.uri.scheme)) return undefined;
|
||||
if (this.guard.isReadOnly(this.keyOf(editor.document))) return undefined;
|
||||
const document = editor.document;
|
||||
const state = this.ensureState(document);
|
||||
const offsets: OffsetRange = {
|
||||
@@ -155,7 +152,7 @@ export class ThreadController implements vscode.Disposable {
|
||||
|
||||
/** Load + (re)render every thread for a document at its resolved anchor (or orphaned). */
|
||||
renderAll(document: vscode.TextDocument): void {
|
||||
const docPath = this.docPathOf(document.uri);
|
||||
const docPath = this.keyOf(document);
|
||||
const state = this.ensureState(document);
|
||||
// fresh artifact from disk (reload / external change)
|
||||
state.artifact = this.store.load(docPath) ?? emptyArtifact(docPath);
|
||||
@@ -182,14 +179,14 @@ export class ThreadController implements vscode.Disposable {
|
||||
const p = uri.fsPath;
|
||||
for (const state of this.docs.values()) {
|
||||
if (this.store.sidecarPath(state.docPath) === p) {
|
||||
const doc = vscode.workspace.textDocuments.find((d) => this.docPathOf(d.uri) === state.docPath);
|
||||
const doc = vscode.workspace.textDocuments.find((d) => this.keyOf(d) === state.docPath);
|
||||
if (doc) this.renderAll(doc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private onDidChange(e: vscode.TextDocumentChangeEvent): void {
|
||||
const state = this.docs.get(this.docPathOf(e.document.uri));
|
||||
const state = this.docs.get(this.keyOf(e.document));
|
||||
if (!state || state.live.size === 0) return;
|
||||
for (const change of e.contentChanges) {
|
||||
const edit = { start: change.rangeOffset, end: change.rangeOffset + change.rangeLength, newLength: change.text.length };
|
||||
@@ -205,7 +202,7 @@ export class ThreadController implements vscode.Disposable {
|
||||
}
|
||||
|
||||
private onDidSave(document: vscode.TextDocument): void {
|
||||
const state = this.docs.get(this.docPathOf(document.uri));
|
||||
const state = this.docs.get(this.keyOf(document));
|
||||
if (!state || state.live.size === 0) return;
|
||||
if (this.guard.isReadOnly(state.docPath)) return;
|
||||
const text = document.getText();
|
||||
|
||||
Reference in New Issue
Block a user