// tools/lsp/vscode-extension/extension.js // // El Language VSCode extension — Language Client // // Launches the el-lsp binary as a child process and connects VSCode's // Language Client to it over stdin/stdout (JSON-RPC 2.0 + Content-Length). // // Capabilities provided by el-lsp: // - textDocumentSync (full) // - completionProvider — builtins + user fns + keywords // - hoverProvider — signatures + descriptions // - definitionProvider — go-to-def for user-defined fns // - diagnostics — unclosed braces/parens, unterminated strings // // Setup (development): // 1. cd tools/lsp && ./build.sh (builds dist/el-lsp) // 2. cd vscode-extension && npm install (installs vscode-languageclient) // 3. Open vscode-extension/ in VSCode // 4. Press F5 (launches Extension Development Host) // 5. Open any .el file // // Package as .vsix: // npm install -g @vscode/vsce // cd tools/lsp/vscode-extension && vsce package 'use strict'; const path = require('path'); const fs = require('fs'); const { workspace, window, ExtensionContext } = require('vscode'); const { LanguageClient, TransportKind, } = require('vscode-languageclient/node'); /** @type {LanguageClient | undefined} */ let client; /** * activate — entry point called when any .el file is first opened. * * @param {ExtensionContext} context */ function activate(context) { // ── Resolve el-lsp binary ──────────────────────────────────────────── const config = workspace.getConfiguration('el'); // Default path resolution (tries in order): // 1. el.lspPath setting (user override) // 2. dist/el-lsp alongside the extension dir (packaged install) // 3. The canonical source-tree location const CANONICAL_LSP = '/Users/will/Development/neuron-technologies/foundation/el/tools/lsp/dist/el-lsp'; const defaultLspPath = (() => { const sibling = path.join(context.extensionPath, 'dist', 'el-lsp'); if (fs.existsSync(sibling)) return sibling; return CANONICAL_LSP; })(); const serverPath = config.get('lspPath') || defaultLspPath; if (!fs.existsSync(serverPath)) { window.showErrorMessage( `El Language Server binary not found at: ${serverPath}\n` + `Run tools/lsp/build.sh to build it, then reload VSCode.\n` + `Or set "el.lspPath" in settings to the correct path.` ); return; } // ── Server options (start el-lsp as child process) ─────────────────── /** @type {import('vscode-languageclient/node').ServerOptions} */ const serverOptions = { run: { command: serverPath, transport: TransportKind.stdio, options: { env: { ...process.env } }, }, debug: { command: serverPath, transport: TransportKind.stdio, options: { env: { ...process.env }, // Redirect el-lsp stderr to VSCode's Output panel in debug mode. }, }, }; // ── Client options ─────────────────────────────────────────────────── /** @type {import('vscode-languageclient/node').LanguageClientOptions} */ const clientOptions = { // Activate for all .el files (file:// and untitled: schemes). documentSelector: [ { scheme: 'file', language: 'el' }, { scheme: 'untitled', language: 'el' }, ], synchronize: { // Fire fileEvents so the client resends on disk changes outside VSCode. fileEvents: workspace.createFileSystemWatcher('**/*.el'), }, outputChannelName: 'El Language Server', // Trace LSP messages to the Output panel for debugging. // Set "el.trace.server": "verbose" in settings to enable. traceOutputChannel: window.createOutputChannel('El LSP Trace'), }; // ── Create and start client ────────────────────────────────────────── client = new LanguageClient( 'el-language-server', 'El Language Server', serverOptions, clientOptions, ); // Register the client so it is disposed when the extension deactivates. context.subscriptions.push(client); client.start().then(() => { // Show a discrete status bar item while the server is active. const status = window.createStatusBarItem(1); status.text = '$(symbol-misc) El LSP'; status.tooltip = 'El Language Server is running'; status.command = 'el.restartServer'; status.show(); context.subscriptions.push(status); }).catch((err) => { window.showErrorMessage(`El Language Server failed to start: ${err.message}`); }); // ── Commands ───────────────────────────────────────────────────────── context.subscriptions.push( require('vscode').commands.registerCommand('el.restartServer', () => { if (client) { client.stop().then(() => client.start()); window.showInformationMessage('El Language Server restarted.'); } }) ); } /** * deactivate — called when the extension is unloaded. * Sends LSP shutdown + exit to the server process. */ function deactivate() { if (!client) { return undefined; } return client.stop(); } module.exports = { activate, deactivate };