import type { Files } from "../files.ts"; import { Doc } from "./doc.ts"; /** One `Files` instance's open docs: a path's `Doc`, and whether a write to it came from `files` itself. */ interface Entry { readonly doc: Doc; applyingExternal: boolean; } export interface FileDocs { /** The shared `Doc` for `path`, created and seeded from `files.read(path)` on first use. */ open(path: string): Doc; } const registry = new WeakMap>(); /** * The Loro docs behind a `Files` instance (D16), one per path, shared by every panel open on that * path. Memoized per `Files` in a `WeakMap` so unrelated `Files` instances (tests, other projects) * never share state. * * Keeps each doc and `files` in sync without an echo loop: a local edit (the doc changing while * `applyingExternal` is false) writes `files`; an external `files.write` replaces the doc's text * with no base, so Loro diffs it and cursors survive. `applyingExternal` suppresses the write that * would otherwise bounce straight back to `files`. */ export function fileDocs(files: Files): FileDocs { let docs = registry.get(files); if (!docs) { docs = new Map(); registry.set(files, docs); files.watch((path) => { const entry = docs!.get(path); if (!entry) return; const text = files.read(path); if (text === undefined || text === entry.doc.text) return; entry.applyingExternal = true; entry.doc.write(text); entry.applyingExternal = false; }); } return { open(path) { let entry = docs!.get(path); if (!entry) { const doc = Doc.create(files.read(path) ?? ""); entry = { doc, applyingExternal: false }; docs!.set(path, entry); doc.onChange(() => { if (entry!.applyingExternal) return; const text = entry!.doc.text; if (files.read(path) !== text) files.write(path, text); }); } return entry.doc; }, }; }