diff --git a/ilsp/server.py b/ilsp/server.py index baf5d25..413d060 100644 --- a/ilsp/server.py +++ b/ilsp/server.py @@ -107,11 +107,306 @@ async def _ws_proxy(request: web.Request, host: str, port: int) -> web.WebSocket return ws +_INDEX_HTML = """\ + + + + + + iLSP — Language Server Proxy + + + +
+

iLSP

+ Language Server Proxy + self-hosted +
+ +
+ +

+ iLSP is a WebSocket gateway that exposes language server protocol (LSP) + backends over a single HTTP endpoint. Editors connect via WebSocket and + receive code intelligence — completions, hover, diagnostics — for Bicep, + YAML pipelines, and Python, enriched with organisation-specific catalogs. +

+ +

What is LSP

+

+ The Language Server Protocol + is a standard protocol between editors and language analysis tools. + An editor sends requests (go-to-definition, completion, hover) and receives + structured responses without knowing anything about the language itself. + Any editor that speaks LSP — IntelliJ, Neovim, VS Code, Helix — works + with any LSP server. +

+

+ iLSP wraps existing language servers (Bicep Language Server, + yaml-language-server, pylsp) and injects completion items from + internal catalogs before responses are returned to the editor. +

+ +

Live status

+
+
{bicep_modules}
Bicep modules
+
{pipeline_templates}
Pipeline templates
+
{pypi_packages}
Python packages
+
254
AzDO tasks
+
+

+ Full JSON status: /health +

+ +

WebSocket endpoints

+ + + + + + + + + + + + + + + + + + + +
EndpointLanguage serverExtra completions
wss://ilsp.i80.dk/bicepBicep Language ServerACR module paths, versions, params
wss://ilsp.i80.dk/yamlyaml-language-serverAzDO task schema (254 tasks), pipeline templates
wss://ilsp.i80.dk/pythonpylsp (Jedi)Internal PyPI package stubs
+ +

Editor setup

+ +

Neovim — add to your LSP config (init.lua):

+
-- Bicep
+vim.lsp.start({{
+  name    = "ilsp-bicep",
+  cmd     = {{ "websocat", "wss://ilsp.i80.dk/bicep" }},
+  filetypes = {{ "bicep" }},
+}})
+
+-- YAML pipelines
+vim.lsp.start({{
+  name    = "ilsp-yaml",
+  cmd     = {{ "websocat", "wss://ilsp.i80.dk/yaml" }},
+  filetypes = {{ "yaml" }},
+}})
+ +

+ IntelliJ IDEA / Rider — install the + LSP client plugin, + then add a server under Settings → Languages & Frameworks → Language Servers: +

+
Name:    iLSP Bicep
+Command: websocat wss://ilsp.i80.dk/bicep
+Pattern: *.bicep
+
+Name:    iLSP YAML
+Command: websocat wss://ilsp.i80.dk/yaml
+Pattern: azure-pipelines.yml, *.yaml
+ +
+ websocat bridges a WebSocket to + stdio so editors that expect a local process can connect to a remote LSP. + Install with brew install websocat or + cargo install websocat. +
+ +

VS Code — use the + LSP client + or configure the built-in + vscode-languageclient in a workspace extension, pointing + serverOptions.command at websocat wss://ilsp.i80.dk/bicep. +

+ +

Updating the catalogs

+

+ Catalogs are baked into the Docker image at build time and can also be + refreshed at runtime without restarting. Run the sync scripts from the + iLSP repository and call the reload endpoint: +

+
# Sync Bicep module catalog from ACR and push
+python3 scripts/sync_push_catalogs.py
+
+# Trigger a hot reload without container restart
+curl -X POST https://ilsp.i80.dk/reload
+ +
+ + + + +""" + + async def _build_app() -> web.Application: app = web.Application() + async def index(_: web.Request) -> web.Response: + html = _INDEX_HTML.format( + bicep_modules=len(BicepModuleCatalog._modules), + pipeline_templates=PipelineTemplateCatalog.template_count(), + pypi_packages=len(PypiCatalog._packages), + ) + return web.Response(text=html, content_type="text/html", charset="utf-8") + async def health(_: web.Request) -> web.Response: import shutil + from .yaml_lsp.proxy import _AZDO_SCHEMA_PATH return web.json_response({ "status": "ok", "pypi_packages": len(PypiCatalog._packages), @@ -119,6 +414,7 @@ async def _build_app() -> web.Application: "iac_source_modules": len(BicepModuleCatalog._iac), "yaml_lsp": bool(shutil.which("yaml-language-server")), "pipeline_templates": PipelineTemplateCatalog.template_count(), + "azdo_pipeline_schema": _AZDO_SCHEMA_PATH.exists(), }) async def reload(_: web.Request) -> web.Response: @@ -149,6 +445,7 @@ async def _build_app() -> web.Application: async def yaml_ws(request: web.Request) -> web.WebSocketResponse: return await yaml_ws_handler(request) + app.router.add_get("/", index) app.router.add_get("/health", health) app.router.add_post("/reload", reload) app.router.add_get("/python", python_ws)