elixir-ls, typescript-language-server, or rust-analyzer.
Overview
Loom’s LSP integration consists of:- Client: GenServer-based LSP client communicating over stdio
- Protocol: JSON-RPC 2.0 message encoding/decoding with Content-Length framing
- Supervisor: Manages multiple LSP client lifecycles
Configuration
Configure LSP servers in.loom.toml:
LSP servers are started automatically on boot when
lsp.enabled = true. Use Loom.LSP.Supervisor.start_from_config/0 to load them.LSP Supervisor
TheLoom.LSP.Supervisor module manages LSP client processes.
start_client/1
Start a new LSP client for a language server.Unique name for this LSP client instance.
Executable name or path to the language server binary.
Command-line arguments to pass to the language server.
Successfully started LSP client process.
Failed to start client (e.g., executable not found).
stop_client/1
Stop a running LSP client by name.list_clients/0
List all running LSP client names.List of active LSP client names.
start_from_config/0
Start all LSP clients defined in.loom.toml.
This function is called automatically during application startup if
lsp.enabled = true.LSP Client
TheLoom.LSP.Client module handles individual language server connections.
initialize/2
Initialize the LSP server connection with a root path.Client name (as registered when started).
Absolute path to the project root directory.
Initialization result containing server capabilities.
Initialization failed (e.g., server not running, timeout).
did_open/3
Notify the server that a file was opened.Client name.
Absolute path to the file.
Language identifier (e.g.,
"elixir", "typescript", "rust").Language IDs should match the LSP specification. Common IDs:
elixir, javascript, typescript, python, go, rust, ruby.did_close/2
Notify the server that a file was closed.get_diagnostics/2
Get current diagnostics for a specific file.List of diagnostic maps containing:
line(integer()): Line number (1-indexed)character(integer()): Character position (1-indexed)severity(:error|:warning|:information|:hint): Issue severitymessage(String.t()): Diagnostic messagesource(String.t()): Diagnostic source (e.g.,"elixir-ls")code(String.t() | integer() | nil): Error code
all_diagnostics/1
Get all diagnostics across all files.Map of file URIs to diagnostic lists:
%{uri => [diagnostic]}status/1
Check the client connection status.One of:
:idle: Client created but not initialized:starting: Initialization in progress:ready: Connected and ready for requests:stopped: Server has exited:not_running: Client process not found
shutdown/1
Gracefully shut down the LSP server.This sends the LSP
shutdown request followed by exit notification. The server process terminates cleanly.LSP Protocol
TheLoom.LSP.Protocol module handles JSON-RPC 2.0 message encoding/decoding.
encode_request/3
Encode a JSON-RPC request with Content-Length header.Unique request identifier.
LSP method name (e.g.,
"textDocument/hover").Request parameters.
encode_notification/2
Encode a JSON-RPC notification (no response expected).decode_message/1
Decode a JSON-RPC message body.extract_message/1
Extract one complete message from a binary buffer.Helper Functions
path_to_uri/1
Convert a file path to afile:// URI.
uri_to_path/1
Convert afile:// URI back to a file path.
severity_name/1
Map LSP diagnostic severity integer to atom.severity_value/1
Map severity atom to LSP integer.Complete Example
Supported Language Servers
Loom has been tested with:| Language | Server | Command | Notes |
|---|---|---|---|
| Elixir | elixir-ls | elixir-ls | Install via brew install elixir-ls |
| TypeScript | typescript-language-server | typescript-language-server --stdio | Install via npm install -g typescript-language-server |
| Rust | rust-analyzer | rust-analyzer | Install via rustup component add rust-analyzer |
| Python | pyright | pyright-langserver --stdio | Install via npm install -g pyright |
| Go | gopls | gopls | Install via go install golang.org/x/tools/gopls@latest |
| Ruby | solargraph | solargraph stdio | Install via gem install solargraph |
Troubleshooting
Server Not Starting
-
Verify the executable is in your PATH:
-
Test the server manually:
- Check Loom logs for startup errors
No Diagnostics Received
- Ensure you called
initialize/2first - Call
did_open/3to notify the server about files - Wait a few seconds for server analysis
- Check server status:
Client.status("server-name")
Connection Hangs
- Some servers require specific arguments (e.g.,
--stdio) - Check stderr output for server errors
- Verify the server supports stdio communication