#axon #language-server #lsp #ide #compiler #axon-server

app axon-lsp

Language Server Protocol server for the Axon programming language. Deterministic diagnostics, hover, completion, and go-to-definition across VSCode, Claude Code, Cursor, Antigravity, Zed, Neovim and any LSP-compatible editor.

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

MIT license

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 in docs/plan_v0.3.0.md; historical plans sit in docs/plan_v0.2.0.md and docs/plan_v0.1.0.md. Install via cargo 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/createBegin → 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:

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