- Add String.fromChar, chars, substring, toUpper, toLower, replace, startsWith, endsWith, join to C backend - Fix record type alias unification by adding expand_type_alias and unify_with_env functions - Update docs to reflect current implementation status - Clean up outdated roadmap items and fix inconsistencies - Add comprehensive language comparison document Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
11 KiB
Lux Language Server Protocol (LSP)
Lux includes a built-in language server that provides IDE features for any editor that supports the Language Server Protocol.
Quick Start
Starting the LSP Server
lux lsp
The server communicates via stdio (stdin/stdout), following the standard LSP protocol.
Supported Features
| Feature | Status | Description |
|---|---|---|
| Diagnostics | Full | Real-time parse and type errors |
| Hover | Full | Type information and documentation |
| Completions | Full | Context-aware code completion |
| Go to Definition | Full | Jump to function/type definitions |
| Formatting | CLI only | Code formatting via lux fmt (not exposed via LSP) |
| Rename | Planned | Rename symbols |
| Find References | Planned | Find all usages |
Editor Setup
VS Code
-
Install the Lux extension from the VS Code marketplace (coming soon)
-
Or configure manually in
settings.json:
{
"lux.lspPath": "/path/to/lux",
"lux.enableLsp": true
}
- Or use a generic LSP client extension like vscode-languageclient:
{
"languageServerExample.serverPath": "lux",
"languageServerExample.serverArgs": ["lsp"]
}
Neovim (with nvim-lspconfig)
Add to your Neovim config (init.lua):
local lspconfig = require('lspconfig')
local configs = require('lspconfig.configs')
-- Register Lux as a new LSP
if not configs.lux then
configs.lux = {
default_config = {
cmd = { 'lux', 'lsp' },
filetypes = { 'lux' },
root_dir = function(fname)
return lspconfig.util.find_git_ancestor(fname) or vim.fn.getcwd()
end,
settings = {},
},
}
end
-- Enable the server
lspconfig.lux.setup{}
Also add filetype detection in ~/.config/nvim/ftdetect/lux.vim:
au BufRead,BufNewFile *.lux set filetype=lux
Neovim (with built-in LSP)
vim.api.nvim_create_autocmd('FileType', {
pattern = 'lux',
callback = function()
vim.lsp.start({
name = 'lux',
cmd = { 'lux', 'lsp' },
root_dir = vim.fs.dirname(vim.fs.find({ '.git', 'lux.toml' }, { upward = true })[1]),
})
end,
})
Emacs (with lsp-mode)
Add to your Emacs config:
(use-package lsp-mode
:hook (lux-mode . lsp)
:config
(add-to-list 'lsp-language-id-configuration '(lux-mode . "lux"))
(lsp-register-client
(make-lsp-client
:new-connection (lsp-stdio-connection '("lux" "lsp"))
:major-modes '(lux-mode)
:server-id 'lux-lsp)))
Emacs (with eglot)
(add-to-list 'eglot-server-programs '(lux-mode . ("lux" "lsp")))
Helix
Add to ~/.config/helix/languages.toml:
[[language]]
name = "lux"
scope = "source.lux"
injection-regex = "lux"
file-types = ["lux"]
roots = ["lux.toml", ".git"]
comment-token = "//"
indent = { tab-width = 4, unit = " " }
language-server = { command = "lux", args = ["lsp"] }
Sublime Text (with LSP package)
Add to Preferences > Package Settings > LSP > Settings:
{
"clients": {
"lux": {
"enabled": true,
"command": ["lux", "lsp"],
"selector": "source.lux"
}
}
}
Zed
Add to your Zed settings:
{
"lsp": {
"lux": {
"binary": {
"path": "lux",
"arguments": ["lsp"]
}
}
}
}
Feature Details
Diagnostics
The LSP server provides real-time diagnostics for:
- Parse errors: Syntax issues, missing tokens, unexpected input
- Type errors: Type mismatches, missing fields, unknown identifiers
- Effect errors: Missing effect declarations, unhandled effects
- Behavioral type errors: Violations of
is pure,is total, etc.
Diagnostics appear as you type, typically within 100ms of changes.
Example diagnostic:
error[E0308]: mismatched types
--> src/main.lux:10:5
|
10 | return "hello"
| ^^^^^^^ expected Int, found String
Hover Information
Hover over any symbol to see:
- Functions: Signature and documentation
- Types: Type definition
- Keywords: Syntax explanation
- Effects: Effect operations
Example hover for List.map:
```lux
List.map(list: List<A>, f: A -> B): List<B>
Transform each element in a list by applying a function.
### Completions
The LSP provides context-aware completions:
#### Module Member Completions
After typing a module name and `.`, you get relevant members:
```lux
List. // Shows: map, filter, fold, reverse, etc.
String. // Shows: length, split, join, trim, etc.
Console. // Shows: print, readLine, readInt
Keyword Completions
At the start of statements:
fn // Function declaration
let // Variable binding
type // Type declaration
effect // Effect declaration
match // Pattern matching
Type Completions
In type position:
Int, Float, Bool, String, Unit
List, Option, Result
Go to Definition
Jump to the definition of:
- Functions: Goes to the
fndeclaration - Types: Goes to the
typedeclaration - Effects: Goes to the
effectdeclaration - Let bindings: Goes to the
letstatement - Imports: Goes to the imported module
Works with:
Ctrl+Click/Cmd+Clickin most editorsgdin Vim/NeovimM-.in Emacs
Module Completions Reference
List Module
| Method | Signature | Description |
|---|---|---|
length |
(list: List<A>): Int |
Get list length |
head |
(list: List<A>): Option<A> |
First element |
tail |
(list: List<A>): List<A> |
All but first |
map |
(list: List<A>, f: A -> B): List<B> |
Transform elements |
filter |
(list: List<A>, p: A -> Bool): List<A> |
Keep matching |
fold |
(list: List<A>, init: B, f: (B, A) -> B): B |
Reduce |
reverse |
(list: List<A>): List<A> |
Reverse order |
concat |
(a: List<A>, b: List<A>): List<A> |
Join lists |
range |
(start: Int, end: Int): List<Int> |
Create range |
get |
(list: List<A>, index: Int): Option<A> |
Get at index |
find |
(list: List<A>, p: A -> Bool): Option<A> |
Find first match |
isEmpty |
(list: List<A>): Bool |
Check empty |
take |
(list: List<A>, n: Int): List<A> |
Take n elements |
drop |
(list: List<A>, n: Int): List<A> |
Drop n elements |
any |
(list: List<A>, p: A -> Bool): Bool |
Any matches |
all |
(list: List<A>, p: A -> Bool): Bool |
All match |
String Module
| Method | Signature | Description |
|---|---|---|
length |
(s: String): Int |
String length |
split |
(s: String, delim: String): List<String> |
Split by delimiter |
join |
(list: List<String>, delim: String): String |
Join with delimiter |
trim |
(s: String): String |
Remove whitespace |
contains |
(s: String, sub: String): Bool |
Check contains |
replace |
(s: String, from: String, to: String): String |
Replace |
chars |
(s: String): List<String> |
To char list |
lines |
(s: String): List<String> |
Split into lines |
toUpper |
(s: String): String |
Uppercase |
toLower |
(s: String): String |
Lowercase |
Console Effect
| Operation | Signature | Description |
|---|---|---|
print |
(msg: String): Unit |
Print message |
readLine |
(): String |
Read line |
readInt |
(): Int |
Read integer |
Math Module
| Function | Signature | Description |
|---|---|---|
abs |
(x: Int): Int |
Absolute value |
min |
(a: Int, b: Int): Int |
Minimum |
max |
(a: Int, b: Int): Int |
Maximum |
pow |
(base: Float, exp: Float): Float |
Exponentiation |
sqrt |
(x: Float): Float |
Square root |
floor |
(x: Float): Int |
Floor |
ceil |
(x: Float): Int |
Ceiling |
round |
(x: Float): Int |
Round |
File Effect
| Operation | Signature | Description |
|---|---|---|
read |
(path: String): String |
Read file |
write |
(path: String, content: String): Unit |
Write file |
exists |
(path: String): Bool |
Check exists |
delete |
(path: String): Bool |
Delete file |
listDir |
(path: String): List<String> |
List directory |
mkdir |
(path: String): Bool |
Create directory |
Http Effect
| Operation | Signature | Description |
|---|---|---|
get |
(url: String): Result<String, String> |
HTTP GET |
post |
(url: String, body: String): Result<String, String> |
HTTP POST |
put |
(url: String, body: String): Result<String, String> |
HTTP PUT |
delete |
(url: String): Result<String, String> |
HTTP DELETE |
Random Effect
| Operation | Signature | Description |
|---|---|---|
int |
(min: Int, max: Int): Int |
Random integer |
float |
(): Float |
Random float 0-1 |
bool |
(): Bool |
Random boolean |
Time Effect
| Operation | Signature | Description |
|---|---|---|
now |
(): Int |
Current timestamp (ms) |
sleep |
(ms: Int): Unit |
Sleep for ms |
Sql Effect
| Operation | Signature | Description |
|---|---|---|
connect |
(url: String): Connection |
Connect to database |
query |
(conn: Connection, sql: String): List<Row> |
Execute query |
execute |
(conn: Connection, sql: String): Int |
Execute statement |
transaction |
(conn: Connection, f: () -> A): A |
Run in transaction |
Troubleshooting
LSP Not Starting
- Verify Lux is installed:
lux --version - Check the server starts:
lux lsp(should wait for input) - Check editor logs for connection errors
No Completions
- Ensure file has
.luxextension - Check file is valid (no parse errors)
- Verify LSP is connected (check status bar)
Slow Diagnostics
The LSP re-parses and type-checks on every change. For large files:
- Consider splitting into modules
- Check for complex recursive types
- Report performance issues on GitHub
Debug Logging
Enable verbose logging:
LUX_LSP_LOG=debug lux lsp
Logs are written to stderr.
Protocol Details
The Lux LSP server implements LSP version 3.17 with the following capabilities:
{
"capabilities": {
"textDocumentSync": "full",
"hoverProvider": true,
"completionProvider": {
"triggerCharacters": ["."]
},
"definitionProvider": true
}
}
Supported Methods
| Method | Support |
|---|---|
initialize |
Full |
shutdown |
Full |
textDocument/didOpen |
Full |
textDocument/didChange |
Full |
textDocument/didClose |
Full |
textDocument/hover |
Full |
textDocument/completion |
Full |
textDocument/definition |
Full |
textDocument/publishDiagnostics |
Full (server-initiated) |
Contributing
The LSP implementation is in src/lsp.rs. To add new features:
- Add capability to
ServerCapabilities - Implement handler in
handle_request - Add tests in
tests/lsp_tests.rs - Update this documentation
See CONTRIBUTING.md for development setup.