import { execFile } from 'node:child_process'; import path from 'node:path'; import * as vscode from 'vscode'; import { RuntimeOptions } from './renderer'; import { ViewerTheme } from './webviewHtml'; let output: vscode.LogOutputChannel | undefined; export function log(): vscode.LogOutputChannel { output ??= vscode.window.createOutputChannel('Archify', { log: true }); return output; } const gitRoots = new Map>(); function gitTopLevel(dir: string): Promise { let pending = gitRoots.get(dir); if (!pending) { pending = new Promise((resolve) => { execFile('git', ['rev-parse', '--show-toplevel'], { cwd: dir, timeout: 10_000 }, (error, stdout) => { resolve(error ? undefined : path.resolve(stdout.trim())); }); }); gitRoots.set(dir, pending); } return pending; } export function clearGitRootCache(): void { gitRoots.clear(); } /** The checkout used to verify source evidence for a file, if any. */ export async function repoRootFor(uri: vscode.Uri | undefined): Promise { const configured = vscode.workspace.getConfiguration('archify', uri).get('repoRoot', '').trim(); if (configured) { if (path.isAbsolute(configured)) return configured; const folder = (uri && vscode.workspace.getWorkspaceFolder(uri)) ?? vscode.workspace.workspaceFolders?.[0]; return folder ? path.resolve(folder.uri.fsPath, configured) : path.resolve(configured); } if (!uri || uri.scheme !== 'file') return undefined; return gitTopLevel(path.dirname(uri.fsPath)); } export async function runtimeFor(context: vscode.ExtensionContext, uri: vscode.Uri | undefined): Promise { const config = vscode.workspace.getConfiguration('archify', uri); const nodePath = config.get('nodePath', '').trim(); const quality = config.get('quality', ''); return { cliPath: context.asAbsolutePath(path.join('vendor', 'archify', 'bin', 'archify.mjs')), nodePath: nodePath || process.execPath, runAsNode: !nodePath, workDir: path.join(context.globalStorageUri.fsPath, 'renders'), quality: quality || undefined, repoRoot: await repoRootFor(uri), log: (line) => log().debug(line), }; } export function evidenceMode(uri: vscode.Uri | undefined): 'fallback' | 'strict' { return vscode.workspace.getConfiguration('archify', uri).get<'fallback' | 'strict'>('sourceEvidence', 'fallback'); } export function viewerTheme(): ViewerTheme { const setting = vscode.workspace.getConfiguration('archify').get('theme', 'vscode'); if (setting === 'dark' || setting === 'light') return setting; if (setting === 'diagram') return undefined; const kind = vscode.window.activeColorTheme.kind; return kind === vscode.ColorThemeKind.Light || kind === vscode.ColorThemeKind.HighContrastLight ? 'light' : 'dark'; } /** Current text of a document: the open editor buffer, else the file on disk. */ export async function readText(uri: vscode.Uri): Promise { const open = vscode.workspace.textDocuments.find((doc) => doc.uri.toString() === uri.toString()); if (open) return open.getText(); return new TextDecoder('utf-8').decode(await vscode.workspace.fs.readFile(uri)); }