8.5 KiB
iLSP — Editor Setup Guide
iLSP is a self-hosted LSP proxy at ilsp.i80.dk that provides smart autocomplete for:
- Bicep — internal module registry, parameter names, allowed values, version tags
- Python — Jedi-powered completions and diagnostics
- YAML pipelines — Azure DevOps template references and GitHub Actions reusable workflows
No extra editor plugins are needed beyond the standard language-server clients you already have. iLSP speaks standard LSP over WebSocket and works with any editor that supports LSP WebSocket transport.
IntelliJ IDEA setup (LSP4IJ)
Install the LSP4IJ plugin, then add language servers:
Bicep
- Settings → Languages & Frameworks → LSP → Language Servers → +
- Name:
iLSP Bicep - Server type:
Command(not WebSocket — see note below) - Command:
/Users/lrihni/Projects/iLSP/scripts/lsp_bridge_debug.sh wss://ilsp.i80.dk/bicep - Arguments: (leave blank)
- File pattern:
*.bicep;*.bicepparam
- Name:
YAML (Azure DevOps + GitHub Actions)
- Settings → Languages & Frameworks → LSP → Language Servers → +
- Name:
iLSP YAML - Server type:
Command - Command:
/Users/lrihni/Projects/iLSP/scripts/lsp_bridge_debug.sh wss://ilsp.i80.dk/yaml - Arguments: (leave blank)
- File pattern:
*.yaml;*.yml;azure-pipelines.yml
- Name:
Python (optional)
-
Settings → Languages & Frameworks → LSP → Language Servers → +
- Name:
iLSP Python - Server type:
Command - Command:
/Users/lrihni/Projects/iLSP/scripts/lsp_bridge_debug.sh wss://ilsp.i80.dk/python - Arguments: (leave blank)
- File pattern:
*.py
Note
: LSP4IJ's WebSocket mode doesn't handle LSP Content-Length framing correctly, causing "starting..." to hang or "Stream closed" errors. Use
lsp_bridge_debug.shwrapper which callslsp_bridge.pyand logs errors to/tmp/lsp_bridge_debug.logfor debugging. - Name:
Troubleshooting
If the language server doesn't start, check the debug log:
tail -f /tmp/lsp_bridge_debug.log
Disable conflicting plugins
Disable Azure Toolkit Bicep completions — this is critical if you have the
Azure Toolkit plugin installed. Without this step, IDEA merges completions from
both LSP4IJ and Azure Toolkit, resulting in noisy suggestions (resource,
projectName, Bicep schema types, etc.) appearing alongside iLSP results.
Go to Settings → Languages & Frameworks → Azure Toolkit for IntelliJ and disable or uncheck Bicep language support. If there is no such option, disable the Azure Toolkit plugin entirely for Bicep projects, or suppress its completion contributor via Settings → Editor → General → Code Completion (uncheck "Show suggestions from plugins that don't support the current language server").
Why: LSP4IJ feeds completions through the LSP protocol where iLSP can filter them. Azure Toolkit injects completions directly into IDEA's completion system, bypassing the LSP layer entirely — iLSP cannot suppress those.
Neovim setup
Important
:
vim.lsp.rpc.connect(url)does not speak WebSocket — it's a raw TCP/pipe connector and will fail withENOENTon awss://URL (verified). Usecmd = { ".../scripts/lsp_bridge_debug.sh", "wss://..." }instead (stdio ↔ WebSocket bridge, same one IntelliJ/LSP4IJ uses). Confirmed working end-to-end (32 module completions returned) with the config below.
-- In your LSP config (e.g. ~/.config/nvim/lua/lsp.lua)
local BRIDGE = "/Users/lrihni/Projects/iLSP/scripts/lsp_bridge_debug.sh"
-- Bicep
vim.lsp.start({
name = "ilsp-bicep",
cmd = { BRIDGE, "wss://ilsp.i80.dk/bicep" },
root_dir = vim.fs.dirname(vim.fs.find({ "bicepconfig.json", ".git" }, { upward = true })[1]),
filetypes = { "bicep" },
})
-- YAML (pipeline files)
vim.lsp.start({
name = "ilsp-yaml",
cmd = { BRIDGE, "wss://ilsp.i80.dk/yaml" },
root_dir = vim.fs.dirname(vim.fs.find({ ".git" }, { upward = true })[1]),
filetypes = { "yaml" },
})
-- Python
vim.lsp.start({
name = "ilsp-python",
cmd = { BRIDGE, "wss://ilsp.i80.dk/python" },
root_dir = vim.fs.dirname(vim.fs.find({ "pyproject.toml", ".git" }, { upward = true })[1]),
filetypes = { "python" },
})
VS Code setup
Install the LSP WebSocket extension (or equivalent), then add to settings.json:
{
"lspWebSocket.servers": [
{ "languageId": "bicep", "url": "wss://ilsp.i80.dk/bicep" },
{ "languageId": "yaml", "url": "wss://ilsp.i80.dk/yaml" },
{ "languageId": "python","url": "wss://ilsp.i80.dk/python" }
]
}
What iLSP adds on top of the standard schema
Bicep — internal module registry
The standard Bicep LSP knows nothing about your internal ACR registry. iLSP intercepts completion requests and injects:
| Context | What you get |
|---|---|
'br/modules:<cursor>' |
All 27 internal modules (appservice, roleassignments, …) |
'br/modules:roleassignments:<cursor>' |
Available versions (1.1.x, 2.0.x, latest, …) |
params { inside a module block |
Parameter names with types, required/optional, defaults |
| parameter value positions | Allowed values for enum-type params |
Items from the internal catalog always sort to the top of the completion list.
YAML pipelines — AzDO template completions
When you type - template: in an azure-pipelines.yml, iLSP recognises the AzDO
pipeline format and injects completions from the pipeline-templates Bitbucket repo:
steps:
- template: tasks/k8s/deploy.yaml@pipeline-templates
# ↑ cursor here → get all task templates with @pipeline-templates label
parameters:
environment: | # ← cursor here → get param names for deploy.yaml
targetNamespace: # ← cursor here → get allowed values if defined
Format detection — iLSP auto-detects AzDO vs GHA:
- template:orstages:/trigger:→ AzDO modeon:/workflow_call/runs-on:→ GHA mode
No extra configuration needed in your editor.
YAML pipelines — GitHub Actions reusable workflows
In .github/workflows/*.yml files that call LRU-Digital reusable workflows:
jobs:
deploy:
uses: LRU-Digital/infra/.github/workflows/deploy-k8s.yml@main
# ↑ cursor here → get all reusable workflows
with:
environment: | # ← cursor here → get input names for deploy-k8s.yml
Updating the catalogs
Catalogs are baked into the Docker image at build time. To update them:
cd ~/Projects/iLSP
# 1. Regenerate catalogs from local repos
python3 scripts/sync_pipeline_templates.py # → pipeline_templates_catalog.json
python3 scripts/sync_iac_module_sources.py # → iac_source_catalog.json (from DevOpsMCP)
python3 scripts/sync_bicep_modules.py # → bicep_modules_catalog.json
# 2. Commit + push — Gitea Actions builds the image and Nomad redeploys it
git add bicep_modules_catalog.json iac_source_catalog.json pipeline_templates_catalog.json
git commit -m "chore: refresh catalogs"
git push
Note
:
scripts/push_catalogs.shand the/reloadendpoint only take effect if/datais volume-mounted into the running container. Production (ilsp.nomad) currently has no/datamount — the underlying Nomad client has raw Docker bind mounts disabled (volumes are not enabled), sopush_catalogs.shsilently pushes to a path the container never sees, and/reloadis a no-op there./dataonly works for local dev viamake run-with-data. In production, catalog updates always require a commit + redeploy (as above) until the client is configured with a proper Nomadhost_volume.
Health check
curl https://ilsp.i80.dk/health
# → {"status":"ok","bicep_modules":27,"iac_source_modules":26,"pipeline_templates":48,"yaml_lsp":true,...}
| Field | What it means |
|---|---|
bicep_modules |
Modules in the ACR registry catalog |
iac_source_modules |
Modules with source-level param docs |
pipeline_templates |
AzDO + GHA templates available for YAML completion |
yaml_lsp |
yaml-language-server detected and working |
Smoke tests
# Against production
python3 scripts/smoke_test_completions.py
# Against local dev container
python3 scripts/smoke_test_completions.py http://localhost:2089
Tests 1–5 cover Bicep, tests 6–7 cover YAML pipeline templates.