Home - NormB/kamailio-lsp GitHub Wiki
A Language Server Protocol implementation for the Kamailio
configuration file language (kamailio.cfg).
| Feature | How |
|---|---|
| Diagnostics | Runs kamailio -c --all-errors -Y <tmpdir> -f <file> on open/save and maps the parser's errors (line, column, column ranges, multi-line spans) to LSP diagnostics — full-fidelity, version-exact semantic validation by the real parser. A fast analyzer layer warns between saves (debounced on change): undefined route() targets and duplicate route definitions. |
| Completion | Context-sensitive: module names after loadmodule " / modparam(", the module's parameters inside the second modparam argument, exported functions of loaded modules plus core functions/parameters, route names inside route( and in route bodies, keywords, and pseudo-variables after $ (replacing the typed token). Duplicate labels collapse; include_file/import_file closures count. |
| Signature help | The innermost unclosed call's signature with the active parameter, on ( and ,. |
| Hover | Documentation for module functions, parameters, and modules, harvested from Kamailio's own docs. |
| Go to definition |
route(NAME) references resolve to their route[NAME] block — in this file or any included file. |
| References / rename / highlights | Every call site + definition of a route name; rename rewrites them all (charset-gated, quoted call sites handled). prepareRename blocks F2 off-symbol and pre-selects the name. |
| Document symbols | All route blocks (request_route, route[...], failure_route[...], event_route[...], …) with full block extents, nested outline. |
| Folding | Route-family blocks fold; brace matching is string/comment-safe. |
| Workspace symbols / code lenses | Ctrl+T searches route definitions across open files + includes; named callable routes show closure-wide reference counts. |
| Call hierarchy | The route call graph: who calls route[X], and what route[X] calls, across the include closure. Main-table only — a failure_route is armed by a module-function string the server cannot see, so it makes calls but reports no callers. |
| Quick fixes | Load the module exporting an unknown command; create a stub for an undefined route(x). |
| Refactorings | Extract a selection into a route[...] of its own, leaving a call behind; remove duplicate loadmodule lines (a second load is a parse error, not untidiness). |
| Inlay hints | Parameter names from the documentation at module-function call sites, and what each #!define expands to at its uses — visible range only. |
| Preprocessor symbols |
#!define and friends: hover for what a name binds, Ctrl+Click to its directive (works inside #!ifdef too), completion, and outline entries. The analyzer expands through them, so a route reached by alias is no longer flagged undefined. |
| Catalog validation |
modparam parameters the configured tree does not document warn as you type. |
| Include links |
include_file/import_file paths are Ctrl+Click document links. |
| Included files | An include_file/import_file fragment opened on its own is answered in its ROOT's context: the analyzer runs over the root's closure and kamailio -c is run on the root, with each error routed to the file it names. Otherwise a fragment reports every route its parent defines as undefined. The VS Code client also gives an unassociated .cfg the language when the workspace's configuration includes it. |
| Formatting | Whole-document and range formatting re-indents by brace depth and strips trailing whitespace, honouring the editor's tab settings. Line-preserving: it never joins, splits or reorders lines, and never touches a string, comment or #! directive. |
| Semantic tokens | Route names + pseudo-variables (both string quote styles), UTF-16 delta-encoded; semanticTokens/range serves just the visible slice. |
| Pull diagnostics |
textDocument/diagnostic and workspace/diagnostic. The workspace sweep reports only ROOT configs — a file another config includes is a fragment, not a program. Pushing stops when the client pulls, so nothing is reported twice. |
| Watched files | An include, the module tree or the wiki checkout changing on disk — a git checkout, a rebuild — re-checks and re-harvests without the buffer being touched. |
| Live settings | Runtime toggles (analyzer, snippets, code lens, max diagnostics, check timeout) apply over workspace/didChangeConfiguration without a server restart. |
| CLI |
kamailio-lsp check [--strict] [--bin <kamailio>] <file>... for CI and git hooks (exit 0/1/2). |
Positions are exchanged in UTF-16 units (the LSP default) and are correct on multibyte lines; doc harvests are cached per source tree (see the admin guide's Caching section).
The documentation catalog is harvested at startup from two places:
module docs from the generated plain-text README in every
src/modules/<name>/ directory of a Kamailio source tree, and
core-language docs (parameters, functions, pseudo-variables) from a
kamailio-wiki checkout
(docs/cookbooks/<version>/ — the newest stable cookbook is picked).
Supported and version-proven: Kamailio 6.1.x (6.1.4, the current
stable line) and 6.0.x (6.0.1) — the proof suite runs against a
real tree, wiki, and binary of each
(KAMAILIO_LSP_TEST_TREE/KAMAILIO_LSP_TEST_WIKI/KAMAILIO_LSP_TEST_BIN).
Nothing pins a version: module docs come from the tree you point at,
core docs from the newest stable cookbook in the wiki checkout, and
diagnostics from your own binary.
Via LSP initializationOptions (or environment fallback):
| Option | Env | Default | Meaning |
|---|---|---|---|
kamailioPath |
KAMAILIO_LSP_BIN |
kamailio |
Binary used for -c diagnostics. |
kamailioSrc |
KAMAILIO_LSP_SRC |
(none) | Source tree to harvest module docs from. |
kamailioWiki |
KAMAILIO_LSP_WIKI |
(none) | kamailio-wiki checkout for core-language docs. Not needed to get started: the core language and all 254 documented modules are built in, harvested from 6.1.x and 6.1.4, and used whenever nothing is configured — so debug, loadmodule " and is_method all complete on a fresh install. A configured source wins and replaces the matching catalogue wholesale. |
modulesPath |
— | (none) | Module search path for the checker (-L). |
Diagnostics fidelity note: -c loads the modules the cfg references,
so it needs an installation where those .so files exist (an
unresolvable module is itself reported as a diagnostic, which is
usually what you want; modulesPath points the checker elsewhere).
New to all of this? Follow the Getting Started guide — one-command install plus click-by-click usage instructions. Short version:
curl -fsSL https://raw.githubusercontent.com/NormB/kamailio-lsp/main/install.sh | shPrebuilt server binaries (Linux, macOS, and Windows — x86_64 and
arm64) and the VS Code .vsix ship with every
GitHub release:
tar xzf kamailio-lsp-<version>-x86_64-linux-gnu.tar.gz
install -m755 kamailio-lsp ~/.local/bin/cargo build --release # server binary: target/release/kamailio-lsp
eval "$(scripts/proof-env.sh)" # real Kamailio tree + binary, once
cargo test # full suite, includes a stdio LSP e2e testA skipped test is a failed test here. Parts of the suite prove
behaviour against a real Kamailio tree and a real kamailio binary, and
they refuse to run without one rather than reporting green while
proving nothing. scripts/proof-env.sh provisions both into
.proof/ (gitignored) and prints the environment to export; CI runs
that same script, so a green CI means the proofs actually ran. A gate
in the suite fails if any test ever announces a skip again.
tree-sitter-kamailio/ carries an error-tolerant grammar for editors
that highlight and fold via tree-sitter (Neovim, Helix, Zed): corpus
tests run in CI; tree-sitter generate builds the parser locally.
- Features-and-Settings — every feature, every setting (VS Code / init option / environment), and the snippet set.
- Admin-Guide — admin guide (overview, dependencies, exported parameters, security, FAQ).
-
Editor-Setup — worked setup for VS Code,
Neovim, coc.nvim, Helix, Emacs, Vim, Sublime Text, Kate and
JetBrains (LSP4IJ), plus any other LSP client, plus using the
checkCLI in CI and git hooks with no editor at all. -
docs/ZED.md— Zed step by step: Zed needs a small WebAssembly extension, and this builds one for you. - API docs:
cargo doc --open(missing_docsisdeny).
-
VS Code: the
client/directory contains the extension (npm install && npm run compile, then run/package with vsce). Settings:kamailioLsp.serverPath,kamailioLsp.kamailioPath,kamailioLsp.kamailioSrc,kamailioLsp.kamailioWiki. -
Neovim (0.10+):
vim.api.nvim_create_autocmd("FileType", { pattern = "kamailio-cfg", callback = function() vim.lsp.start({ name = "kamailio-lsp", cmd = { "kamailio-lsp" }, init_options = { kamailioPath = "/usr/sbin/kamailio", kamailioSrc = "/path/to/kamailio", kamailioWiki = "/path/to/kamailio-wiki", }, }) end, })
-
src/catalog.rs— module-README + wiki-cookbook documentation harvester -
src/analyze.rs— comment/string-aware lexical scan of cfg text (loadmodules, routes, cursor context); deliberately not a grammar -
src/diag.rs—kamailio -coutput parser -
src/logic.rs— pure completion/hover/definition assembly -
src/server.rs— tower-lsp-server wiring
Semantic truth stays in Kamailio itself (-c); the server never
guesses about grammar validity, so it is automatically correct for
whatever Kamailio version it is pointed at.
Nothing this server sees leaves your machine. There is no network
client in it: the crate links no HTTP library — check Cargo.lock —
opens no sockets, and speaks JSON-RPC to your editor over
stdin/stdout. No telemetry, no analytics, no crash reporting, no
update check, and no model: hover and completion text is parsed out
of Kamailio's own documentation on disk, never generated.
What it touches, all of it local:
-
Reads — the cfg you opened and every file its
include_file/import_fileclosure names, plus thekamailioSrctree andkamailioWikicheckout you configure. -
Writes — one documentation-catalog cache under
cacheDir(default: the platform cache directory). It holds documentation harvested from those trees; your configuration is never written to it. -
Runs — your own
kamailiobinary, askamailio -con the file you opened, to produce diagnostics. That is code execution; see Security note for what gates it.
Two caveats worth knowing:
- Setting
kamailioLsp.trace.servertomessagesorverboseechoes the LSP traffic — which carries your cfg text — into the editor's output channel. It stays on the machine, but it is the one place config content lands in a log that is easy to paste into a bug report. The default isoff. - Your editor is a separate question. Its own telemetry, an AI assistant extension, or anything else with access to the buffer sees what you type regardless of what this server does. That boundary is outside this project.
The only outbound connection anywhere in this repository is
install.sh/install.ps1 fetching a release from GitHub, and your
editor fetching the extension. Both are downloads.
kamailio -c dlopens the modules the cfg loads — their
constructors run. Opening a config from an untrusted source therefore
executes code paths you did not write. Rely on your editor's
workspace-trust prompt, and/or disable diagnostics entirely by
setting kamailioPath (or KAMAILIO_LSP_BIN) to an empty string
— completion, hover, and navigation keep working without it.
-c runs are serialized and bounded (10s default,
KAMAILIO_LSP_CHECK_TIMEOUT_MS to tune).
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT license (LICENSE-MIT)
at your option. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.