7 releases
Uses new Rust 2024
| new 0.3.3 | May 9, 2026 |
|---|---|
| 0.3.2 | May 9, 2026 |
| 0.2.1 | May 3, 2026 |
| 0.1.1 | Apr 26, 2026 |
#38 in Programming languages
460KB
7.5K
SLoC
axon-lsp
Language Server Protocol implementation for the Axon programming language. A single Rust binary that speaks LSP and, by construction, works in VSCode, Claude Code, Cursor, Antigravity, Zed, Neovim — or anything else that speaks LSP — with no per-IDE work.
Status: v0.3.0 released — published on crates.io with
axon-frontend = "0.3"as a registry dep. v0.3.0 lifts the capability surface past the rust-analyzer / pyright "core" baseline with eight new LSP-spec features (signature help, document highlight, folding, smart-select, code lens, call hierarchy, on-type formatting, pull diagnostics) plus cancel-aware workspace fan-outs and two new code actions. Roadmap and Resultado blocks for every sub-fase live indocs/plan_v0.3.0.md; historical plans sit indocs/plan_v0.2.0.mdanddocs/plan_v0.1.0.md. Install viacargo install axon-lsp, download from GitHub Releases, install the VSCode extension, or build from source — see docs/integrations/install.md.
What it does
| Capability | Behavior |
|---|---|
publishDiagnostics |
Lex / parse / type errors with axon-{lex,parse,type} source tag and precise ranges (whole identifier for type errors, single char for punctuation) |
hover |
Markdown — local declarations show kind + signature; outer doc comments (///, /** */) attached to channel decls render as a Markdown paragraph above the signature; built-in types (String, Channel, Trusted, …) and syntax keywords pull rich docs from the embedded corpus |
definition |
Zero-width Location at the declaration's keyword |
documentSymbol |
Outline tree with sensible SymbolKind per declaration kind |
completion |
Context-aware: inside a type annotation surfaces only types, in declaration name slots stays out of the way, default context offers keyword snippets + user decls + built-in types with lsp-docs documentation attached |
references |
File-local + workspace-wide. Walks every TypeExpr-bearing site (type field types, flow params, return types, intent output types) with Loc-precise ranges; cross-file scan reads in-memory snapshots first, falls back to disk parses |
prepareRename + rename |
Workspace-wide atomic WorkspaceEdit. D8 rejections pinned in code: built-in types, declaration keywords, stdlib symbols, and identifiers absent from the workspace index reject with stable user-facing messages |
documentSymbol + workspace/symbol |
The latter is a substring-match query against an index of every name → DeclLocation the server has seen in any .axon file under workspace folders |
semanticTokens/full |
LSP standard registry mapping — types, functions, variables, parameters, properties, decl keywords, with declaration / readonly modifiers. The TextMate grammar still paints lexical concerns (strings, numbers, regular comments) so the two layers don't double-paint |
inlayHint |
Inferred type after let literal RHS — : String, : Integer, : Float, : Bool. Range filter respected so off-screen lines don't ship hints |
codeAction |
Quick fixes (kind = quickfix): Rename to typo candidate (Levenshtein-≤2 against the workspace decl name set on Undefined <kind> 'NAME' …, up to 3 candidates), Insert missing } (zero-width insertion on Expected RBrace, found …), Add missing field (matcher ready for axon-frontend 0.4 — fires on … field 'F' … type 'T' … shape and inserts F: Any after the type's last existing field). Refactors (kind = refactor.rewrite): Make T optional — cursor-driven, appends ? to a non-optional type expression, idempotent on already-optional types |
formatting |
Token-level passthrough through the axon-fmt crate. Right-trims trailing whitespace per line + ensures exactly one trailing \n. Comments survive verbatim across all six kinds (//, /* */, ///, /** */, //!, /*! */) thanks to the Fase 14 trivia channel. Mirrors axon fmt in axon-lang v1.9.0+ for byte-identical output |
onTypeFormatting |
Trigger: }. Surfaces only the line edits within [pos.line, pos.line + 2] so the user's keystroke flow isn't fought by full-document rewrites. Same idempotence + comment-round-trip guarantees as formatting |
signatureHelp |
Pops the flow's signature label + active-parameter highlight when the cursor sits inside `run FlowName(arg1, arg2 |
documentHighlight |
File-local — every occurrence of the symbol under the cursor with WRITE for the declaration and READ for each use. Reuses the references AST walker via a RangeKind-aware variant |
foldingRange |
Three families: top-level decl bodies (region), multi-line block comments (comment), and runs of three or more consecutive line comments (comment). Walker skips strings + every comment shape so { / } inside "…" or // … don't open frames |
selectionRange |
AST-driven smart-select expansion with strict-nesting filter: program → decl → field/parameter → type-expr (full extent including generic param + ?) → word. Multi-cursor supported (LSP allows Vec<Position> per request) |
codeLens |
One N references lens per top-level declaration. Click invokes editor.action.showReferences pre-populated with the workspace-wide use locations. 0 references lenses are intentionally omitted (D3 — no 0 references noise). resolve_provider: false so each lens ships complete from the first pull |
prepareCallHierarchy + incomingCalls + outgoingCalls |
Limited to call-able kinds (flow, persona, tool — D6 conservative scope). Top-level run statements surface as synthesised callers with kind EVENT; flow bodies with step.persona_ref / use_tool.tool_name references surface as flow callers. Outgoing only fires for flows |
diagnostic + workspace/diagnostic (LSP 3.17 pull) |
Advertised alongside push. When the client advertises textDocument.diagnostic, the server flips into pull mode for every URI — did_open and the debounced reparse stop publishing push notifications. workspace/diagnostic walks the union workspace_index.all_uris ∪ doc_store.open_uris so parse-error files (which never reach the index) still surface their diagnostics |
window/workDoneProgress (workspace scan) |
Wraps the recursive .axon discovery + parse pass on initialized. Lifecycle: workDoneProgress/create → Begin → throttled Report (~20 reports per scan, message "42 / 200 — file.axon") → End. Older clients that don't ack create get the scan without the lifecycle — graceful degradation, never blocks |
| Cancel-aware workspace fan-outs | Five long-running handlers (references, rename, code_lens, incoming_calls, workspace_diagnostic) yield via tokio::task::yield_now() between iterations so tower-lsp's drop-on-cancel semantics actually preempt stale requests. The user navigating away mid-scan no longer pays for the rest of the work |
axon/askAdvisor (opt-in) |
Custom LSP method backed by Anthropic's Messages API. Two-axis gate: build with --features llm AND set AXON_LSP_LLM_ENABLED=1 + ANTHROPIC_API_KEY=…. Default builds reply with MethodNotFound; the deterministic stack is unaffected by either gate. |
Editor integrations
Single binary, every editor. Pick yours and follow the doc:
- VSCode — docs/integrations/vscode.md (extension source)
- Cursor — docs/integrations/cursor.md
- Antigravity — docs/integrations/antigravity.md
- Zed — docs/integrations/zed.md
- Neovim — docs/integrations/neovim.md
- Claude Code — docs/integrations/claude-code.md
docs/integrations/README.md carries
the index plus the canonical 4-check smoke-test program every editor
must light up.
Architecture
crates/
axon-lsp/ # binary, tower-lsp bootstrap + every LSP handler
lsp-core/ # logic — pure, sync, no I/O
# document.rs (rope), diagnostics.rs (range scanner),
# hover.rs, definition.rs, completion.rs (context heuristic),
# symbols.rs (DeclMeta + outline), text.rs (shared helpers),
# frontend.rs (axon-frontend shim),
# workspace.rs (cross-file decl index), references.rs,
# rename.rs, semantic_tokens.rs, inlay_hints.rs,
# code_actions.rs, formatting.rs (axon-fmt shim),
# signature_help.rs, document_highlight.rs,
# folding_range.rs, selection_range.rs, code_lens.rs,
# call_hierarchy.rs (v0.3.0 capability completeness)
axon-fmt/ # token-level lossless formatter, consumed by `lsp-core`
# via workspace dep + invoked by `textDocument/formatting`.
# Mirrors `axon fmt` in `axon-lang` v1.9.0+
lsp-docs/ # 18+ Markdown entries embedded at compile time via build.rs;
# zero runtime deps, lookups hit `&'static str` slices
lsp-llm/ # opt-in advisor, feature = "llm";
# default builds keep `reqwest` out of the binary entirely
# — pinned by an executable smoke test that scans the
# release binary for forbidden symbol bytes
editors/
vscode/ # TypeScript extension; spawns the binary, attaches
# the editor's selection as advisor context, ships
# commands + snippets
docs/
plan_v0.2.0.md # the live roadmap + Resultado blocks (current)
plan_v0.1.0.md # historical, kept for traceability
fase_14_lossless_lexing.md # upstream reference doc for the
# trivia channel `axon-fmt` consumes
style.md # canonical Axon style — what the formatter
# changes (and what it deliberately doesn't)
integrations/ # one self-contained doc per editor
lsp-core and lsp-docs keep cargo tree --edges normal empty of
tokio / axum / sqlx / aws-* / reqwest / hyper / jsonwebtoken —
verified in CI on every commit. The Fase 12.c contract (frontend with
zero runtime deps) holds end-to-end into the LSP binary.
Build
Rust 1.95.0 stable (pinned by rust-toolchain.toml).
cargo build --workspace --release
cargo test --workspace
The advisor crate is excluded from the default build:
cargo build --workspace --release --features axon-lsp/llm
Releases
Releases page publishes:
- 3 platform archives (
linux-x86_64,macos-arm64,windows-x86_64) — one binary each. SHA256SUMS— combined integrity-verification list.
macOS Intel (x86_64-apple-darwin) builds from source
(cargo install axon-lsp) until a cross-compiled / self-hosted
target lands in a later release.
Auto-generated release notes summarise the commits since the
previous tag. The full v0.1.0 story lives in
CHANGELOG.md and the per-sub-fase Resultado blocks
in docs/plan_v0.1.0.md.
Contributing
axon-lsp is adopter-agnostic — it serves the Axon language the
same way rust-analyzer serves Rust. No customer or tenant names
appear in code, tests, fixtures, or docs. Details in
CONTRIBUTING.md.
License
MIT — see LICENSE.
Dependencies
~16–25MB
~370K SLoC