# iLSP — Internal LSP Proxy Self-hosted Language Server Protocol proxy for LRU's internal tooling. Runs at **https://ilsp.i80.dk** (Nomad/Docker, `autobox.i80.dk`). Provides smart autocomplete on top of standard LSPs: | Language | Endpoint | Extra completions | |----------|----------|------------------| | Bicep | `wss://ilsp.i80.dk/bicep` | Internal ACR modules, versions, params | | YAML | `wss://ilsp.i80.dk/yaml` | AzDO pipeline templates, GHA reusable workflows | | Python | `wss://ilsp.i80.dk/python` | Jedi (standard) | → See **[EDITOR_SETUP.md](EDITOR_SETUP.md)** for editor configuration and a full feature overview. ## LSP feature coverage Full capability audit against production (verified 2026-08-14) — everything below is confirmed working end-to-end over the live WebSocket endpoints, not just in theory: | Feature | Bicep (`/bicep`) | Python (`/python`) | YAML (`/yaml`) | |---|---|---|---| | Completions (+ internal catalogs) | ✅ | ✅ | ✅ | | Hover | ✅ | ✅ | ✅ | | Go to definition | ✅ | ✅ | — | | Find references | ✅ | ✅ | — | | Document symbols | ✅ | ✅ | — | | Document formatting | ✅ | ✅ | — | | Code actions | ✅ | ✅ | — | | Signature help | ✅ *(iLSP-provided)* | ✅ | — | | Folding range | ✅ *(iLSP-provided)* | ✅ | — | | Workspace symbol | ✅ *(iLSP-provided)* | ❌ (pylsp doesn't implement it) | — | | Diagnostics | ✅ | ✅ | ✅ | Bicep.LangServer itself (v0.46.1, latest) doesn't implement signature help, folding range, or workspace symbol — iLSP answers these three locally in the proxy (`ilsp/bicep_lsp/local_features.py`) instead of forwarding to a backend that would just error or return nothing. ### Confirmed examples (live against `wss://ilsp.i80.dk/bicep`) **Role-array completion** — cursor inside `roles: [...]` returns all 682 Azure built-in roles: ``` roles: ['KEY_VAULT_SECRETS_USER', 'STORAGE_BLOB_DATA_CONTRIBUTOR', ] → 682 items: ACCESS_REVIEW_OPERATOR_SERVICE_ROLE, ACRDELETE, ACRIMAGESIGNER, ACRPULL, ACRPUSH, ... ``` **Go to definition** — jumping from a variable usage to its declaration: ```bicep var storageName = '${projectName}storage' # ← declared here (line 2) resource sa '...' = { name: storageName # ← cursor here → jumps to line 2 } ``` **Find references** — from the `storageName` declaration, finds both places it's used: the string interpolation `${projectName}storage` isn't a reference, but the `resource sa { name: storageName }` property assignment is — returns the declaration itself plus that 1 real usage (2 results total). **Document symbols** — outline for a file with params/vars/resources/modules: ``` location, projectName, storageName, sa, roleAssignment, storageId ``` **Signature help** — cursor right after `resourceId(`: ``` resourceId(resourceType, resourceName1, resourceName2...) ^ active parameter highlighted ``` **Folding range** — every multi-line `{}`/`[]`/`()` block returned as a collapsible region (nested blocks included). **Workspace symbol** — searching `"storage"` across all open documents in the editor session returns `storageName`, `storageId`, etc. ## Quick start ```bash # Check service health curl https://ilsp.i80.dk/health # Run smoke tests python3 scripts/smoke_test_completions.py # Local dev make run # build + start + health check make run-quick # start without rebuilding make logs # tail container logs make health # curl health endpoint ``` ## Updating catalogs Catalogs are baked into the Docker image at build time — commit + push to update production (see [EDITOR_SETUP.md](EDITOR_SETUP.md#updating-catalogs) for the full workflow and a note on why `push_catalogs.sh` / `/reload` don't take effect in prod). ```bash python3 scripts/sync_pipeline_templates.py # scan AzDO + GHA template repos python3 scripts/sync_bicep_modules.py # from DevOpsMCP repo — needs az CLI + ACR access git add *_catalog.json && git commit -m "chore: refresh catalogs" && git push ``` ## Deploy Push to `main` → Gitea Actions builds image → Nomad deploys automatically. ```bash # Manual deploy (if CI is down) git push ssh autobox.i80.dk 'nomad job run /opt/nomad/jobs/ilsp.nomad' ```