"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.activate = activate; exports.deactivate = deactivate; const net = __importStar(require("node:net")); const vscode = __importStar(require("vscode")); const node_1 = require("vscode-languageclient/node"); let client; let output; let reconnectTimer; let reconnectContext; let stoppingClient = false; let lastAppliedThemeSignature; const RECONNECT_DELAY_MS = 2000; const VSCODE_HOST_ID = "vscode"; function activate(context) { reconnectContext = context; output = vscode.window.createOutputChannel("Prometeu LSP"); output.appendLine("Prometeu VS Code extension activated."); const connectCommand = vscode.commands.registerCommand("prometeu.connectStudioLsp", async () => { await startClient(context); }); const restartCommand = vscode.commands.registerCommand("prometeu.restartStudioLsp", async () => { await stopClient(); await startClient(context); }); context.subscriptions.push(output, connectCommand, restartCommand); void startClient(context); } async function deactivate() { await stopClient(); } async function startClient(context) { clearReconnectTimer(); if (client) { output.appendLine("Prometeu LSP client is already running."); return; } const config = vscode.workspace.getConfiguration("prometeu.studio"); const host = config.get("host", "127.0.0.1"); const port = config.get("port", 7775); output.show(true); output.appendLine(`Connecting to Prometeu Studio LSP at ${host}:${port}...`); const serverOptions = async () => { return new Promise((resolve, reject) => { const socket = net.createConnection({ host, port }); let settled = false; const connectTimeout = setTimeout(() => { if (settled) { return; } settled = true; socket.destroy(); reject(new Error("Connection timed out.")); }, 3000); socket.once("connect", () => { if (settled) { return; } settled = true; clearTimeout(connectTimeout); output.appendLine("TCP connection established with Prometeu Studio."); resolve({ reader: socket, writer: socket }); }); socket.once("error", (error) => { if (settled) { return; } settled = true; clearTimeout(connectTimeout); reject(error); }); }); }; const clientOptions = { documentSelector: [ { scheme: "file", language: "pbs" } ], synchronize: { fileEvents: [ vscode.workspace.createFileSystemWatcher("**/*.pbs") ] }, initializationOptions: { clientKind: "vscode", prometeuClientVersion: context.extension.packageJSON.version }, outputChannel: output, errorHandler: { error: (error) => { output.appendLine(`Prometeu LSP transport error: ${error.message}`); return { action: node_1.ErrorAction.Continue }; }, closed: () => { output.appendLine("Prometeu LSP transport closed."); return { action: node_1.CloseAction.DoNotRestart }; } } }; const nextClient = new node_1.LanguageClient("prometeuStudioLsp", "Prometeu Studio LSP", serverOptions, clientOptions); client = nextClient; nextClient.onDidChangeState((event) => { output.appendLine(`Prometeu LSP state changed: ${node_1.State[event.oldState]} -> ${node_1.State[event.newState]}`); if (event.newState !== node_1.State.Stopped) { return; } if (client === nextClient) { client = undefined; } if (!stoppingClient) { scheduleReconnect(); } }); try { await nextClient.start(); await applyPrometeuVisualTheme(nextClient.initializeResult); output.appendLine("Prometeu Studio LSP client started."); vscode.window.showInformationMessage("Connected to Prometeu Studio LSP."); } catch (error) { if (client === nextClient) { client = undefined; } const message = error instanceof Error ? error.message : String(error); output.appendLine(`Failed to start Prometeu LSP client: ${message}`); vscode.window.showErrorMessage(`Could not connect to Prometeu Studio LSP: ${message}`); scheduleReconnect(); } } async function stopClient() { clearReconnectTimer(); if (!client) { return; } const currentClient = client; client = undefined; stoppingClient = true; output.appendLine("Stopping Prometeu Studio LSP client..."); try { await currentClient.stop(); output.appendLine("Prometeu Studio LSP client stopped."); } catch (error) { const message = error instanceof Error ? error.message : String(error); output.appendLine(`Failed to stop Prometeu LSP client cleanly: ${message}`); } finally { stoppingClient = false; } } function scheduleReconnect() { if (!reconnectContext) { return; } if (reconnectTimer || client) { return; } output.appendLine(`Scheduling Prometeu LSP reconnect in ${RECONNECT_DELAY_MS}ms...`); reconnectTimer = setTimeout(() => { reconnectTimer = undefined; void startClient(reconnectContext); }, RECONNECT_DELAY_MS); } function clearReconnectTimer() { if (!reconnectTimer) { return; } clearTimeout(reconnectTimer); reconnectTimer = undefined; } async function applyPrometeuVisualTheme(initializeResult) { const visualThemesPayload = extractPrometeuVisualThemesPayload(initializeResult); if (!visualThemesPayload) { output.appendLine("Prometeu visual theme payload not present in initialize result."); return; } const semanticProjectionPayload = extractPrometeuSemanticHostProjectionsPayload(initializeResult); const vscodeProjection = selectHostProjection(semanticProjectionPayload, VSCODE_HOST_ID); if (!semanticProjectionPayload) { output.appendLine("Prometeu semantic host projection payload not present in initialize result."); } else if (!vscodeProjection) { output.appendLine("Prometeu semantic host projection payload does not declare a vscode projection."); } const activeTheme = visualThemesPayload.visualThemes.find((theme) => theme.themeId === visualThemesPayload.activeVisualThemeId); if (!activeTheme) { output.appendLine(`Prometeu visual theme '${visualThemesPayload.activeVisualThemeId}' was not declared by the server.`); return; } const themeSignature = JSON.stringify({ languageId: visualThemesPayload.frontendLanguageId, themeId: activeTheme.themeId, editorPalette: activeTheme.editorPalette, tokenStyles: activeTheme.tokenStyles, semanticProjection: vscodeProjection ?? null }); if (themeSignature === lastAppliedThemeSignature) { output.appendLine(`Prometeu visual theme '${activeTheme.themeId}' is already applied.`); return; } const configurationTarget = vscode.workspace.workspaceFolders?.length ? vscode.ConfigurationTarget.Workspace : vscode.ConfigurationTarget.Global; const configuration = vscode.workspace.getConfiguration(); const existingSemanticCustomizations = configuration.get("editor.semanticTokenColorCustomizations") ?? {}; const semanticRules = { ...(readObject(existingSemanticCustomizations.rules)), ...buildSemanticTokenRules(visualThemesPayload.frontendLanguageId, activeTheme, vscodeProjection) }; await configuration.update("editor.semanticTokenColorCustomizations", { ...existingSemanticCustomizations, enabled: true, rules: semanticRules }, configurationTarget); const existingWorkbenchColors = configuration.get("workbench.colorCustomizations") ?? {}; await configuration.update("workbench.colorCustomizations", { ...existingWorkbenchColors, "editor.foreground": activeTheme.editorPalette.baseForeground, "editor.selectionBackground": activeTheme.editorPalette.selectionBackground, "editorLineNumber.foreground": activeTheme.editorPalette.lineNumberForeground }, configurationTarget); lastAppliedThemeSignature = themeSignature; output.appendLine(`Applied Prometeu visual theme '${activeTheme.themeId}' for language '${visualThemesPayload.frontendLanguageId}'.`); } function extractPrometeuVisualThemesPayload(initializeResult) { if (!initializeResult || typeof initializeResult !== "object") { return undefined; } const capabilities = readObject(initializeResult.capabilities); const experimental = readObject(capabilities.experimental); const payload = readObject(experimental.prometeuVisualThemes); if (typeof payload.frontendLanguageId !== "string" || typeof payload.activeVisualThemeId !== "string") { return undefined; } if (!Array.isArray(payload.visualThemes)) { return undefined; } return payload; } function extractPrometeuSemanticHostProjectionsPayload(initializeResult) { if (!initializeResult || typeof initializeResult !== "object") { return undefined; } const capabilities = readObject(initializeResult.capabilities); const experimental = readObject(capabilities.experimental); const payload = readObject(experimental.prometeuSemanticHostProjections); if (typeof payload.frontendLanguageId !== "string" || !Array.isArray(payload.semanticKeys) || !Array.isArray(payload.hostProjections)) { return undefined; } return payload; } function selectHostProjection(payload, hostId) { if (!payload) { return undefined; } return payload.hostProjections.find((projection) => projection.hostId === hostId); } function buildSemanticTokenRules(languageId, theme, hostProjection) { const rules = {}; const projectionBySemanticKey = new Map((hostProjection?.tokenProjections ?? []).map((projection) => [projection.semanticKey, projection])); for (const tokenStyle of theme.tokenStyles) { const styleValue = !tokenStyle.italic && !tokenStyle.bold && !tokenStyle.underline ? tokenStyle.foreground : { foreground: tokenStyle.foreground, ...(tokenStyle.italic ? { italic: true } : {}), ...(tokenStyle.bold ? { bold: true } : {}), ...(tokenStyle.underline ? { underline: true } : {}) }; rules[`${tokenStyle.semanticKey}:${languageId}`] = styleValue; const projection = projectionBySemanticKey.get(tokenStyle.semanticKey); if (!projection) { continue; } const projectedSelector = buildSemanticSelector(languageId, projection.hostTokenType, projection.hostTokenModifiers); if (!(projectedSelector in rules)) { rules[projectedSelector] = styleValue; } const fallbackSelector = buildSemanticSelector(languageId, projection.fallbackTokenType, projection.fallbackTokenModifiers); if (!(fallbackSelector in rules)) { rules[fallbackSelector] = styleValue; } } return rules; } function buildSemanticSelector(languageId, tokenType, modifiers) { const normalizedModifiers = [...modifiers].sort(); if (normalizedModifiers.length === 0) { return `${tokenType}:${languageId}`; } return `${tokenType}.${normalizedModifiers.join(".")}:${languageId}`; } function readObject(value) { if (!value || typeof value !== "object" || Array.isArray(value)) { return {}; } return value; } //# sourceMappingURL=extension.js.map