// The `oxy:` URI scheme serves file content at a state through `oxy cat` // (spec ยง3): oxy:/?root=&change= // where `change` is `@base` (the working copy's base) or anything // `oxy cat -r` accepts. import * as vscode from "vscode"; import { Oxy } from "./oxy"; export const SCHEME = "oxy"; export function oxyUri(root: string, path: string, change: string): vscode.Uri { const query = new URLSearchParams({ root, change }).toString(); return vscode.Uri.from({ scheme: SCHEME, path: "/" + path, query }); } export function baseUri(root: string, path: string): vscode.Uri { return oxyUri(root, path, "@base"); } export function parseOxyUri(uri: vscode.Uri): { root: string; path: string; change: string } | undefined { if (uri.scheme !== SCHEME) { return undefined; } const q = new URLSearchParams(uri.query); const root = q.get("root"); const change = q.get("change") ?? "@base"; if (!root) { return undefined; } return { root, path: uri.path.replace(/^\//, ""), change }; } export class OxyContentProvider implements vscode.TextDocumentContentProvider { private readonly emitter = new vscode.EventEmitter(); readonly onDidChange = this.emitter.event; constructor(private readonly oxy: Oxy) {} /** Tell open `oxy:` documents under `root` to reload (after an operation). */ invalidate(root: string): void { for (const doc of vscode.workspace.textDocuments) { const parsed = parseOxyUri(doc.uri); if (parsed && parsed.root === root && parsed.change === "@base") { this.emitter.fire(doc.uri); } } } async provideTextDocumentContent(uri: vscode.Uri): Promise { const parsed = parseOxyUri(uri); if (!parsed) { return ""; } try { const bytes = await this.oxy.cat(parsed.root, parsed.path, parsed.change === "@base" ? undefined : parsed.change); return bytes.toString("utf8"); } catch { // A path absent from that state (an added file's base) is empty. return ""; } } }