Skip to content

Language Server (LSP)

The Skein Language Server is an Elixir application (skein_lsp) that implements the Language Server Protocol. It uses the Skein compiler as its analysis engine.

The server advertises these LSP capabilities:

CapabilityDescription
textDocument/publishDiagnosticsCompile errors and warnings
textDocument/documentSymbolDocument outline (modules, functions, handlers)
textDocument/hoverType information on hover
textDocument/definitionGo-to-definition for functions and types
textDocument/completionContext-aware code completion
textDocument/codeActionQuickfixes applied generically from each error’s span/edit_kind/fix_code (with a per-code fallback for span-less diagnostics)
textDocument/semanticTokens/fullSemantic highlighting
textDocument/didOpenDocument tracking
textDocument/didChangeFull document sync
textDocument/didSaveSave-triggered recompilation
textDocument/didCloseDocument cleanup

When a document is opened, changed, or saved, the server runs the Skein compiler pipeline:

Source text
│
├── Skein.Lexer.tokenize/1
│ └── {:error, errors} → Lexer diagnostics
│
├── Skein.Parser.parse/2
│ └── {:error, errors} → Parser diagnostics
│
└── Skein.Analyzer.analyze/1
├── {:error, errors} → Analyzer diagnostics (AST still retained)
└── {:ok, ast} → No diagnostics

Each Skein.Error is converted to an LSP Diagnostic with:

  • Position mapped from 1-indexed (Skein) to 0-indexed (LSP)
  • Severity mapped from :error/:warning to LSP severity levels
  • Error code and fix hint included in the message

The server walks the AST to extract symbols for the document outline:

AST NodeSymbol Kind
ModuleModule
AgentClass
FnFunction
TypeDeclStruct
EnumDeclEnum
HandlerEvent
AgentHandlerEvent
ToolDeclInterface
SupervisorNamespace
Test / Scenario / GoldenFunction
FieldField
VariantEnumMember
State fieldsProperty

The hover provider uses a name-based lookup strategy:

  1. Extract the word at the cursor position
  2. Search declarations in the current AST for a matching name
  3. If no match, check built-in types (Int, String, Option, etc.)
  4. Format the result as a Markdown code block with type signature

Completions are context-sensitive based on the text before the cursor:

ContextStrategy
After .Look up the namespace and return its methods
After : or ->Return type completions
After @Return annotation names
GeneralKeywords + types + user symbols + effect namespaces + snippets

The LSP server is built with GenLSP, which provides:

  • OTP behaviour for LSP process management
  • Typed structs for all LSP protocol messages
  • Stdio communication transport
  • Built-in error handling (exceptions don’t crash the server)
PackageVersionPurpose
gen_lspv0.11.3LSP behaviour and protocol types
typed_structv0.3.0Struct definitions (GenLSP dep)
nimble_optionsv1.1.1Option validation (GenLSP dep)
schematicv0.2.1Schema validation (GenLSP dep)
skein_compilerin_umbrellaSkein lexer, parser, analyzer
Terminal window
# Via the standalone binary (used by the VS Code extension by default)
skein lsp
# Via mix task (inside a Skein compiler checkout)
mix skein.lsp
# Programmatically
SkeinLsp.start()

The server communicates over stdio using JSON-RPC as specified by the LSP protocol.

The language server has a test suite covering all features (see CI for current counts):

Terminal window
mix test apps/skein_lsp/test/

Tests exercise each module directly without needing a running LSP process:

  • Diagnostics tests — Valid source produces no diagnostics; invalid source produces correct error codes, locations, and severities
  • Symbols tests — Modules produce correct symbol hierarchies; agents include state fields, phases, and handlers
  • Completion tests — Keywords, types, effect namespaces, and method completions after .
  • Code action tests — Generic span/edit_kind quickfixes (replace, insert, line insert/delete) pinned against real compiler output, plus the per-code fallback for missing tokens (E0001), missing capabilities (E0012), unused capabilities (W0002), and unused bindings (W0001)
  • Hover tests — Function signatures, built-in type descriptions
  • Semantic token tests — Valid source encodes to groups-of-5 integers; invalid source returns empty