Files
lux/docs/LSP.md
Brandon Lucas 33b4f57faf fix: C backend String functions, record type aliases, docs cleanup
- 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>
2026-02-16 01:06:20 -05:00

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

  1. Install the Lux extension from the VS Code marketplace (coming soon)

  2. Or configure manually in settings.json:

{
  "lux.lspPath": "/path/to/lux",
  "lux.enableLsp": true
}
  1. 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 fn declaration
  • Types: Goes to the type declaration
  • Effects: Goes to the effect declaration
  • Let bindings: Goes to the let statement
  • Imports: Goes to the imported module

Works with:

  • Ctrl+Click / Cmd+Click in most editors
  • gd in Vim/Neovim
  • M-. 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

  1. Verify Lux is installed: lux --version
  2. Check the server starts: lux lsp (should wait for input)
  3. Check editor logs for connection errors

No Completions

  1. Ensure file has .lux extension
  2. Check file is valid (no parse errors)
  3. Verify LSP is connected (check status bar)

Slow Diagnostics

The LSP re-parses and type-checks on every change. For large files:

  1. Consider splitting into modules
  2. Check for complex recursive types
  3. 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:

  1. Add capability to ServerCapabilities
  2. Implement handler in handle_request
  3. Add tests in tests/lsp_tests.rs
  4. Update this documentation

See CONTRIBUTING.md for development setup.