| 1 |
// The `oxy:` URI scheme serves file content at a state through `oxy cat` |
| 2 |
// (spec §3): oxy:/<repository-relative path>?root=<repository root>&change=<change> |
| 3 |
// where `change` is `@base` (the working copy's base) or anything |
| 4 |
// `oxy cat -r` accepts. |
| 5 |
|
| 6 |
import * as vscode from "vscode"; |
| 7 |
import { Oxy } from "./oxy"; |
| 8 |
|
| 9 |
export const SCHEME = "oxy"; |
| 10 |
|
| 11 |
export function oxyUri(root: string, path: string, change: string): vscode.Uri { |
| 12 |
const query = new URLSearchParams({ root, change }).toString(); |
| 13 |
return vscode.Uri.from({ scheme: SCHEME, path: "/" + path, query }); |
| 14 |
} |
| 15 |
|
| 16 |
export function baseUri(root: string, path: string): vscode.Uri { |
| 17 |
return oxyUri(root, path, "@base"); |
| 18 |
} |
| 19 |
|
| 20 |
export function parseOxyUri(uri: vscode.Uri): { root: string; path: string; change: string } | undefined { |
| 21 |
if (uri.scheme !== SCHEME) { |
| 22 |
return undefined; |
| 23 |
} |
| 24 |
const q = new URLSearchParams(uri.query); |
| 25 |
const root = q.get("root"); |
| 26 |
const change = q.get("change") ?? "@base"; |
| 27 |
if (!root) { |
| 28 |
return undefined; |
| 29 |
} |
| 30 |
return { root, path: uri.path.replace(/^\//, ""), change }; |
| 31 |
} |
| 32 |
|
| 33 |
export class OxyContentProvider implements vscode.TextDocumentContentProvider { |
| 34 |
private readonly emitter = new vscode.EventEmitter<vscode.Uri>(); |
| 35 |
readonly onDidChange = this.emitter.event; |
| 36 |
|
| 37 |
constructor(private readonly oxy: Oxy) {} |
| 38 |
|
| 39 |
/** Tell open `oxy:` documents under `root` to reload (after an operation). */ |
| 40 |
invalidate(root: string): void { |
| 41 |
for (const doc of vscode.workspace.textDocuments) { |
| 42 |
const parsed = parseOxyUri(doc.uri); |
| 43 |
if (parsed && parsed.root === root && parsed.change === "@base") { |
| 44 |
this.emitter.fire(doc.uri); |
| 45 |
} |
| 46 |
} |
| 47 |
} |
| 48 |
|
| 49 |
async provideTextDocumentContent(uri: vscode.Uri): Promise<string> { |
| 50 |
const parsed = parseOxyUri(uri); |
| 51 |
if (!parsed) { |
| 52 |
return ""; |
| 53 |
} |
| 54 |
try { |
| 55 |
const bytes = await this.oxy.cat(parsed.root, parsed.path, parsed.change === "@base" ? undefined : parsed.change); |
| 56 |
return bytes.toString("utf8"); |
| 57 |
} catch { |
| 58 |
// A path absent from that state (an added file's base) is empty. |
| 59 |
return ""; |
| 60 |
} |
| 61 |
} |
| 62 |
} |