import * as vscode from "vscode";
import type * as Y from "yjs";
import type { RoomClient } from "./room-client";
const LOCAL = "vscode-local";
/**
* Two-way binding between one VS Code TextDocument and its Y.Text. Local edits become Yjs ops; remote
* ops become WorkspaceEdits. If an interleaving ever leaves the two out of step, the document is
* re-synced from the shared text, which is the source of truth.
*/
export class DocumentBinding implements vscode.Disposable {
private applying = 0;
private readonly observer: (event: Y.YTextEvent, transaction: Y.Transaction) => void;
private chain: Promise<void> = Promise.resolve();
private pending = false;
/** Remote deltas already in the Y.Text but not yet in the document; local offsets are mapped through them. */
private unapplied: Array<Y.YTextEvent["delta"]> = [];
private readonly text: Y.Text;
constructor(
private readonly room: RoomClient,
readonly document: vscode.TextDocument,
readonly path: string,
) {
this.text = room.text(path);
this.observer = (event, transaction) => {
if (transaction.origin === LOCAL) return;
// The room evicted this clean file to free capacity; keep the editor as is and reseed it from head.
if (!room.files.has(path)) {
room.control({ type: "open", path });
return;
}
this.unapplied.push(event.delta);
this.queueRemote();
};
this.text.observe(this.observer);
if (document.getText() !== this.text.toString()) this.queueRemote();
}