Language Server
RustThe VM Registry LSP (vm-registry-lsp) is a Language Server Protocol implementation that provides editor intelligence for VMFile and VMCompose YAML files. It delivers real-time diagnostics, context-aware completions, and hover documentation directly in your editor — catching errors as you type rather than at deploy time.
Overview
The LSP is a standalone Rust binary that communicates with editors over the standard LSP JSON-RPC protocol via stdin/stdout. It understands the full schema of both VMFile and VMCompose formats, providing the same validation logic that the daemon applies at import/deploy time but with immediate, inline feedback.
Editor (Zed, Neovim, VS Code, etc.)
│
│ LSP JSON-RPC (stdin/stdout)
│
▼
vm-registry-lsp
├── Document tracking
├── YAML parsing
├── Schema validation
├── Completion engine
└── Hover documentationBuilding
cd vm-registry-lsp
cargo build --releaseThe binary is produced at target/release/vm-registry-lsp.
Editor Configuration
Zed
Add the following to your Zed settings.json:
{
"lsp": {
"vm-registry-lsp": {
"binary": {
"path": "/path/to/vm-registry-lsp"
}
}
},
"languages": {
"YAML": {
"language_servers": ["vm-registry-lsp"]
}
}
}Neovim (nvim-lspconfig)
local lspconfig = require('lspconfig')
local configs = require('lspconfig.configs')
configs.vmregistry = {
default_config = {
cmd = { '/path/to/vm-registry-lsp' },
filetypes = { 'yaml' },
root_dir = lspconfig.util.find_git_ancestor,
settings = {},
},
}
lspconfig.vmregistry.setup {}VS Code
Create a .vscode/settings.json in your workspace:
{
"yaml.customTags": [],
"vmregistry.lsp.path": "/path/to/vm-registry-lsp"
}TIP
The LSP activates for files matching VMFile and VMCompose patterns. It detects the file kind based on content — files containing a services key are treated as VMCompose, while files containing metadata and spec keys are treated as VMFile.
Capabilities
Diagnostics
The LSP validates documents in real-time as you edit, reporting errors and warnings inline. Diagnostics cover:
Structural validation:
- Missing required fields (
apiVersion,metadata.name,spec.diskImage,services, etc.) - Unknown or misspelled field names at every nesting level
- Incorrect value types (e.g., a string where an integer is expected)
Value validation:
apiVersionmatches a known version (vmregistry.io/v1alpha1orv1)diskFormatis one ofqcow2,raw,isobootloader.typeisUEFIorBIOS- Network
modeis one ofnat,isolated,routed,bridge,open,macvtap, ordefault restartPolicyis one ofalways,on-failure,never,unless-stopped- Network
driveris one ofbridge,nat,isolated,macvtap
Format validation:
- Memory values match the
<number><unit>pattern with valid units (MiB,GiB,TiB) - IP addresses use valid IPv4 dotted-decimal notation
- Subnets use valid CIDR notation (e.g.,
192.168.100.0/24) - MAC addresses use colon-separated hex format (e.g.,
52:54:00:12:34:56) - Service and network names follow the naming pattern (lowercase letters, digits, hyphens)
Cross-reference validation (VMCompose):
- Service
dependsOnentries reference services that exist in the file - Service network
nameentries reference networks defined in the top-levelnetworkssection - Detection of cyclic dependencies between services
Completions
The LSP provides context-aware completions with snippet support. When you trigger completion:
- At the top level, it suggests root fields (
apiVersion,metadata,spec,services,networks, etc.) - Inside a mapping, it suggests the valid child fields for that context
- For enum fields, it suggests only the allowed values with descriptions
- Completions include snippet templates that expand into multi-line structures with tab stops
For example, completing inside spec in a VMFile offers:
| Completion | Snippet |
|---|---|
diskImage | diskImage: disk.qcow2 |
diskFormat | diskFormat: qcow2|raw|iso |
resources | Multi-line block with cpu, memory, and bootloader |
Hover
Hovering over any field name displays documentation including:
- A description of the field's purpose
- Whether the field is required or optional
- The expected value type
- For enum fields, the list of valid values with explanations
- Format requirements (e.g., memory pattern, CIDR notation)
File Kind Detection
The LSP automatically detects whether a YAML file is a VMFile or VMCompose based on its content:
| Indicator | Detected Kind |
|---|---|
Contains services key | VMCompose |
Contains kind: VMCompose | VMCompose |
Contains metadata and spec keys | VMFile |
File extension .VMCompose | VMCompose |
File named VMFile or *.VMFile | VMFile |
Each file kind activates a different schema, ensuring that completions, diagnostics, and hover documentation are appropriate for the document type.
Schema Architecture
All validation, completion, and hover intelligence is driven by a centralized schema definition (src/schema.rs). The schema encodes:
- Field descriptors — name, documentation, required status, value type, enum values, child fields, and completion snippets for every field in both VMFile and VMCompose formats
- Value types —
Str,Int,Bool,Mapping,SeqScalar,SeqMapping,DynamicMapping,MemoryStr,IpAddr,Cidr,MacAddr,DiskSize - Path resolution — resolving dotted YAML paths to field descriptors, with support for dynamic mappings (where user-defined keys like service names act as intermediate path segments)
- Enum documentation — short descriptions for each valid value of enum fields, displayed in completion detail text
This schema mirrors the validation logic in the daemon's VMFile and VMCompose parsers, ensuring consistency between editor-time and deploy-time validation.
Internal Structure
| Module | Responsibility |
|---|---|
main.rs | LSP server initialization, capability registration, and main event loop |
dispatch.rs | Message dispatcher — routes incoming LSP notifications and requests to the appropriate handler |
document.rs | Document tracking — maintains the current state of open files, detects file kind |
schema.rs | Schema definitions — field descriptors, value types, path resolution, and enum documentation for VMFile and VMCompose |
diagnostics.rs | Diagnostic engine — validates documents against the schema and produces LSP diagnostic messages |
completion.rs | Completion engine — generates context-aware completion items with snippets based on cursor position and schema |
hover.rs | Hover engine — resolves the field at the cursor position and formats documentation for display |
yaml_util.rs | YAML utilities — cursor-to-path resolution, node traversal, and YAML-specific helpers |
Dependencies
| Dependency | Purpose |
|---|---|
lsp-server | LSP protocol transport layer (JSON-RPC over stdin/stdout) |
lsp-types | LSP type definitions (messages, capabilities, diagnostics, completions, etc.) |
crossbeam-channel | Channel-based message passing for the LSP event loop |
yaml-rust2 | YAML parser for analyzing document structure |
serde / serde_json | JSON serialization for LSP message payloads |
log / env_logger | Logging for development and debugging |